util.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. package util
  2. import (
  3. "fmt"
  4. "math"
  5. "math/rand"
  6. "regexp"
  7. "time"
  8. "gitee.com/xuyiping_admin/pkg/xerr"
  9. )
  10. const (
  11. LayoutTime = "2006-01-02 15:04:05"
  12. Layout = "2006-01-02"
  13. LayoutMonth = "2006-01"
  14. )
  15. // GenerateRandomNumberString 生成指定长度的数字串
  16. func GenerateRandomNumberString(length int) string {
  17. const charset = "0123456789"
  18. seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
  19. result := make([]byte, length)
  20. for i := range result {
  21. result[i] = charset[seededRand.Intn(len(charset))]
  22. }
  23. return string(result)
  24. }
  25. // TimeParseLocalUnix 获取当天零点的时间戳
  26. // eg 2023-02-22 => 1676995200
  27. func TimeParseLocalUnix(DayTime string) int64 {
  28. value := DayTime
  29. if len(DayTime) <= 11 {
  30. value = fmt.Sprintf("%s 00:00:00", DayTime)
  31. }
  32. loc, _ := time.LoadLocation("Local")
  33. theTime, _ := time.ParseInLocation(LayoutTime, value, loc)
  34. return theTime.Unix()
  35. }
  36. // TimeParseLocalEndUnix 获取当天24点的时间戳
  37. // eg 2023-02-22 => 1676995200
  38. func TimeParseLocalEndUnix(DayTime string) int64 {
  39. value := DayTime
  40. if len(DayTime) <= 11 {
  41. value = fmt.Sprintf("%s 23:59:59", DayTime)
  42. }
  43. loc, _ := time.LoadLocation("Local")
  44. theTime, _ := time.ParseInLocation(LayoutTime, value, loc)
  45. return theTime.Unix()
  46. }
  47. // ConvertParseLocalUnix 字符串转换当天时间戳
  48. // eg 15:04:05 => 1676998245
  49. func ConvertParseLocalUnix(timeParse string) (int64, error) {
  50. loc, err := time.LoadLocation("Local")
  51. if err != nil {
  52. return 0, err
  53. }
  54. value := fmt.Sprintf("%s %s", time.Now().Format(Layout), timeParse)
  55. theTime, err := time.ParseInLocation(LayoutTime, value, loc)
  56. if err != nil {
  57. return 0, err
  58. }
  59. return theTime.Unix(), nil
  60. }
  61. // GetMonthRemainDay 获取当前月还剩几天
  62. func GetMonthRemainDay() int {
  63. now := time.Now()
  64. lastDayOfMonth := time.Date(now.Year(), now.Month()+1, 0, 23, 59, 59, 999999999, now.Location())
  65. return int(lastDayOfMonth.Sub(now).Hours()/24) + 1
  66. }
  67. // Ceil 向上取整函数
  68. func Ceil(x float64) float64 {
  69. // 使用 math.Floor 计算小于或等于 x 的最大整数
  70. // 然后检查 x 是否为整数,如果不是,则结果加 1
  71. // 注意:math.Floor 返回的是 float64 类型,所以我们需要进行比较
  72. // 来确定是否需要加 1
  73. intPart := math.Floor(x)
  74. if x-intPart > 0 {
  75. return intPart + 1
  76. }
  77. return intPart
  78. }
  79. // GetLastDayOfMonth
  80. // 接受一个字符串形式的月份(如 "2024-12"),
  81. // 并返回该月份的最后一天的日期(2024-12-31)
  82. func GetLastDayOfMonth(month string) (string, error) {
  83. t, err := time.Parse(LayoutMonth, month)
  84. if err != nil {
  85. return "", err // 如果解析失败,返回错误
  86. }
  87. // 获取下个月的第一天
  88. nextMonth := t.AddDate(0, 1, 0)
  89. // 返回上个月的最后一天
  90. lastDay := nextMonth.AddDate(0, 0, -1)
  91. return lastDay.Format(Layout), nil
  92. }
  93. // GetMonthsInRange
  94. // 接受两个字符串形式的月份(如 "2024-01" 到 "2024-12"),
  95. // 并返回一个包含这两个月份之间所有月份的字符串切片。
  96. func GetMonthsInRange(startMonth, endMonth string) ([]string, error) {
  97. // 解析起始月份
  98. startTime, err := time.Parse(LayoutMonth, startMonth)
  99. if err != nil {
  100. return nil, err
  101. }
  102. // 解析结束月份
  103. endTime, err := time.Parse(LayoutMonth, endMonth)
  104. if err != nil {
  105. return nil, err
  106. }
  107. // 初始化结果切片
  108. var months []string
  109. // 循环添加每个月份直到达到或超过结束月份
  110. for curr := startTime; curr.Before(endTime) || curr.Equal(endTime); curr = curr.AddDate(0, 1, 0) {
  111. months = append(months, curr.Format(LayoutMonth))
  112. }
  113. return months, nil
  114. }
  115. // RoundToTwoDecimals 四舍五入并保留两位小数
  116. func RoundToTwoDecimals(num float64) float64 {
  117. // 使用 math.Round 函数进行四舍五入
  118. // 先乘以 100,然后四舍五入,最后除以 100
  119. return math.Round(num*100) / 100
  120. }
  121. // Get21DayPeriods 获取范围时间内21天周期的切片
  122. // 从结束时间开始往前推算,超过开始时间,继续向前推算,直至凑整21天
  123. func Get21DayPeriods(startDay, endDay string) ([][]string, error) {
  124. startDate, err := time.Parse(Layout, startDay)
  125. if err != nil {
  126. return nil, err
  127. }
  128. endDate, err := time.Parse(Layout, endDay)
  129. if err != nil {
  130. return nil, err
  131. }
  132. if startDate.After(endDate) {
  133. return nil, xerr.Custom("start date is after end date")
  134. }
  135. var periods [][]string
  136. for date := endDate; date.After(startDate); {
  137. // Calculate the end of the current period
  138. periodEnd := date.AddDate(0, 0, -20)
  139. // Append the current period to the result slice
  140. periods = append(periods, []string{periodEnd.Format(Layout), date.Format(Layout)})
  141. // Move on to the next day
  142. date = periodEnd.AddDate(0, 0, -1)
  143. }
  144. reverseRows(periods)
  145. return periods, nil
  146. }
  147. func reverseRows(matrix [][]string) {
  148. // 获取矩阵的行数
  149. rows := len(matrix)
  150. if rows == 0 {
  151. return // 如果矩阵为空,则直接返回
  152. }
  153. // 初始化两个指针,一个指向开始,一个指向末尾
  154. for i, j := 0, rows-1; i < j; i, j = i+1, j-1 {
  155. // 交换当前行和对应行
  156. matrix[i], matrix[j] = matrix[j], matrix[i]
  157. }
  158. }
  159. // GetRangeDayMiddleDay 获取指定日期范围中间的某一天
  160. func GetRangeDayMiddleDay(dateRange []string, middleDay int32) (string, error) {
  161. if len(dateRange) < 2 {
  162. return "", xerr.Custom("date range is not enough")
  163. }
  164. if middleDay < 0 {
  165. return "", xerr.Custom("middle day is not enough")
  166. }
  167. startDate, _ := time.Parse(Layout, dateRange[0])
  168. return startDate.AddDate(0, 0, int(middleDay)-1).Format(Layout), nil
  169. }
  170. // GetRangeDayByDays 获取指定范围日期内按照指定天数来切割
  171. // (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]]
  172. func GetRangeDayByDays(startDay, endDay string, days int32) ([][]string, error) {
  173. var res [][]string
  174. if days <= 0 {
  175. return res, nil
  176. }
  177. startDate, err := time.Parse(Layout, startDay)
  178. if err != nil {
  179. return nil, err
  180. }
  181. endDate, err := time.Parse(Layout, endDay)
  182. if err != nil {
  183. return nil, err
  184. }
  185. if startDate.After(endDate) {
  186. return nil, xerr.Custom("start date is after end date")
  187. }
  188. if startDate == endDate {
  189. return [][]string{{startDay, endDay}}, nil
  190. }
  191. for date := startDate; date.Before(endDate) || date.Equal(endDate); date = date.AddDate(0, 0, int(days)) {
  192. if date.AddDate(0, 0, int(days)-1).After(endDate) {
  193. res = append(res, []string{date.Format(Layout), endDate.Format(Layout)})
  194. break
  195. }
  196. res = append(res, []string{date.Format(Layout), date.AddDate(0, 0, int(days)-1).Format(Layout)})
  197. }
  198. return res, nil
  199. }
  200. // 计算样本均值
  201. func mean(data []float64) float64 {
  202. sum := 0.0
  203. for _, v := range data {
  204. sum += v
  205. }
  206. return sum / float64(len(data))
  207. }
  208. // 计算样本标准差
  209. func stddev(data []float64, mean float64) float64 {
  210. sum := 0.0
  211. for _, v := range data {
  212. sum += (v - mean) * (v - mean)
  213. }
  214. variance := sum / float64(len(data)-1)
  215. return math.Sqrt(variance)
  216. }
  217. // ConfidenceInterval 计算95%置信区间
  218. func ConfidenceInterval(data []float64) (float64, float64) {
  219. n := float64(len(data))
  220. meanVal := mean(data)
  221. stdDev := stddev(data, meanVal)
  222. z := 1.96 // 95%置信水平对应的Z值
  223. marginOfError := z * (stdDev / math.Sqrt(n))
  224. lowerBound := meanVal - marginOfError
  225. upperBound := meanVal + marginOfError
  226. return lowerBound, upperBound
  227. }
  228. // ConfidenceInterval2 计算95%置信区间
  229. func ConfidenceInterval2(p float64, total float64) (min, max float64) {
  230. if p <= 0 || total <= 0 {
  231. return 0, 0
  232. }
  233. z := 1.96 // 95%置信水平对应的Z值
  234. marginOf := z * math.Sqrt(p*(1-p)/total) * 100
  235. return math.Max(0, p*100-math.Ceil(marginOf)), math.Max(1, p*100+math.Ceil(marginOf))
  236. }
  237. // RemoveDuplicates 去除切片中的重复元素
  238. func RemoveDuplicates(slice []string) []string {
  239. if len(slice) <= 0 {
  240. return slice
  241. }
  242. // 创建一个map来跟踪已经遇到的元素
  243. seen := make(map[string]struct{})
  244. // 创建一个新的切片来存储不重复的元素
  245. var result []string
  246. // 遍历原始切片
  247. for _, v := range slice {
  248. // 如果元素尚未在map中,则将其添加到结果切片和map中
  249. if _, exists := seen[v]; !exists {
  250. seen[v] = struct{}{}
  251. result = append(result, v)
  252. }
  253. }
  254. // 返回不重复的切片
  255. return result
  256. }
  257. func DaysBetween(startDayUnix int64, endDayUnix int64) int64 {
  258. time1 := time.Unix(startDayUnix, 0)
  259. time2 := time.Unix(endDayUnix, 0)
  260. // Truncate to the start of the day (00:00:00)
  261. startOfDay1 := time.Date(time1.Year(), time1.Month(), time1.Day(), 0, 0, 0, 0, time1.Location())
  262. startOfDay2 := time.Date(time2.Year(), time2.Month(), time2.Day(), 0, 0, 0, 0, time2.Location())
  263. // Calculate the difference in days
  264. daysDiff := int64(startOfDay2.Sub(startOfDay1).Hours() / 24)
  265. // Adjust sign if time2 is before time1
  266. if startOfDay2.Before(startOfDay1) {
  267. daysDiff = -daysDiff
  268. }
  269. return daysDiff
  270. }
  271. // MsgFormat 格式化消息字符串 字符串里面有多个冒号,仅删除冒号前后的空格(如果存在)
  272. func MsgFormat(input string) string {
  273. // 定义正则表达式,用于匹配冒号两边的空格
  274. re := regexp.MustCompile(`\s*:\s*`)
  275. // 使用正则表达式替换所有匹配的部分
  276. return re.ReplaceAllString(input, ":")
  277. }
  278. /*
  279. GetNeckRingActiveTimer
  280. 1. frameId值如果是:1到6代表每天的0点到2点,11-16 代表每天的2点到4点, 21-26 代表每天的4点到6点,31-36代表每天的6点到8点,
  281. 41-46 代表每天的8点到10点,51-56代表每天的10点到12点,61-66代表每天的12-14点,71-76代表每天的14-16点,81-86代表每天的16-18点,
  282. 91-96代表每天的18-20点,101-106代表每天的20-22点,111-116代表每天的22-24点。其中每天数字代表20分钟。如果frameId大于接受时间点,就代表frameId是昨天的。
  283. 2. 如果farmId取值出现8,18,28,3848,58,68,78,88,98,108,118数字分别代表2个小时,从0-2点开始,以此类推。
  284. 帮我根据frameId值,获取对应的时间点
  285. todo 116和118有问题,需要处理
  286. */
  287. func GetNeckRingActiveTimer(frameId int32) (dateTime string, hours int) {
  288. if frameId < 0 || frameId > 118 {
  289. return "", 0
  290. }
  291. nowTime := time.Now()
  292. currHour := nowTime.Hour()
  293. // 处理2小时的特殊 farmId
  294. specialHours := map[int]int{
  295. 8: 2, 18: 4, 28: 6, 38: 8, 48: 10, 58: 12, 68: 14, 78: 16, 88: 18, 98: 20, 108: 22, 118: 0,
  296. }
  297. hours, ok := specialHours[int(frameId)]
  298. startHour := hours
  299. if ok {
  300. if hours > currHour {
  301. startHour -= 24
  302. }
  303. dateTime = time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), startHour, 0, 0, 0, nowTime.Location()).Format(Layout)
  304. return
  305. }
  306. hours = int(math.Floor(float64(frameId)/10) * 2)
  307. units := int(frameId % 10)
  308. hours += units / 3
  309. startHour = hours
  310. if hours > currHour {
  311. startHour -= 24
  312. }
  313. dateTime = time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), startHour, 0, 0, 0, nowTime.Location()).Format(Layout)
  314. return
  315. }