golang时间与时区相关操作总结
前言
任何语言处理时间的功能都是最基础也是平时十分常用的,另外需要注意任何脱离时区的时间都是没有任何意义的!
这里总结一下笔者在这一个多月写go项目用到的、收集到的一些好用的处理时间的方法以及时间处理的一些基础知识点。
golang时间操作基础 ***
go关于时间操作的基础我这边自己做了一下笔记:
自己项目中封装的整合 ***
注意:GetTimeSubSecs这个方法后面不要加.Seconds()
package utils import ( "errors" "fmt" "time" ) const ( // 获取当前时间字符串的固定格式 CurrentTimeFormat = "2006-01-02 15:04:05" MYNano = "2006-01-02 15:04:05.000000000" MYMicro = "2006-01-02 15:04:05.000000" MYMil = "2006-01-02 15:04:05.000" MYSec = "2006-01-02 15:04:05" MYCST = "2006-01-02 15:04:05 +0800 CST" MYUTC = "2006-01-02 15:04:05 +0000 UTC" MYDate = "2006-01-02" MYTime = "15:04:05" FBTIME = "2006-01-02T15:04:05+0800" APPTIME = "2006-01-02T15:04:05.000" TWITTERTIME = "2006-01-02T15:04:05Z" ) var TimeLocation *time.Location func init() { var err error TimeLocation, err = time.LoadLocation("Asia/Shanghai") if err != nil { panic(err) } } // 使用缓存重构的活动时间节点 func GetCacheActivityStartTime() time.Time { return time.Date(2023, 12, 12, 0, 0, 0, 0, TimeLocation) } func GetCurrentTime() time.Time { return time.Now().In(TimeLocation) } func GetCurrentTimeString() string { return time.Now().In(TimeLocation).Format(CurrentTimeFormat) } // 时间转时间戳(秒) func Time2TimeStampSecond(t time.Time) int64 { return t.Unix() } // 时间转时间戳(毫秒) func Time2TimeStampMilliSecond(t time.Time) int64 { return t.UnixMilli() } // 当前毫秒级别时间戳 func GetCurrentTimeStampMilli() int64 { return time.Now().In(TimeLocation).UnixMilli() } // 当前秒级别时间戳 func GetCurrentTimeStampSec() int64 { return time.Now().In(TimeLocation).Unix() } // 间戳转时间系列 // 第一个参数表示秒级别的时间戳,第二个参数表示纳秒级别的时间戳,如果是毫秒级别的时间戳需要先乘以1e6然后放入第二个参数即可 func timestamp2Time(sec int64, nsec int64) time.Time { return time.Unix(sec, nsec).In(TimeLocation) } // 秒级别时间戳转时间 func TimestampSec2Time(sec int64) time.Time { return timestamp2Time(sec, 0).In(TimeLocation) } // 毫秒级别时间戳转时间 func TimestampMilli2Time(stamp int64) time.Time { return timestamp2Time(0, stamp*1e6).In(TimeLocation) } // 纳秒级别时间戳转时间 func TimestampNano2Time(stamp int64) time.Time { return timestamp2Time(0, stamp).In(TimeLocation) } // 返回参数1: t1时间减去t2时间的时间差 // 返回参数2: t1时间是否在t2时间的后面 func GetTimeSubSecs(t1, t2 time.Time) time.Duration { // Notice 后面不要加Seconds()方法,有时候即使t1在t2后面返回的也有可能是一个负数! return t1.Sub(t2) } // 时间字符串转时间 func TimeStr2Time(timeStr string) (time.Time, error) { // 可能的转换格式 useFormat := []string{ MYNano, MYMicro, MYMil, MYSec, MYCST, MYUTC, MYDate, MYTime, FBTIME, APPTIME, TWITTERTIME, time.RFC3339, time.RFC3339Nano, } var t time.Time // 默认: 0001-01-01 00:00:00 +0000 UTC for _, useF := range useFormat { tt, err1 := time.ParseInLocation(useF, timeStr, TimeLocation) if err1 != nil { continue } t = tt break } if t == getTimeDefault() { // 0001-01-01 00:00:00 +0000 UTC return t, errors.New(fmt.Sprintf("时间字符串格式错误! timeStr: %v", timeStr)) } return t, nil } func getTimeDefault() time.Time { t, _ := time.ParseInLocation("2006-01-02 15:04:05", "", TimeLocation) return t }
package utils import ( "fmt" "testing" "time" ) func TestT1(t *testing.T) { var ti time.Time fmt.Println("ti: ", ti) fmt.Println("当前秒级别时间戳: ", Time2TimeStampSecond(GetCurrentTime())) fmt.Println("当前毫秒级别时间戳: ", Time2TimeStampMilliSecond(GetCurrentTime())) } func TestTimestampSec2Time(t *testing.T) { var stampSec int64 = 1681280157 ret := TimestampSec2Time(stampSec) fmt.Printf(">>>>> %T, %v", ret, ret) } func TestGetTimeSubSecs(t *testing.T) { var stampSec int64 = 1681900577 t1 := TimestampSec2Time(stampSec) fmt.Println("t1: ", t1) t2 := GetCurrentTime() fmt.Println("t2: ", t2) ret1 := GetTimeSubSecs(t1, t2) fmt.Printf(">>> ret1Type: %T, ret1: %v, ret1是否大于0: %v \n", ret1, ret1, ret1 > 0) }
当前时间加7天时间的秒级时间戳
// 当前时间加7天时间的秒级时间戳 func GetAfter7DaysTimestampSec() int64 { t7 := GetCurrentTime().Add(time.Hour * 24 * 7) return Time2TimeStampSecond(t7) }
~~~
// 获取当前时间距离当天结束还有多少秒 —— redis中设置过期key常用 // 需要导入第三方包:github.com/jinzhu/now func GetEndOfDayRemainSeconds() time.Duration { currentTime := time.Now().In(TIME_LOCATION) return time.Second * time.Duration(now.New(currentTime).EndOfDay().Sub(currentTime).Seconds()) }
~~~
// 根据一个时间数字获取那一天开始的时间戳 // in: 20240212 // out: 1707667200,格式化的时间是:2024-02-12 00:00:00 func GetStartOfDayTImeStampByDateInt64(dateInt64 int64) (int64, error) { dateStr := fmt.Sprintf("%08d", dateInt64) // 解析日期字符串 parsedDate, err := time.Parse("20060102", dateStr) if err != nil { return 0, err } startOfDay := time.Date(parsedDate.Year(), parsedDate.Month(), parsedDate.Day(), 0, 0, 0, 0, TimeLocation) // 转换为时间戳 timestamp := startOfDay.Unix() return timestamp, nil }
~~~
常用的方法 ***
获取当前时间开始的小时(分钟)时间 - Truncate方法
func TestT85(t *testing.T) { fmt.Println(">>>当前时间绝对值 ", time.Now()) fmt.Println(">>>当前时间开始小时 ", time.Now().Truncate(time.Hour)) fmt.Println(">>>当前时间开始分钟 ", time.Now().Truncate(time.Minute)) /* >>>当前时间绝对值 2022-06-28 11:10:10.944918 +0800 CST m=+0.000980066 >>>当前时间开始小时 2022-06-28 11:00:00 +0800 CST >>>当前时间开始分钟 2022-06-28 11:10:00 +0800 CST */
// 计算时间差的好用方法 startDate := time.Now().Truncate(time.Hour).Add(-time.Hour * 2) fmt.Println("startDate: ", startDate) // startDate: 2022-06-28 09:00:00 +0800 CST endDate := startDate.Add(time.Hour - time.Second) fmt.Println("endDate: ", endDate) // endDate: 2022-06-28 09:59:59 +0800 CST }
初始化时区的方法
package time_module import "time" var TIME_LOCATION *time.Location func init() { var err error TIME_LOCATION, err = time.LoadLocation("Asia/Shanghai") if err != nil { panic(err) } } // 当前时区的当前时间 func GetCurrentTime() time.Time { return time.Now().In(TIME_LOCATION) }
封装的一些简单方法
package time_module import ( "github.com/jinzhu/now" "time" ) /* 时间比较与加减:注意先转换到同一个时区!在底层方法中将时间转换到同一个时区 */ // 2个日期是否相等(注意先转到同一个时区) func DateEqual(date1, date2 time.Time) bool { date1 = date1.In(TIME_LOCATION) date2 = date2.In(TIME_LOCATION) y1, m1, d1 := date1.Date() y2, m2, d2 := date2.Date() return y1 == y2 && m1 == m2 && d1 == d2 } // 2个日期的前后 先转到同一时区 func DateAfter(date1, date2 time.Time) bool { // 在同一个时区比较 date1 = date1.In(TIME_LOCATION) date2 = date2.In(TIME_LOCATION) return date1.After(date2) } // 计算2个时间相差的天数 先转到同一个时区 // 注意时间前后:t1 需要比 t2 大!如果t1比t2小得出的结果会是一个负数! func TimeSubDays(t1, t2 time.Time) int { // 转换成同一个时区去比较! t1 = t1.In(TIME_LOCATION) t2 = t2.In(TIME_LOCATION) t1 = time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, TIME_LOCATION) t2 = time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, TIME_LOCATION) return int(t1.Sub(t2).Hours() / 24) } // 获取当前时间距离当天结束还有多少秒 —— redis中设置过期key常用 // 需要导入第三方包:github.com/jinzhu/now func GetEndOfDayRemainSeconds() time.Duration { currentTime := time.Now().In(TIME_LOCATION) return time.Second * time.Duration(now.New(currentTime).EndOfDay().Sub(currentTime).Seconds()) }
计算2个时间相差的天数的测试
package scripts_stroage import ( "fmt" "testing" "time" ) var TimeLocation *time.Location // 包初始化的时候初始化时区!!! func init() { var err error TimeLocation, err = time.LoadLocation("Asia/Shanghai") if err != nil { panic(err) } } // 计算两个时间相差的天数 TODO 注意时间前后:t1 需要比 t2 大!如果t1比t2小得出的结果会是一个负数!!! func TimeSubDays(t1, t2 time.Time) int { // 转换成同一个时区去比较! t1 = t1.In(TimeLocation) t2 = t2.In(TimeLocation) fmt.Println("T1>>> ", t1) fmt.Println("T2>>> ", t2) t1 = time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, TimeLocation) t2 = time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, TimeLocation) return int(t1.Sub(t2).Hours() / 24) } func TestTimeSubDays(t *testing.T) { // 1、用东8区时区创建时间,结果是1 t1 := time.Date(2018, 1, 10, 0, 0, 1, 100, time.Local) t2 := time.Date(2018, 1, 9, 23, 59, 22, 100, time.Local) fmt.Println("t1: ", t1) fmt.Println("t2: ", t2) // 即使相差1秒,相差也是1天 println("1>>> ", TimeSubDays(t1, t2)) // TODO 2、用UTC时区创造2个时间注意一下~ // TODO 是因为TimeSubDays方法中的In方法将2个时间统一转换成东8区的时间区处理了!这2个时间转换成东8区时间确实是同一天! t3 := time.Date(2017, 1, 10, 0, 0, 1, 100, time.UTC) t4 := time.Date(2017, 1, 9, 23, 59, 22, 100, time.UTC) fmt.Println("t3: ", t3) fmt.Println("t4: ", t4) println("2>>> ", TimeSubDays(t3, t4)) }
封装好的一些方法合集(使用上面初始化的时区) ******
package time_module import ( "fmt" "strconv" "strings" "time" ) // GetMillis is a convenience method to get milliseconds since epoch. func GetMillis() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } func GetMillisStr() string { return fmt.Sprint(GetMillis()) } // GetMillisForTime is a convenience method to get milliseconds since epoch for provided Time. func GetMillisForTime(thisTime time.Time) int64 { return thisTime.UnixNano() / int64(time.Millisecond) } func GetSecondsForMillisecond(millisecond int64) int64 { return millisecond / int64(time.Microsecond) } // PadDateStringZeros is a convenience method to pad 2 digit date parts with zeros to meet ISO 8601 format func PadDateStringZeros(dateString string) string { parts := strings.Split(dateString, "-") for index, part := range parts { if len(part) == 1 { parts[index] = "0" + part } } dateString = strings.Join(parts[:], "-") return dateString } // GetStartOfDayMillis is a convenience method to get milliseconds since epoch for provided date's start of day func GetStartOfDayMillis(thisTime time.Time, loc *time.Location) int64 { resultTime := time.Date(thisTime.Year(), thisTime.Month(), thisTime.Day(), 0, 0, 0, 0, loc) return GetMillisForTime(resultTime) } // GetEndOfDayMillis is a convenience method to get milliseconds since epoch for provided date's end of day func GetEndOfDayMillis(thisTime time.Time, loc *time.Location) int64 { resultTime := time.Date(thisTime.Year(), thisTime.Month(), thisTime.Day(), 23, 59, 59, 999999999, loc) return GetMillisForTime(resultTime) } // GetStartOfDayMillis is a convenience method to get milliseconds since epoch for provided date's start of day func GetStartOfMonthMillis(thisTime time.Time, loc *time.Location) int64 { resultTime := time.Date(thisTime.Year(), thisTime.Month(), 1, 0, 0, 0, 0, loc) return GetMillisForTime(resultTime) } // GetEndOfMonthMillis is a convenience method to get milliseconds since epoch for provided date's end of day func GetStartOfNextMonthMillis(thisTime time.Time, loc *time.Location) int64 { resultTime := time.Date(thisTime.Year(), thisTime.Month(), 1, 0, 0, 0, 0, loc) resultTime = resultTime.AddDate(0, 1, 0) return GetMillisForTime(resultTime) } // 下一个月开始1号的时间戳 func GetStartOfNextMonthMillisByTimeStamp(timeStamp int64) int64 { tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION) return GetStartOfNextMonthMillis(tm, TIME_LOCATION) } func GetStartOfMonthMillisByTimeStamp(timeStamp int64) int64 { tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION) return GetStartOfMonthMillis(tm, TIME_LOCATION) } // 当前月份开始1号的时间戳 func GetDataStorePartitionByTimeStamp(timeStamp int64) string { tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION) return fmt.Sprintf("p%d", GetStartOfMonthMillis(tm, TIME_LOCATION)) } // 按东八区解析当前date, format,返回时间戳 func GetTimeStampsFromDateCST(date, format string) (int64, string) { date = PadDateStringZeros(date) resultTime, err := time.ParseInLocation(format, date, TIME_LOCATION) if err != nil { return 0, "Model.GetTimeStampsFromDateCST" } return GetMillisForTime(resultTime), "" } func GetDateCSTFromTimeStamp(timeStamp int64, format string) string { tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION) return tm.Format(format) } func GetDateCSTFromTimeStampStr(timeStamp string, format string) string { ts, err := strconv.ParseInt(timeStamp, 10, 64) if err != nil { return "NAN" } tm := time.Unix(0, ts*int64(1000*1000)).In(TIME_LOCATION) return tm.Format(format) } func WeekStart(year, week int) time.Time { // Start from the middle of the year: t := time.Date(year, 7, 1, 0, 0, 0, 0, TIME_LOCATION) // Roll back to Monday: if wd := t.Weekday(); wd == time.Sunday { t = t.AddDate(0, 0, -6) } else { t = t.AddDate(0, 0, -int(wd)+1) } // Difference in weeks: _, w := t.ISOWeek() t = t.AddDate(0, 0, (week-w)*7) return t } func WeekRange(year, week int) (start, end time.Time) { start = WeekStart(year, week) end = start.AddDate(0, 0, 6) return } func WeekRangeFormat(year, week int, format string) string { start, end := WeekRange(year, week) return start.Format(format) + "~" + end.Format(format) }
根据开始的时间戳返回一段时间的方法 ***
有时候我们会拿到一个时间点作为开始时间,而这个时间还有可能是个时间戳,这时候需要将时间戳转成时间(注意时区),而开始时间一般只有年月日,我们还需要做一些中间处理,我这里写了一个方法供大家参考:
var TIME_LOCATION_CST *time.Location // 时区默认为东八区! func init() { TIME_LOCATION_CST, _ = time.LoadLocation("Asia/Shanghai") } // 根据月与日是单位数还是双数返回不同的字符串 func getDateStr(month time.Month, day int) (monthStr, dayStr string) { if month < 10 { monthStr = fmt.Sprintf("0%d", month) } else { monthStr = fmt.Sprintf("%d", month) } if day < 10 { dayStr = fmt.Sprintf("0%d", day) } else { dayStr = fmt.Sprintf("%d", day) } return monthStr, dayStr } // 根据开始的时间戳获取开始与结束的日期 —— 默认东八区 func GetQueryTimeFromTimeStamp(StartTimeStamp int64) (queryStart, queryEnd time.Time, appError *AppError) { // TODO 注意:时间戳是 毫秒 级别的,所以下面要除以1000 // 1、开始时间 createTime := time.Unix(StartTimeStamp/1000, 0) // 先转字符串再转成年月日的格式 year, month, day := createTime.Date() // 如果月与日是单位数,需要在前面加上0!否则下面parse的时候可能会报错! monthStr, dayStr := getDateStr(month, day) startTimeStr := fmt.Sprintf("%d-%s-%s", year, monthStr, dayStr) if ret1, err := time.ParseInLocation("2006-01-02", startTimeStr, TIME_LOCATION_CST); err == nil { queryStart = ret1 } else { appError = NewAppError("GetQueryTimeFromTimeStamp", "time.ParseInLocation.error", err.Error(), nil) return ret1, ret1, appError } // 2、结束时间 TODO:注意这里返回 "明天" 的日期,返回后根据自己的实际情况做AddDate操作! nowTime := time.Now() endTime := nowTime.AddDate(0, 0, 1) year, month, day = endTime.Date() // 如果月与日是单位数,需要在前面加上0! monthStr, dayStr = getDateStr(month, day) endTImeStr := fmt.Sprintf("%d-%s-%s", year, monthStr, dayStr) if ret2, err := time.ParseInLocation("2006-01-02", endTImeStr, TIME_LOCATION_CST); err == nil { queryEnd = ret2 } else { appError = NewAppError("GetQueryTimeFromTimeStamp", "time.ParseInLocation.error", err.Error(), nil) return ret2, ret2, appError } return queryStart, queryEnd, nil }
根据开始与结束日期以5天为间隔返回每一个时间段的开始与结束日期 ***
还需要用到上面的 GetQueryTimeFromTimeStamp 方法,在此基础上获取这个时间段内以5天为间隔的开始与结束日期的组合:
func (s *SnapchatAccountHistoryAdCostTask) getTimeRange(createTimeStamp int64) ([]map[string]string, *model.AppError) { var timeRangeLst []map[string]string // 根据账号创建的时间戳获取开始与结束的日期 结束时间是"明天" startTime, endTime, err := model.GetQueryTimeFromTimeStamp(createTimeStamp) if err != nil { return nil, err } fmt.Printf("startTime: %v, endTime: %v \n", startTime, endTime) // startTime: 2020-08-21 00:00:00 +0800 CST, endTime: 2020-12-23 00:00:00 +0800 CST // 算一下两个时间差几天 timeDUration := endTime.Sub(startTime) fmt.Println("相差的小时数... ", timeDUration.Hours()) // 2976 // 小时数转天数 肯定是整数,因为开始与结束日期都是0点 days := int(int64(timeDUration.Hours()) / 24) fmt.Println("相差的天数... ", days) // 124 // 构建返回的列表 // 按照每5天一组组合数据 每一组的最后一个日期不算作统计的日期 for i := 0; i < days; i += 5 { currStartDate := startTime.AddDate(0, 0, i) currEndDate := startTime.AddDate(0, 0, i+5) // 如果 currEndDate超过了 "明天" 那最后的结束日期就用明天 if currEndDate.After(endTime) { currEndDate = endTime } currStartStr := currStartDate.Format("2006-01-02") currEndStr := currEndDate.Format("2006-01-02") currMap := map[string]string{ "startDate": currStartStr, "endDate": currEndStr, } timeRangeLst = append(timeRangeLst, currMap) } return timeRangeLst, nil }
返回的结果如下(注意返回的结果每个元素是字典):
timeRangeLst>>>
map[endDate:2020-11-04 startDate:2020-10-30]
map[endDate:2020-11-09 startDate:2020-11-04]
map[endDate:2020-11-14 startDate:2020-11-09] map[endDate:2020-11-19 startDate:2020-11-14] map[endDate:2020-11-24 startDate:2020-11-19] map[endDate:2020-11-29 startDate:2020-11-24] map[endDate:2020-12-04 startDate:2020-11-29] map[endDate:2020-12-09 startDate:2020-12-04] map[endDate:2020-12-14 startDate:2020-12-09]
map[endDate:2020-12-19 startDate:2020-12-14]
map[endDate:2020-12-23 startDate:2020-12-19]]
根据开始时间戳获取一个开始到结束日期的时间字符串切片 ***
还需要用到上面的 GetQueryTimeFromTimeStamp 方法,写一个死循环即可实现:
// 根据账号的创建时间戳得到一个包含开始到结束日期的字符串切片 func (f *FacebookAccountHistoryAdCostTask) getDateSlice(createTimeStamp int64) ([]string, *model.AppError) { var dateSlice []string // 根据账号的创建时间获取开始与结束日期的0点 startTime, endTime, err := model.GetQueryTimeFromTimeStamp(createTimeStamp) if err != nil { return dateSlice, err } // 这里获取到的endTime是"明天",Facebook渠道的endTime是今天 endTime = endTime.AddDate(0, 0, -1) mlog.Info(fmt.Sprintf("startTime: %v \n, endTime: %v \n", startTime, endTime)) // startTime: 2019-05-16 00:00:00 +0800 CST , endTime: 2020-12-24 00:00:00 +0800 CST // 得到一个从开始时间到结束时间字符串的列表 for { if startTime.After(endTime){ break } startString := startTime.Format("2006-01-02") dateSlice = append(dateSlice, startString) startTime = startTime.AddDate(0, 0, 1) } return dateSlice, nil }
结果如下:
[2020-11-13
2020-11-14 2020-11-15 2020-11-16 2020-11-17 2020-11-18 2020-11-19 2020-11-20 2020-11-21 2020-11-22 2020-11-23 2020-11-24 2020-11-25 2020-11-26 2020-11-27 2020-11-28 2020-11-29 2020-11-30 2020-12-01 2020-12-02 2020-12-03 2020-12-04 2020-12-05 2020-12-06 2020-12-07 2020-12-08 2020-12-09 2020-12-10 2020-12-11 2020-12-12 2020-12-13 2020-12-14 2020-12-15 2020-12-16 2020-12-17 2020-12-18 2020-12-19 2020-12-20 2020-12-21 2020-12-22 2020-12-23
2020-12-24]
根据上一步的时间切片获取分割的时间切片
根据开始与结束日期获取一个开始到结束日期的时间切片:
package t9 import ( "fmt" "testing" "time" ) var TIME_LOCATION_CST, _ = time.LoadLocation("Asia/Shanghai") // 根据开始与结束日期获取一个包含开始到结束日期字符串的切片 func GetDateSlice(startTime, endTime time.Time) (dateSlice []string) { for { if startTime.After(endTime) { break } startStr := startTime.Format("2006-01-02") dateSlice = append(dateSlice, startStr) startTime = startTime.AddDate(0, 0, 1) } return } func TestRange(t *testing.T) { startTime := time.Date(2020, 01, 01, 00, 00, 00, 00, TIME_LOCATION_CST) endTime := time.Date(2020, 02, 03, 00, 00, 00, 00, TIME_LOCATION_CST) timeRangeLst := GetDateSlice(startTime, endTime) fmt.Println("timeRangeLst>>> ", timeRangeLst) /* [2020-01-01 2020-01-02 2020-01-03 2020-01-04 2020-01-05 2020-01-06 2020-01-07 2020-01-08 2020-01-09 2020-01-10 2020-01-11 2020-01-12 2020-01-13 2020-01-14 2020-01-15 2020-01-16 2020-01-17 2020-01-18 2020-01-19 2020-01-20 2020-01-21 2020-01-22 2020-01-23 2020-01-24 2020-01-25 2020-01-26 2020-01-27 2020-01-28 2020-01-29 2020-01-30 2020-01-31 2020-02-01 2020-02-02 2020-02-03] */
把它里面的字符串按照每4个一组整理成切片套切片的格式:
// 根据存时间字符串的切片获取一个按照每4天分组的新切片 func getNewDateSlice(dateSlice []string) [][]string { var retSlice [][]string length := len(dateSlice) // 分组 for i := 0; i < length; i += 4 { var currSlice []string if i+4 > length{ currSlice = dateSlice[i : length] }else{ currSlice = dateSlice[i : i+4] } retSlice = append(retSlice, currSlice) } return retSlice }
结果如下:
[[2020-01-01 2020-01-02 2020-01-03 2020-01-04]
[2020-01-05 2020-01-06 2020-01-07 2020-01-08]
[2020-01-09 2020-01-10 2020-01-11 2020-01-12]
[2020-01-13 2020-01-14 2020-01-15 2020-01-16]
[2020-01-17 2020-01-18 2020-01-19 2020-01-20]
[2020-01-21 2020-01-22 2020-01-23 2020-01-24]
[2020-01-25 2020-01-26 2020-01-27 2020-01-28]
[2020-01-29 2020-01-30 2020-01-31 2020-02-01]
[2020-02-02 2020-02-03]]
下一个时间的开始是上一个时间的结尾的切片嵌套的构建方法:
package t9 import ( "fmt" "testing" "time" ) var TIME_LOCATION_CST, _ = time.LoadLocation("Asia/Shanghai") // 根据开始与结束日期获取一个包含开始到结束日期字符串的切片 func GetDateSlice(startTime, endTime time.Time) (dateSlice []string) { for { if startTime.After(endTime) { break } startStr := startTime.Format("2006-01-02") dateSlice = append(dateSlice, startStr) startTime = startTime.AddDate(0, 0, 1) } return } // 整理下dateSlice的格式:下一个列表的第一个元素是上一个列表最后一个元素 func MinInt(a, b int) int { if a < b { return a } return b } func getNewDateSlice(dateSlice []string) (newDateSlice [][]string) { for i := 1; i < len(dateSlice); i += 5 { currSlice := dateSlice[i-1 : MinInt(i+5, len(dateSlice))] newDateSlice = append(newDateSlice, currSlice) } return } func TestRange(t *testing.T) { startTime := time.Date(2020, 01, 01, 00, 00, 00, 00, TIME_LOCATION_CST) endTime := time.Date(2020, 02, 03, 00, 00, 00, 00, TIME_LOCATION_CST) timeRangeLst := GetDateSlice(startTime, endTime) fmt.Println("timeRangeLst>>> ", timeRangeLst) /* [2020-01-01 2020-01-02 2020-01-03 2020-01-04 2020-01-05 2020-01-06 2020-01-07 2020-01-08 2020-01-09 2020-01-10 2020-01-11 2020-01-12 2020-01-13 2020-01-14 2020-01-15 2020-01-16 2020-01-17 2020-01-18 2020-01-19 2020-01-20 2020-01-21 2020-01-22 2020-01-23 2020-01-24 2020-01-25 2020-01-26 2020-01-27 2020-01-28 2020-01-29 2020-01-30 2020-01-31 2020-02-01 2020-02-02 2020-02-03] */ ret := getNewDateSlice(timeRangeLst) fmt.Println("ret>>> ", ret) /* [[2020-01-01 2020-01-02 2020-01-03 2020-01-04 2020-01-05 2020-01-06] [2020-01-06 2020-01-07 2020-01-08 2020-01-09 2020-01-10 2020-01-11] [2020-01-11 2020-01-12 2020-01-13 2020-01-14 2020-01-15 2020-01-16] [2020-01-16 2020-01-17 2020-01-18 2020-01-19 2020-01-20 2020-01-21] [2020-01-21 2020-01-22 2020-01-23 2020-01-24 2020-01-25 2020-01-26] [2020-01-26 2020-01-27 2020-01-28 2020-01-29 2020-01-30 2020-01-31] [2020-01-31 2020-02-01 2020-02-02 2020-02-03]] */ }
~~~