package util import ( "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<= 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 => 1676995200 func 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 => 1676995200 func 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 => 1676998245 func 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 } /* GetNeckRingActiveTimer 1. 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 获取XFrameId func XFrameId(frameid int32) int32 { return int32(math.Floor(float64(frameid / 10))) } // FrameIds 获取FrameIds func 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 { cows := "" if len(cowIds) <= 0 { return cows } for _, v := range cowIds { cows += fmt.Sprintf("%d,", v) } return strings.TrimRight(cows, cutset) } // 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) }