util.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. package util
  2. import (
  3. "bytes"
  4. "fmt"
  5. "math"
  6. "math/rand"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "gitee.com/xuyiping_admin/pkg/xerr"
  12. )
  13. const (
  14. LayoutTime = "2006-01-02 15:04:05"
  15. Layout = "2006-01-02"
  16. LayoutMonth = "2006-01"
  17. LetterBytes = "abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ23456789"
  18. LetterIdxBits = 6 // 6 bits to represent a letter index
  19. LetterIdxMask = 1<<LetterIdxBits - 1 // All 1-bits, as many as letterIdxBits
  20. LetterIdxMax = 63 / LetterIdxBits // # of letter indices fitting in 63 bits
  21. )
  22. var (
  23. FrameIdMap = map[int32]int32{
  24. 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 8: 8, // 00-02
  25. 11: 11, 12: 12, 13: 13, 14: 14, 15: 15, 16: 16, 18: 18, // 02-04
  26. 21: 21, 22: 22, 23: 23, 24: 24, 25: 25, 26: 26, 28: 28, // 04-06
  27. 31: 31, 32: 32, 33: 33, 34: 34, 35: 35, 36: 36, 38: 38, // 06-08
  28. 41: 41, 42: 42, 43: 43, 44: 44, 45: 45, 46: 46, 48: 48, // 08-10
  29. 51: 51, 52: 52, 53: 53, 54: 54, 55: 55, 56: 56, 58: 58, // 10-12
  30. 61: 61, 62: 62, 63: 63, 64: 64, 65: 65, 66: 66, 68: 68, // 12-14
  31. 71: 71, 72: 72, 73: 73, 74: 74, 75: 75, 76: 76, 78: 78, // 14-16
  32. 81: 81, 82: 82, 83: 83, 84: 84, 85: 85, 86: 86, 88: 88, // 16-18
  33. 91: 91, 92: 92, 93: 93, 94: 94, 95: 95, 96: 96, 98: 98, // 18-20
  34. 101: 101, 102: 102, 103: 103, 104: 104, 105: 105, 106: 106, 108: 108, // 20-22
  35. 111: 111, 112: 112, 113: 113, 114: 114, 115: 115, 116: 116, 118: 118, // 22-24
  36. }
  37. 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}
  38. FrameIdMapReverse = map[int32]int32{
  39. 0: 8,
  40. 1: 8,
  41. 2: 18,
  42. 3: 18,
  43. 4: 28,
  44. 5: 28,
  45. 6: 38,
  46. 7: 38,
  47. 8: 48,
  48. 9: 48,
  49. 10: 58,
  50. 11: 58,
  51. 12: 68,
  52. 13: 68,
  53. 14: 78,
  54. 15: 78,
  55. 16: 88,
  56. 17: 88,
  57. 18: 98,
  58. 19: 98,
  59. 20: 108,
  60. 21: 108,
  61. 22: 118, // 04-06
  62. 23: 118,
  63. }
  64. ExpectedFrameIDs = []int32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
  65. )
  66. // GenerateRandomNumberString 生成指定长度的数字串
  67. func GenerateRandomNumberString(n int) string {
  68. result := make([]byte, n)
  69. // A rand.Int63() generates 63 random bits, enough for letterIdxMax characters!
  70. rand.Seed(time.Now().UnixNano())
  71. for i, cache, remain := n-1, rand.Int63(), LetterIdxMax; i >= 0; {
  72. if remain == 0 {
  73. cache, remain = rand.Int63(), LetterIdxMax
  74. }
  75. if idx := int(cache & LetterIdxMask); idx < len(LetterBytes) {
  76. result[i] = LetterBytes[idx]
  77. i--
  78. }
  79. cache >>= LetterIdxBits
  80. remain--
  81. }
  82. return string(result)
  83. }
  84. // TimeParseLocalUnix 获取当天零点的时间戳
  85. // eg 2023-02-22 => 1676995200
  86. func TimeParseLocalUnix(DayTime string) int64 {
  87. value := DayTime
  88. if len(DayTime) <= 11 {
  89. value = fmt.Sprintf("%s 00:00:00", DayTime)
  90. }
  91. loc, _ := time.LoadLocation("Local")
  92. theTime, _ := time.ParseInLocation(LayoutTime, value, loc)
  93. return theTime.Unix()
  94. }
  95. // TimeParseLocalEndUnix 获取当天24点的时间戳
  96. // eg 2023-02-22 => 1676995200
  97. func TimeParseLocalEndUnix(DayTime string) int64 {
  98. value := DayTime
  99. if len(DayTime) <= 11 {
  100. value = fmt.Sprintf("%s 23:59:59", DayTime)
  101. }
  102. loc, _ := time.LoadLocation("Local")
  103. theTime, _ := time.ParseInLocation(LayoutTime, value, loc)
  104. return theTime.Unix()
  105. }
  106. // ConvertParseLocalUnix 字符串转换当天时间戳
  107. // eg 15:04:05 => 1676998245
  108. func ConvertParseLocalUnix(timeParse string) (int64, error) {
  109. loc, err := time.LoadLocation("Local")
  110. if err != nil {
  111. return 0, err
  112. }
  113. value := fmt.Sprintf("%s %s", time.Now().Format(Layout), timeParse)
  114. theTime, err := time.ParseInLocation(LayoutTime, value, loc)
  115. if err != nil {
  116. return 0, err
  117. }
  118. return theTime.Unix(), nil
  119. }
  120. // GetMonthRemainDay 获取当前月还剩几天
  121. func GetMonthRemainDay() int {
  122. now := time.Now()
  123. lastDayOfMonth := time.Date(now.Year(), now.Month()+1, 0, 23, 59, 59, 999999999, now.Location())
  124. return int(lastDayOfMonth.Sub(now).Hours()/24) + 1
  125. }
  126. // Ceil 向上取整函数
  127. func Ceil(x float64) float64 {
  128. // 使用 math.Floor 计算小于或等于 x 的最大整数
  129. // 然后检查 x 是否为整数,如果不是,则结果加 1
  130. // 注意:math.Floor 返回的是 float64 类型,所以我们需要进行比较
  131. // 来确定是否需要加 1
  132. intPart := math.Floor(x)
  133. if x-intPart > 0 {
  134. return intPart + 1
  135. }
  136. return intPart
  137. }
  138. // GetLastDayOfMonth
  139. // 接受一个字符串形式的月份(如 "2024-12"),
  140. // 并返回该月份的最后一天的日期(2024-12-31)
  141. func GetLastDayOfMonth(month string) (string, error) {
  142. t, err := time.Parse(LayoutMonth, month)
  143. if err != nil {
  144. return "", err // 如果解析失败,返回错误
  145. }
  146. // 获取下个月的第一天
  147. nextMonth := t.AddDate(0, 1, 0)
  148. // 返回上个月的最后一天
  149. lastDay := nextMonth.AddDate(0, 0, -1)
  150. return lastDay.Format(Layout), nil
  151. }
  152. // GetMonthsInRange
  153. // 接受两个字符串形式的月份(如 "2024-01" 到 "2024-12"),
  154. // 并返回一个包含这两个月份之间所有月份的字符串切片。
  155. func GetMonthsInRange(startMonth, endMonth string) ([]string, error) {
  156. // 解析起始月份
  157. startTime, err := time.Parse(LayoutMonth, startMonth)
  158. if err != nil {
  159. return nil, err
  160. }
  161. // 解析结束月份
  162. endTime, err := time.Parse(LayoutMonth, endMonth)
  163. if err != nil {
  164. return nil, err
  165. }
  166. // 初始化结果切片
  167. var months []string
  168. // 循环添加每个月份直到达到或超过结束月份
  169. for curr := startTime; curr.Before(endTime) || curr.Equal(endTime); curr = curr.AddDate(0, 1, 0) {
  170. months = append(months, curr.Format(LayoutMonth))
  171. }
  172. return months, nil
  173. }
  174. // RoundToTwoDecimals 四舍五入并保留两位小数
  175. func RoundToTwoDecimals(num float64) float64 {
  176. // 使用 math.Round 函数进行四舍五入
  177. // 先乘以 100,然后四舍五入,最后除以 100
  178. return math.Round(num*100) / 100
  179. }
  180. // Get21DayPeriods 获取范围时间内21天周期的切片
  181. // 从结束时间开始往前推算,超过开始时间,继续向前推算,直至凑整21天
  182. func Get21DayPeriods(startDay, endDay string) ([][]string, error) {
  183. startDate, err := time.Parse(Layout, startDay)
  184. if err != nil {
  185. return nil, err
  186. }
  187. endDate, err := time.Parse(Layout, endDay)
  188. if err != nil {
  189. return nil, err
  190. }
  191. if startDate.After(endDate) {
  192. return nil, xerr.Custom("start date is after end date")
  193. }
  194. var periods [][]string
  195. for date := endDate; date.After(startDate); {
  196. // Calculate the end of the current period
  197. periodEnd := date.AddDate(0, 0, -20)
  198. // Append the current period to the result slice
  199. periods = append(periods, []string{periodEnd.Format(Layout), date.Format(Layout)})
  200. // Move on to the next day
  201. date = periodEnd.AddDate(0, 0, -1)
  202. }
  203. reverseRows(periods)
  204. return periods, nil
  205. }
  206. func reverseRows(matrix [][]string) {
  207. // 获取矩阵的行数
  208. rows := len(matrix)
  209. if rows == 0 {
  210. return // 如果矩阵为空,则直接返回
  211. }
  212. // 初始化两个指针,一个指向开始,一个指向末尾
  213. for i, j := 0, rows-1; i < j; i, j = i+1, j-1 {
  214. // 交换当前行和对应行
  215. matrix[i], matrix[j] = matrix[j], matrix[i]
  216. }
  217. }
  218. // GetRangeDayMiddleDay 获取指定日期范围中间的某一天
  219. func GetRangeDayMiddleDay(dateRange []string, middleDay int32) (string, error) {
  220. if len(dateRange) < 2 {
  221. return "", xerr.Custom("date range is not enough")
  222. }
  223. if middleDay < 0 {
  224. return "", xerr.Custom("middle day is not enough")
  225. }
  226. startDate, _ := time.Parse(Layout, dateRange[0])
  227. return startDate.AddDate(0, 0, int(middleDay)-1).Format(Layout), nil
  228. }
  229. // GetRangeDayByDays 获取指定范围日期内按照指定天数来切割
  230. // (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]]
  231. func GetRangeDayByDays(startDay, endDay string, days int32) ([][]string, error) {
  232. var res [][]string
  233. if days <= 0 {
  234. return res, nil
  235. }
  236. startDate, err := time.Parse(Layout, startDay)
  237. if err != nil {
  238. return nil, err
  239. }
  240. endDate, err := time.Parse(Layout, endDay)
  241. if err != nil {
  242. return nil, err
  243. }
  244. if startDate.After(endDate) {
  245. return nil, xerr.Custom("start date is after end date")
  246. }
  247. if startDate == endDate {
  248. return [][]string{{startDay, endDay}}, nil
  249. }
  250. for date := startDate; date.Before(endDate) || date.Equal(endDate); date = date.AddDate(0, 0, int(days)) {
  251. if date.AddDate(0, 0, int(days)-1).After(endDate) {
  252. res = append(res, []string{date.Format(Layout), endDate.Format(Layout)})
  253. break
  254. }
  255. res = append(res, []string{date.Format(Layout), date.AddDate(0, 0, int(days)-1).Format(Layout)})
  256. }
  257. return res, nil
  258. }
  259. // 计算样本均值
  260. func mean(data []float64) float64 {
  261. sum := 0.0
  262. for _, v := range data {
  263. sum += v
  264. }
  265. return sum / float64(len(data))
  266. }
  267. // 计算样本标准差
  268. func stddev(data []float64, mean float64) float64 {
  269. sum := 0.0
  270. for _, v := range data {
  271. sum += (v - mean) * (v - mean)
  272. }
  273. variance := sum / float64(len(data)-1)
  274. return math.Sqrt(variance)
  275. }
  276. // ConfidenceInterval 计算95%置信区间
  277. func ConfidenceInterval(data []float64) (float64, float64) {
  278. n := float64(len(data))
  279. meanVal := mean(data)
  280. stdDev := stddev(data, meanVal)
  281. z := 1.96 // 95%置信水平对应的Z值
  282. marginOfError := z * (stdDev / math.Sqrt(n))
  283. lowerBound := meanVal - marginOfError
  284. upperBound := meanVal + marginOfError
  285. return lowerBound, upperBound
  286. }
  287. // ConfidenceInterval2 计算95%置信区间
  288. func ConfidenceInterval2(p float64, total float64) (min, max float64) {
  289. if p <= 0 || total <= 0 {
  290. return 0, 0
  291. }
  292. z := 1.96 // 95%置信水平对应的Z值
  293. marginOf := z * math.Sqrt(p*(1-p)/total) * 100
  294. return math.Max(0, p*100-math.Ceil(marginOf)), math.Max(1, p*100+math.Ceil(marginOf))
  295. }
  296. // RemoveDuplicates 去除切片中的重复元素
  297. func RemoveDuplicates(slice []string) []string {
  298. if len(slice) <= 0 {
  299. return slice
  300. }
  301. // 创建一个map来跟踪已经遇到的元素
  302. seen := make(map[string]struct{})
  303. // 创建一个新的切片来存储不重复的元素
  304. var result []string
  305. // 遍历原始切片
  306. for _, v := range slice {
  307. // 如果元素尚未在map中,则将其添加到结果切片和map中
  308. if _, exists := seen[v]; !exists {
  309. seen[v] = struct{}{}
  310. result = append(result, v)
  311. }
  312. }
  313. // 返回不重复的切片
  314. return result
  315. }
  316. // DaysBetween 计算两个日期(时间戳)之间的天数差
  317. func DaysBetween(startDayUnix int64, endDayUnix int64) int64 {
  318. time1 := time.Unix(startDayUnix, 0)
  319. time2 := time.Unix(endDayUnix, 0)
  320. // Truncate to the start of the day (00:00:00)
  321. startOfDay1 := time.Date(time1.Year(), time1.Month(), time1.Day(), 0, 0, 0, 0, time1.Location())
  322. startOfDay2 := time.Date(time2.Year(), time2.Month(), time2.Day(), 0, 0, 0, 0, time2.Location())
  323. // Calculate the difference in days
  324. daysDiff := int64(startOfDay2.Sub(startOfDay1).Hours() / 24)
  325. return daysDiff
  326. }
  327. // GetDaysBetween 获取两个日期之间的所有天数
  328. // 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]
  329. func GetDaysBetween(startDate, endDate string) ([]string, error) {
  330. // 解析日期
  331. start, err := time.Parse(Layout, startDate)
  332. if err != nil {
  333. return nil, fmt.Errorf("解析开始日期失败: %v", err)
  334. }
  335. end, err := time.Parse(Layout, endDate)
  336. if err != nil {
  337. return nil, fmt.Errorf("解析结束日期失败: %v", err)
  338. }
  339. // 确保开始日期早于或等于结束日期
  340. if start.After(end) {
  341. return nil, fmt.Errorf("开始日期不能晚于结束日期")
  342. }
  343. // 存储日期的数组
  344. var days []string
  345. // 遍历日期
  346. for current := start; !current.After(end); current = current.AddDate(0, 0, 1) {
  347. days = append(days, current.Format(Layout))
  348. }
  349. return days, nil
  350. }
  351. // GetMonthsBetween 返回两个日期之间的所有月份
  352. // 参数格式为 "YYYY-MM",例如 "2025-01"
  353. func GetMonthsBetween(start, end string) ([]string, error) {
  354. // 检查参数合法性
  355. if err := validateDate(start); err != nil {
  356. return nil, fmt.Errorf("invalid start date: %v", err)
  357. }
  358. if err := validateDate(end); err != nil {
  359. return nil, fmt.Errorf("invalid end date: %v", err)
  360. }
  361. // 解析起始日期和结束日期
  362. startTime, err := time.Parse(LayoutMonth, start)
  363. if err != nil {
  364. return nil, fmt.Errorf("failed to parse start date: %v", err)
  365. }
  366. endTime, err := time.Parse(LayoutMonth, end)
  367. if err != nil {
  368. return nil, fmt.Errorf("failed to parse end date: %v", err)
  369. }
  370. // 检查起始日期是否早于结束日期
  371. if startTime.After(endTime) {
  372. return nil, xerr.Custom("start date must be before or equal to end date")
  373. }
  374. // 生成月份列表
  375. var months []string
  376. for current := startTime; !current.After(endTime); current = current.AddDate(0, 1, 0) {
  377. months = append(months, current.Format(LayoutMonth))
  378. }
  379. return months, nil
  380. }
  381. // validateDate 检查日期字符串的合法性
  382. func validateDate(date string) error {
  383. if len(date) != 7 || date[4] != '-' {
  384. return xerr.Custom("date format must be YYYY-MM")
  385. }
  386. _, err := time.Parse(LayoutMonth, date)
  387. if err != nil {
  388. return xerr.Customf("invalid date: %v", err)
  389. }
  390. return nil
  391. }
  392. // MsgFormat 格式化消息字符串 字符串里面有多个冒号,仅删除冒号前后的空格(如果存在)
  393. func MsgFormat(input string) string {
  394. // 定义正则表达式,用于匹配冒号两边的空格
  395. re := regexp.MustCompile(`\s*:\s*`)
  396. // 使用正则表达式替换所有匹配的部分
  397. return re.ReplaceAllString(input, ":")
  398. }
  399. // IsValidFrameId 检查 FrameId 是否有效
  400. func IsValidFrameId(frameId int32) bool {
  401. _, ok := FrameIdMap[frameId]
  402. return ok
  403. }
  404. /*
  405. GetNeckRingActiveTimer
  406. 1. frameId值如果是:1到6代表每天的0点到2点,11-16 代表每天的2点到4点, 21-26 代表每天的4点到6点,31-36代表每天的6点到8点,
  407. 41-46 代表每天的8点到10点,51-56代表每天的10点到12点,61-66代表每天的12-14点,71-76代表每天的14-16点,81-86代表每天的16-18点,
  408. 91-96代表每天的18-20点,101-106代表每天的20-22点,111-116代表每天的22-24点。其中每个数字代表20分钟。
  409. 2. 如果frameId大于当前时间点,并往前推20个小时,如果在这个20小时范围内, 就代表frameId是昨天的。
  410. 3. 如果farmId的值出现8,18,28,3848,58,68,78,88,98,108,118数字分别代表2个小时,从0-2点开始,以此类推。
  411. 帮我根据frameId值,获取对应的时间(YYYY-MM-DD)和小时
  412. */
  413. func GetNeckRingActiveTimer(frameId int32) (dateTime string, hours int) {
  414. if frameId < 0 || frameId > 118 {
  415. return "", 0
  416. }
  417. if _, ok := FrameIdMap[frameId]; !ok {
  418. return "", 0
  419. }
  420. nowTime := time.Now()
  421. currHour := nowTime.Hour()
  422. // 处理2小时的特殊 farmId
  423. hours, ok := SpecialHours[int(frameId)]
  424. day := 0
  425. if ok {
  426. if hours == 0 {
  427. hours = 24
  428. }
  429. if hours > currHour {
  430. day = -1
  431. }
  432. hours = 0
  433. dateTime = nowTime.AddDate(0, 0, day).Format(Layout)
  434. return
  435. }
  436. hours = int(math.Floor(float64(frameId)/10) * 2)
  437. units := int(frameId % 10)
  438. hours += units / 3
  439. if hours > currHour {
  440. for i := 0; i <= 20; i++ {
  441. twentyHoursAgo := nowTime.Add(-time.Duration(i) * time.Hour).Hour()
  442. if twentyHoursAgo == 0 {
  443. twentyHoursAgo = 24
  444. }
  445. if hours == twentyHoursAgo {
  446. day = -1
  447. break
  448. }
  449. }
  450. }
  451. dateTime = nowTime.AddDate(0, 0, day).Format(Layout)
  452. return
  453. }
  454. // XFrameId 获取XFrameId
  455. func XFrameId(frameid int32) int32 {
  456. return int32(math.Floor(float64(frameid / 10)))
  457. }
  458. // FrameIds 获取FrameIds
  459. func FrameIds(xFrameId int32) []int32 {
  460. frameIds := make([]int32, 0)
  461. for i := 1; i <= 8; i++ {
  462. if i == 7 {
  463. continue
  464. }
  465. frameIds = append(frameIds, xFrameId*10+int32(i))
  466. }
  467. return frameIds
  468. }
  469. func CurrentMaxFrameId() int32 {
  470. currentHour := time.Now().Hour()
  471. return int32(math.Floor(float64(currentHour/2))) * 10
  472. }
  473. func ArrayInt32ToStrings(cowIds []int32, cutset string) string {
  474. var cows bytes.Buffer
  475. if len(cowIds) <= 0 {
  476. return cows.String()
  477. }
  478. for i, v := range cowIds {
  479. if i > 0 {
  480. cows.WriteString(cutset)
  481. }
  482. cows.WriteString(strconv.Itoa(int(v))) // 将整数转换为字符串
  483. }
  484. return cows.String()
  485. }
  486. // ConvertCowIdsToInt64Slice 将逗号拼接的字符串转换为 int64 切片
  487. func ConvertCowIdsToInt64Slice(input string) ([]int64, error) {
  488. // 如果输入为空,直接返回空切片
  489. if input == "" {
  490. return []int64{}, nil
  491. }
  492. // 按逗号分割字符串
  493. parts := strings.Split(input, ",")
  494. // 初始化结果切片
  495. result := make([]int64, 0, len(parts))
  496. // 遍历每个部分,转换为 int64
  497. for _, part := range parts {
  498. // 去除空格
  499. part = strings.TrimSpace(part)
  500. if part == "" {
  501. continue // 忽略空字符串
  502. }
  503. // 转换为 int64
  504. num, err := strconv.ParseInt(part, 10, 64)
  505. if err != nil {
  506. return nil, fmt.Errorf("invalid number: %s", part)
  507. }
  508. // 添加到结果切片
  509. result = append(result, num)
  510. }
  511. return result, nil
  512. }
  513. // GetMonthStartAndEndTimestamp 获取当前月份的开始时间戳和结束时间戳
  514. func GetMonthStartAndEndTimestamp() (startTimestamp, endTimestamp int64) {
  515. // 获取当前时间
  516. now := time.Now()
  517. // 获取当前月份的第一天
  518. startOfMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
  519. // 获取下一个月份的第一天,然后减去一秒,得到当前月份的最后一天
  520. endOfMonth := startOfMonth.AddDate(0, 1, 0).Add(-time.Second)
  521. // 转换为时间戳
  522. startTimestamp = startOfMonth.Unix()
  523. endTimestamp = endOfMonth.Unix()
  524. return startTimestamp, endTimestamp
  525. }
  526. // SubDays 计算两个日期(时间戳)之间的天数差
  527. func SubDays(startDay, endDay int64) int32 {
  528. s1 := time.Unix(startDay, 0)
  529. s2 := time.Unix(endDay, 0)
  530. return int32(s2.Sub(s1).Hours() / 24)
  531. }