| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605 | package utilimport (	"bytes"	"fmt"	"math"	"math/rand"	"regexp"	"strconv"	"strings"	"time"	"gitee.com/xuyiping_admin/pkg/xerr")const (	LayoutTime    = "2006-01-02 15:04:05"	Layout        = "2006-01-02"	LayoutMonth   = "2006-01"	LetterBytes   = "abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ23456789"	LetterIdxBits = 6                    // 6 bits to represent a letter index	LetterIdxMask = 1<<LetterIdxBits - 1 // All 1-bits, as many as letterIdxBits	LetterIdxMax  = 63 / LetterIdxBits   // # of letter indices fitting in 63 bits)var (	FrameIdMap = map[int32]int32{		1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 8: 8, // 00-02		11: 11, 12: 12, 13: 13, 14: 14, 15: 15, 16: 16, 18: 18, // 02-04		21: 21, 22: 22, 23: 23, 24: 24, 25: 25, 26: 26, 28: 28, // 04-06		31: 31, 32: 32, 33: 33, 34: 34, 35: 35, 36: 36, 38: 38, // 06-08		41: 41, 42: 42, 43: 43, 44: 44, 45: 45, 46: 46, 48: 48, // 08-10		51: 51, 52: 52, 53: 53, 54: 54, 55: 55, 56: 56, 58: 58, // 10-12		61: 61, 62: 62, 63: 63, 64: 64, 65: 65, 66: 66, 68: 68, // 12-14		71: 71, 72: 72, 73: 73, 74: 74, 75: 75, 76: 76, 78: 78, // 14-16		81: 81, 82: 82, 83: 83, 84: 84, 85: 85, 86: 86, 88: 88, // 16-18		91: 91, 92: 92, 93: 93, 94: 94, 95: 95, 96: 96, 98: 98, // 18-20		101: 101, 102: 102, 103: 103, 104: 104, 105: 105, 106: 106, 108: 108, // 20-22		111: 111, 112: 112, 113: 113, 114: 114, 115: 115, 116: 116, 118: 118, // 22-24	}	SpecialHours      = map[int]int{8: 2, 18: 4, 28: 6, 38: 8, 48: 10, 58: 12, 68: 14, 78: 16, 88: 18, 98: 20, 108: 22, 118: 0}	FrameIdMapReverse = map[int32]int32{		0:  8,		1:  8,		2:  18,		3:  18,		4:  28,		5:  28,		6:  38,		7:  38,		8:  48,		9:  48,		10: 58,		11: 58,		12: 68,		13: 68,		14: 78,		15: 78,		16: 88,		17: 88,		18: 98,		19: 98,		20: 108,		21: 108,		22: 118, // 04-06		23: 118,	}	ExpectedFrameIDs = []int32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11})// GenerateRandomNumberString 生成指定长度的数字串func GenerateRandomNumberString(n int) string {	result := make([]byte, n)	// A rand.Int63() generates 63 random bits, enough for letterIdxMax characters!	rand.Seed(time.Now().UnixNano())	for i, cache, remain := n-1, rand.Int63(), LetterIdxMax; i >= 0; {		if remain == 0 {			cache, remain = rand.Int63(), LetterIdxMax		}		if idx := int(cache & LetterIdxMask); idx < len(LetterBytes) {			result[i] = LetterBytes[idx]			i--		}		cache >>= LetterIdxBits		remain--	}	return string(result)}// TimeParseLocalUnix 获取当天零点的时间戳// eg 2023-02-22 => 1676995200func TimeParseLocalUnix(DayTime string) int64 {	value := DayTime	if len(DayTime) <= 11 {		value = fmt.Sprintf("%s 00:00:00", DayTime)	}	loc, _ := time.LoadLocation("Local")	theTime, _ := time.ParseInLocation(LayoutTime, value, loc)	return theTime.Unix()}// TimeParseLocalEndUnix 获取当天24点的时间戳// eg 2023-02-22  => 1676995200func TimeParseLocalEndUnix(DayTime string) int64 {	value := DayTime	if len(DayTime) <= 11 {		value = fmt.Sprintf("%s 23:59:59", DayTime)	}	loc, _ := time.LoadLocation("Local")	theTime, _ := time.ParseInLocation(LayoutTime, value, loc)	return theTime.Unix()}// ConvertParseLocalUnix 字符串转换当天时间戳// eg 15:04:05 => 1676998245func ConvertParseLocalUnix(timeParse string) (int64, error) {	loc, err := time.LoadLocation("Local")	if err != nil {		return 0, err	}	value := fmt.Sprintf("%s %s", time.Now().Format(Layout), timeParse)	theTime, err := time.ParseInLocation(LayoutTime, value, loc)	if err != nil {		return 0, err	}	return theTime.Unix(), nil}// GetMonthRemainDay 获取当前月还剩几天func GetMonthRemainDay() int {	now := time.Now()	lastDayOfMonth := time.Date(now.Year(), now.Month()+1, 0, 23, 59, 59, 999999999, now.Location())	return int(lastDayOfMonth.Sub(now).Hours()/24) + 1}// Ceil 向上取整函数func Ceil(x float64) float64 {	// 使用 math.Floor 计算小于或等于 x 的最大整数	// 然后检查 x 是否为整数,如果不是,则结果加 1	// 注意:math.Floor 返回的是 float64 类型,所以我们需要进行比较	// 来确定是否需要加 1	intPart := math.Floor(x)	if x-intPart > 0 {		return intPart + 1	}	return intPart}// GetLastDayOfMonth// 接受一个字符串形式的月份(如 "2024-12"),// 并返回该月份的最后一天的日期(2024-12-31)func GetLastDayOfMonth(month string) (string, error) {	t, err := time.Parse(LayoutMonth, month)	if err != nil {		return "", err // 如果解析失败,返回错误	}	// 获取下个月的第一天	nextMonth := t.AddDate(0, 1, 0)	// 返回上个月的最后一天	lastDay := nextMonth.AddDate(0, 0, -1)	return lastDay.Format(Layout), nil}// GetMonthsInRange// 接受两个字符串形式的月份(如 "2024-01" 到 "2024-12"),// 并返回一个包含这两个月份之间所有月份的字符串切片。func GetMonthsInRange(startMonth, endMonth string) ([]string, error) {	// 解析起始月份	startTime, err := time.Parse(LayoutMonth, startMonth)	if err != nil {		return nil, err	}	// 解析结束月份	endTime, err := time.Parse(LayoutMonth, endMonth)	if err != nil {		return nil, err	}	// 初始化结果切片	var months []string	// 循环添加每个月份直到达到或超过结束月份	for curr := startTime; curr.Before(endTime) || curr.Equal(endTime); curr = curr.AddDate(0, 1, 0) {		months = append(months, curr.Format(LayoutMonth))	}	return months, nil}// RoundToTwoDecimals 四舍五入并保留两位小数func RoundToTwoDecimals(num float64) float64 {	// 使用 math.Round 函数进行四舍五入	// 先乘以 100,然后四舍五入,最后除以 100	return math.Round(num*100) / 100}// Get21DayPeriods 获取范围时间内21天周期的切片// 从结束时间开始往前推算,超过开始时间,继续向前推算,直至凑整21天func Get21DayPeriods(startDay, endDay string) ([][]string, error) {	startDate, err := time.Parse(Layout, startDay)	if err != nil {		return nil, err	}	endDate, err := time.Parse(Layout, endDay)	if err != nil {		return nil, err	}	if startDate.After(endDate) {		return nil, xerr.Custom("start date is after end date")	}	var periods [][]string	for date := endDate; date.After(startDate); {		// Calculate the end of the current period		periodEnd := date.AddDate(0, 0, -20)		// Append the current period to the result slice		periods = append(periods, []string{periodEnd.Format(Layout), date.Format(Layout)})		// Move on to the next day		date = periodEnd.AddDate(0, 0, -1)	}	reverseRows(periods)	return periods, nil}func reverseRows(matrix [][]string) {	// 获取矩阵的行数	rows := len(matrix)	if rows == 0 {		return // 如果矩阵为空,则直接返回	}	// 初始化两个指针,一个指向开始,一个指向末尾	for i, j := 0, rows-1; i < j; i, j = i+1, j-1 {		// 交换当前行和对应行		matrix[i], matrix[j] = matrix[j], matrix[i]	}}// GetRangeDayMiddleDay 获取指定日期范围中间的某一天func GetRangeDayMiddleDay(dateRange []string, middleDay int32) (string, error) {	if len(dateRange) < 2 {		return "", xerr.Custom("date range is not enough")	}	if middleDay < 0 {		return "", xerr.Custom("middle day is not enough")	}	startDate, _ := time.Parse(Layout, dateRange[0])	return startDate.AddDate(0, 0, int(middleDay)-1).Format(Layout), nil}// GetRangeDayByDays 获取指定范围日期内按照指定天数来切割// (2024-10-01 ~ 2024-10-31,5)=> [[2024-10-01,2024-10-05], [2024-10-06,2024-10-10], [2024-10-11,2024-10-15], [2024-10-16,2024-10-20], [2024-10-21,2024-10-25], [2024-10-26,2024-10-30],[2024-10-31,2024-10-31]]func GetRangeDayByDays(startDay, endDay string, days int32) ([][]string, error) {	var res [][]string	if days <= 0 {		return res, nil	}	startDate, err := time.Parse(Layout, startDay)	if err != nil {		return nil, err	}	endDate, err := time.Parse(Layout, endDay)	if err != nil {		return nil, err	}	if startDate.After(endDate) {		return nil, xerr.Custom("start date is after end date")	}	if startDate == endDate {		return [][]string{{startDay, endDay}}, nil	}	for date := startDate; date.Before(endDate) || date.Equal(endDate); date = date.AddDate(0, 0, int(days)) {		if date.AddDate(0, 0, int(days)-1).After(endDate) {			res = append(res, []string{date.Format(Layout), endDate.Format(Layout)})			break		}		res = append(res, []string{date.Format(Layout), date.AddDate(0, 0, int(days)-1).Format(Layout)})	}	return res, nil}// 计算样本均值func mean(data []float64) float64 {	sum := 0.0	for _, v := range data {		sum += v	}	return sum / float64(len(data))}// 计算样本标准差func stddev(data []float64, mean float64) float64 {	sum := 0.0	for _, v := range data {		sum += (v - mean) * (v - mean)	}	variance := sum / float64(len(data)-1)	return math.Sqrt(variance)}// ConfidenceInterval 计算95%置信区间func ConfidenceInterval(data []float64) (float64, float64) {	n := float64(len(data))	meanVal := mean(data)	stdDev := stddev(data, meanVal)	z := 1.96 // 95%置信水平对应的Z值	marginOfError := z * (stdDev / math.Sqrt(n))	lowerBound := meanVal - marginOfError	upperBound := meanVal + marginOfError	return lowerBound, upperBound}// ConfidenceInterval2 计算95%置信区间func ConfidenceInterval2(p float64, total float64) (min, max float64) {	if p <= 0 || total <= 0 {		return 0, 0	}	z := 1.96 // 95%置信水平对应的Z值	marginOf := z * math.Sqrt(p*(1-p)/total) * 100	return math.Max(0, p*100-math.Ceil(marginOf)), math.Max(1, p*100+math.Ceil(marginOf))}// RemoveDuplicates 去除切片中的重复元素func RemoveDuplicates(slice []string) []string {	if len(slice) <= 0 {		return slice	}	// 创建一个map来跟踪已经遇到的元素	seen := make(map[string]struct{})	// 创建一个新的切片来存储不重复的元素	var result []string	// 遍历原始切片	for _, v := range slice {		// 如果元素尚未在map中,则将其添加到结果切片和map中		if _, exists := seen[v]; !exists {			seen[v] = struct{}{}			result = append(result, v)		}	}	// 返回不重复的切片	return result}// DaysBetween 计算两个日期(时间戳)之间的天数差func DaysBetween(startDayUnix int64, endDayUnix int64) int64 {	time1 := time.Unix(startDayUnix, 0)	time2 := time.Unix(endDayUnix, 0)	// Truncate to the start of the day (00:00:00)	startOfDay1 := time.Date(time1.Year(), time1.Month(), time1.Day(), 0, 0, 0, 0, time1.Location())	startOfDay2 := time.Date(time2.Year(), time2.Month(), time2.Day(), 0, 0, 0, 0, time2.Location())	// Calculate the difference in days	daysDiff := int64(startOfDay2.Sub(startOfDay1).Hours() / 24)	return daysDiff}// GetDaysBetween 获取两个日期之间的所有天数// 2024-10-01 ~ 2024-10-07 => [2024-10-01,2024-10-02,2024-10-03,2024-10-04,2024-10-05,2024-10-06,2024-10-07]func GetDaysBetween(startDate, endDate string) ([]string, error) {	// 解析日期	start, err := time.Parse(Layout, startDate)	if err != nil {		return nil, fmt.Errorf("解析开始日期失败: %v", err)	}	end, err := time.Parse(Layout, endDate)	if err != nil {		return nil, fmt.Errorf("解析结束日期失败: %v", err)	}	// 确保开始日期早于或等于结束日期	if start.After(end) {		return nil, fmt.Errorf("开始日期不能晚于结束日期")	}	// 存储日期的数组	var days []string	// 遍历日期	for current := start; !current.After(end); current = current.AddDate(0, 0, 1) {		days = append(days, current.Format(Layout))	}	return days, nil}// GetMonthsBetween 返回两个日期之间的所有月份// 参数格式为 "YYYY-MM",例如 "2025-01"func GetMonthsBetween(start, end string) ([]string, error) {	// 检查参数合法性	if err := validateDate(start); err != nil {		return nil, fmt.Errorf("invalid start date: %v", err)	}	if err := validateDate(end); err != nil {		return nil, fmt.Errorf("invalid end date: %v", err)	}	// 解析起始日期和结束日期	startTime, err := time.Parse(LayoutMonth, start)	if err != nil {		return nil, fmt.Errorf("failed to parse start date: %v", err)	}	endTime, err := time.Parse(LayoutMonth, end)	if err != nil {		return nil, fmt.Errorf("failed to parse end date: %v", err)	}	// 检查起始日期是否早于结束日期	if startTime.After(endTime) {		return nil, xerr.Custom("start date must be before or equal to end date")	}	// 生成月份列表	var months []string	for current := startTime; !current.After(endTime); current = current.AddDate(0, 1, 0) {		months = append(months, current.Format(LayoutMonth))	}	return months, nil}// validateDate 检查日期字符串的合法性func validateDate(date string) error {	if len(date) != 7 || date[4] != '-' {		return xerr.Custom("date format must be YYYY-MM")	}	_, err := time.Parse(LayoutMonth, date)	if err != nil {		return xerr.Customf("invalid date: %v", err)	}	return nil}// MsgFormat 格式化消息字符串 字符串里面有多个冒号,仅删除冒号前后的空格(如果存在)func MsgFormat(input string) string {	// 定义正则表达式,用于匹配冒号两边的空格	re := regexp.MustCompile(`\s*:\s*`)	// 使用正则表达式替换所有匹配的部分	return re.ReplaceAllString(input, ":")}// IsValidFrameId 检查 FrameId 是否有效func IsValidFrameId(frameId int32) bool {	_, ok := FrameIdMap[frameId]	return ok}/*GetNeckRingActiveTimer1. frameId值如果是:1到6代表每天的0点到2点,11-16 代表每天的2点到4点, 21-26 代表每天的4点到6点,31-36代表每天的6点到8点,41-46 代表每天的8点到10点,51-56代表每天的10点到12点,61-66代表每天的12-14点,71-76代表每天的14-16点,81-86代表每天的16-18点,91-96代表每天的18-20点,101-106代表每天的20-22点,111-116代表每天的22-24点。其中每个数字代表20分钟。2. 如果frameId大于当前时间点,并往前推20个小时,如果在这个20小时范围内, 就代表frameId是昨天的。3. 如果farmId的值出现8,18,28,3848,58,68,78,88,98,108,118数字分别代表2个小时,从0-2点开始,以此类推。帮我根据frameId值,获取对应的时间(YYYY-MM-DD)和小时*/func GetNeckRingActiveTimer(frameId int32) (dateTime string, hours int) {	if frameId < 0 || frameId > 118 {		return "", 0	}	if _, ok := FrameIdMap[frameId]; !ok {		return "", 0	}	nowTime := time.Now()	currHour := nowTime.Hour()	// 处理2小时的特殊 farmId	hours, ok := SpecialHours[int(frameId)]	day := 0	if ok {		if hours == 0 {			hours = 24		}		if hours > currHour {			day = -1		}		hours = 0		dateTime = nowTime.AddDate(0, 0, day).Format(Layout)		return	}	hours = int(math.Floor(float64(frameId)/10) * 2)	units := int(frameId % 10)	hours += units / 3	if hours > currHour {		for i := 0; i <= 20; i++ {			twentyHoursAgo := nowTime.Add(-time.Duration(i) * time.Hour).Hour()			if twentyHoursAgo == 0 {				twentyHoursAgo = 24			}			if hours == twentyHoursAgo {				day = -1				break			}		}	}	dateTime = nowTime.AddDate(0, 0, day).Format(Layout)	return}// XFrameId 获取XFrameIdfunc XFrameId(frameid int32) int32 {	return int32(math.Floor(float64(frameid / 10)))}// FrameIds 获取FrameIdsfunc FrameIds(xFrameId int32) []int32 {	frameIds := make([]int32, 0)	for i := 1; i <= 8; i++ {		if i == 7 {			continue		}		frameIds = append(frameIds, xFrameId*10+int32(i))	}	return frameIds}func CurrentMaxFrameId() int32 {	currentHour := time.Now().Hour()	return int32(math.Floor(float64(currentHour/2))) * 10}func ArrayInt32ToStrings(cowIds []int32, cutset string) string {	var cows bytes.Buffer	if len(cowIds) <= 0 {		return cows.String()	}	for i, v := range cowIds {		if i > 0 {			cows.WriteString(cutset)		}		cows.WriteString(strconv.Itoa(int(v))) // 将整数转换为字符串	}	return cows.String()}// ConvertCowIdsToInt64Slice 将逗号拼接的字符串转换为 int64 切片func ConvertCowIdsToInt64Slice(input string) ([]int64, error) {	// 如果输入为空,直接返回空切片	if input == "" {		return []int64{}, nil	}	// 按逗号分割字符串	parts := strings.Split(input, ",")	// 初始化结果切片	result := make([]int64, 0, len(parts))	// 遍历每个部分,转换为 int64	for _, part := range parts {		// 去除空格		part = strings.TrimSpace(part)		if part == "" {			continue // 忽略空字符串		}		// 转换为 int64		num, err := strconv.ParseInt(part, 10, 64)		if err != nil {			return nil, fmt.Errorf("invalid number: %s", part)		}		// 添加到结果切片		result = append(result, num)	}	return result, nil}// GetMonthStartAndEndTimestamp 获取当前月份的开始时间戳和结束时间戳func GetMonthStartAndEndTimestamp() (startTimestamp, endTimestamp int64) {	// 获取当前时间	now := time.Now()	// 获取当前月份的第一天	startOfMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())	// 获取下一个月份的第一天,然后减去一秒,得到当前月份的最后一天	endOfMonth := startOfMonth.AddDate(0, 1, 0).Add(-time.Second)	// 转换为时间戳	startTimestamp = startOfMonth.Unix()	endTimestamp = endOfMonth.Unix()	return startTimestamp, endTimestamp}// SubDays 计算两个日期(时间戳)之间的天数差func SubDays(startDay, endDay int64) int32 {	s1 := time.Unix(startDay, 0)	s2 := time.Unix(endDay, 0)	return int32(s2.Sub(s1).Hours() / 24)}
 |