golang时间与时区相关操作总结

前言

任何语言处理时间的功能都是最基础也是平时十分常用的,另外需要注意任何脱离时区的时间都是没有任何意义的!

这里总结一下笔者在这一个多月写go项目用到的、收集到的一些好用的处理时间的方法以及时间处理的一些基础知识点。

golang时间操作基础 ***

go关于时间操作的基础我这边自己做了一下笔记:

golang时间操作基础

实际中自己总结的一些方法 ***

根据开始的时间戳返回一段时间的方法 ***

有时候我们会拿到一个时间点作为开始时间,而这个时间还有可能是个时间戳,这时候需要将时间戳转成时间(注意时区),而开始时间一般只有年月日,我们还需要做一些中间处理,我这里写了一个方法供大家参考:

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 
", 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 
, endTime: %v 
", 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]]

    */

}

根据时间戳获取当天0点的字符串—用默认时区

有时候我们会拿到一个时间戳,需要做一下业务的处理。需要注意的是:时间戳是不带时区的!在时间戳转时间时默认使用的是当地的时区!


   // 毫秒级别的时间戳 ———— 13位
   accountCreateTimeStamp := 1597978828455 // 毫秒时间戳转时间(这个方法默认用当地的时区转 —— 中国的话就是东八区时间+0800) createTime := time.Unix(int64(accountCreateTimeStamp / 1000), 0) fmt.Println("createTime>>> ",createTime) // 2020-08-21 11:00:28 +0800 CST —— 时间类型 // 获取年月日拼接成字符串 year, month, day := createTime.Date()// 构建0点时间的字符串 startTimeStr := fmt.Sprintf("%d-%d-%dT00:00:00 +0800 CST", year, month, day) fmt.Println("startTimeStr>>> ", startTimeStr) // 2020-8-21T00:00:00 +0800 CST —— 字符串类型

根据时间戳返回时间字符串的方法

// 根据账号的创建时间(时间戳)获取查询的开始与结束的端点 ———— 返回字符串
func (s *SnapchatAccountHistoryAdCostTask) getQueryTimeFromAccountCreateTimeStamp(accountCreateTimeStamp int64) (string, string){
    var queryStart, queryEnd string
    // 开始 TODO 注意这里的时间戳是 毫秒 级别的,所以下面要除以1000
    createTime := time.Unix(int64(accountCreateTimeStamp / 1000), 0)
    // fmt.Println("createTime>>> ",createTime) // 2020-08-21 11:00:28 +0800 CST
    year, month, day := createTime.Date()
    queryStart = fmt.Sprintf("%d-%d-%d",year, month, day)
    // 结束时间是 "明天"
    nowTime := time.Now()
    endTime := nowTime.AddDate(0,0,1)
    // fmt.Println("endTime>>> ", endTime)
    year, month, day = endTime.Date()
    queryEnd = fmt.Sprintf("%d-%d-%d", year, month, day)
    return queryStart, queryEnd
}

封装好的一些方法 ***

时间字符串(带时区)转时间/时间戳 ***

转时间戳的思路是:先将时间字符串转为时间,然后将时间转为时间戳。

转换过程代码简介

    dateStr := "2020-09-01"
    timeStr := fmt.Sprintf("%sT00:00:00+0800", dateStr)
// 先转时间 timeRet, err :
= time.ParseInLocation("2006-01-02T15:04:05+0800", timeStr, model.TIME_LOCATION_CST) if err != nil { f.Error("转时间戳异常!", mlog.Err(err)) } fmt.Println("timeRet>>> ", timeRet) // 再转时间戳 毫秒 timeStamp := timeRet.UnixNano() / int64(time.Millisecond) fmt.Println("tiemStamp>>> ", timeStamp)

虽然时间戳没有时区的概念,但是在将字符串转为时间的时候一定要注意时区!

封装好的方法:

var TIME_LOCATION_CST *time.Location

// 初始化时默认设置为 Asia/Shanghai
func init() {
    TIME_LOCATION_CST, _ = time.LoadLocation("Asia/Shanghai")
}

// 各种可能出现的时间格式(实际中如果有其他的话,加进去,然后下面遍历的时候也记得加上)
const (
    BINano      = "2006-01-02 15:04:05.000000000"
    BIMicro     = "2006-01-02 15:04:05.000000"
    BIMil       = "2006-01-02 15:04:05.000"
    BISec       = "2006-01-02 15:04:05"
    BICST       = "2006-01-02 15:04:05 +0800 CST"
    BIUTC       = "2006-01-02 15:04:05 +0000 UTC"
    BIDate      = "2006-01-02"
    BITime      = "15:04:05"
    FBTIME      = "2006-01-02T15:04:05+0800"
    APPTIME     = "2006-01-02T15:04:05.000"
    TWITTERTIME = "2006-01-02T15:04:05Z"
)

//———— 先将字符串转为时间格式,然后再转成时间戳
// 时间字符串转时间戳(毫秒) 默认使用 Asia/Shanghai 时区
// 默认为东8区的话用这个
func TimeStr2Timestamp(str string) (int64, error) {
    return TimeStr2TimestampBasic(str, "", TIME_LOCATION_CST)
}

// 时间字符串转时间戳(毫秒) 
//  如果想加入时间格式化与时区,用这个方法!
func TimeStr2TimestampBasic(str string, format string, loc *time.Location) (int64, error) {
    t, err := TimeStr2TimeBasic(str, format, loc)
    if err != nil {
        return -1., err
    }
    return (t.UnixNano()) / 1e6, nil // 时间为毫秒级别,这里是1e6
}

// 将时间字符串转为时间类型(如果上面的时间字符串格式还少,将新的格式直接加进去就好了)
// 时间字符串转时间戳的话,直接使用这个方法就好了!
func TimeStr2TimeBasic(value string, resultFormat string, resultLoc *time.Location) (time.Time, error) { /** - params value: 转换内容字符串 resultFormat: 结果时间格式 resultLoc: 结果时区 */ resultLoc = getLocationDefault(resultLoc) useFormat := []string{ // 可能的转换格式 BINano, BIMicro, BIMil, BISec, BICST, BIUTC, BIDate, BITime, FBTIME, APPTIME, TWITTERTIME, time.RFC3339, time.RFC3339Nano, } var t time.Time for _, usef := range useFormat { tt, error := time.ParseInLocation(usef, value, resultLoc) t = tt if error != nil { continue } break } if t == getTimeDefault(resultLoc) { return t, errors.New("时间字符串格式错误") } if resultFormat == "" { // 时间为毫秒级别 resultFormat = "2006-01-02 15:04:05.000" } st := t.Format(resultFormat) fixedt, _ := time.ParseInLocation(resultFormat, st, resultLoc) return fixedt, nil } // 获取time默认值 func getTimeDefault(loc *time.Location) time.Time { loc = getLocationDefault(loc) t, _ := time.ParseInLocation("2006-01-02 15:04:05", "", loc) return t } func getLocationDefault(loc *time.Location) *time.Location { if loc == nil { loc, _ = time.LoadLocation("Local") } return loc }

具体使用:

  timeStr := "2020-12-19T15:26:47+0800"
    
    // 时间字符串转时间戳 ———— 注意下面2个结果:不同时区得到的时间戳是不一样的!因为中间转的时间不一样了!

    // 1 默认使用当地的时区
    timtStamp, _ := TimeStr2Timestamp(timeStr)
    fmt.Println(timtStamp) // 1608362807000

    // 2 指定时区
    timeZone := "America/New_York"
    // 获取location
    location, _ := time.LoadLocation(timeZone)
    timeStamp, _ := TimeStr2TimestampBasic(timeStr,"",location)
    fmt.Println(timeStamp) // 1608409607000

封装的时间处理的包

1

package time_demo

import (
    "errors"
    "github.com/jinzhu/now"
    "time"
)

/** 时间-时间戳-时间字符串互相转换
- time: auto timezone associated | 时间必关联时区属性
- str<=>time时,loc或者format不改变时间展示值,仅改变时区或偏移 | timezone associated
- timestamp: means seconds/nanoseconds of currenttime since January 1, 1970 UTC.
*/

var TIME_LOCATION_CST *time.Location

// 初始化时默认设置为 Asia/Shanghai
func init() {
    TIME_LOCATION_CST, _ = time.LoadLocation("Asia/Shanghai")
}

// 各种可能出现的时间格式
const (
    BINano      = "2006-01-02 15:04:05.000000000"
    BIMicro     = "2006-01-02 15:04:05.000000"
    BIMil       = "2006-01-02 15:04:05.000"
    BISec       = "2006-01-02 15:04:05"
    BICST       = "2006-01-02 15:04:05 +0800 CST"
    BIUTC       = "2006-01-02 15:04:05 +0000 UTC"
    BIDate      = "2006-01-02"
    BITime      = "15:04:05"
    FBTIME      = "2006-01-02T15:04:05+0800"
    APPTIME     = "2006-01-02T15:04:05.000"
    TWITTERTIME = "2006-01-02T15:04:05Z"
)

// 时间字符串转时间戳(毫秒) ———— 先将字符串转为时间格式,然后再转成时间戳 默认使用 Asia/Shanghai 时区
func TimeStr2Timestamp(str string) (int64, error) {
    return TimeStr2TimestampBasic(str, "", TIME_LOCATION_CST)
}

// 时间字符串转时间戳(毫秒) ———— 如果想加入时间格式化与时区,用这个方法!
func TimeStr2TimestampBasic(str string, format string, loc *time.Location) (int64, error) {
    t, err := TimeStr2TimeBasic(str, format, loc)
    if err != nil {
        return -1., err
    }
    return (t.UnixNano()) / 1e6, nil // 时间为毫秒级别,这里是1e6
}

// 将时间字符串转为时间类型(如果上面的时间字符串格式还少,将新的格式直接加进去就好了)
func TimeStr2TimeBasic(value string, resultFormat string, resultLoc *time.Location) (time.Time, error) {
    /**
    - params
        value:             转换内容字符串
        resultFormat:    结果时间格式
        resultLoc:        结果时区
    */
    resultLoc = getLocationDefault(resultLoc)
    useFormat := []string{ // 可能的转换格式
        BINano, BIMicro, BIMil, BISec, BICST, BIUTC, BIDate, BITime, FBTIME, APPTIME, TWITTERTIME,
        time.RFC3339,
        time.RFC3339Nano,
    }
    var t time.Time
    for _, usef := range useFormat {
        tt, error := time.ParseInLocation(usef, value, resultLoc)
        t = tt
        if error != nil {
            continue
        }
        break
    }
    if t == getTimeDefault(resultLoc) {
        return t, errors.New("时间字符串格式错误")
    }

    if resultFormat == "" {
        // 时间为毫秒级别
        resultFormat = "2006-01-02 15:04:05.000"
    }
    st := t.Format(resultFormat)
    fixedt, _ := time.ParseInLocation(resultFormat, st, resultLoc)

    return fixedt, nil
}

// 获取time默认值
func getTimeDefault(loc *time.Location) time.Time {
    loc = getLocationDefault(loc)
    t, _ := time.ParseInLocation("2006-01-02 15:04:05", "", loc)
    return t
}

func getLocationDefault(loc *time.Location) *time.Location {
    if loc == nil {
        loc, _ = time.LoadLocation("Local")
    }
    return loc
}

/* timestamp <--> time */
// Timestamp2Time 时间戳转时间
func Timestamp2Time(stamp int64, nsec int64) time.Time {
    return time.Unix(stamp, nsec)
}

// Timestamp2TimeNano 时间戳转时间,纳秒
func Timestamp2TimeNano(stamp int64) time.Time {
    return Timestamp2Time(0, stamp)
}

func Timestamp2TimeMil(stamp int64) time.Time {
    return Timestamp2Time(0, stamp*1e6)
}

func Timestamp2TimeSec(stamp int64) time.Time {
    return Timestamp2Time(stamp, 0)
}

/* Time2Timestamp 时间转时间戳 */
func Time2Timestamp(t time.Time, nsec int64) int64 {
    if nsec >= 0 {
        return t.Unix()
    }
    return t.UnixNano()
}

func Time2TimestampSec(t time.Time) int64 {
    return Time2Timestamp(t, 0)
}

func Time2TimestampMil(t time.Time) int64 {
    return Time2TimestampNano(t) / 1e6
}

func Time2TimestampNano(t time.Time) int64 {
    return Time2Timestamp(t, -1)
}

/* TimeStr <--> time */
// 字符串转时间 ———— 注意字符串的格式得在定义的那里面
func TimeStr2Time(str string) (time.Time, error) {
    return TimeStr2TimeBasic(str, "", nil)
}

/* Time <--> time */
// Time2TimeBasic 特定格式化的时间
func Time2TimeBasic(t time.Time, format string, loc *time.Location) (time.Time, error) {
    loc = getLocationDefault(loc)
    if format == "" {
        return t, nil
    }
    strT := t.Format(format)

    resT, err := TimeStr2TimeBasic(strT, format, loc)
    if err != nil {
        return t, err
    }
    return resT, nil
}

// Time2Time 特定格式化的时间
func Time2Time(t time.Time, format string) (time.Time, error) {

    return Time2TimeBasic(t, format, nil)
}

/* 时间计算 */
// TimePlus 时间加减
func TimePlus(t time.Time, value string) time.Time {
    // next 增加"[x]d"(天数)格式支持
    m, _ := time.ParseDuration(value)
    tCal := t.Add(m)
    return tCal
}

func TimeSubtraction(t1 time.Time, t2 time.Time) time.Duration {

    return t1.Sub(t2)
}

func TimeSubtractionDay(t1 time.Time, t2 time.Time) int64 {

    timeD := TimeSubtraction(t1, t2)
    return (int64)(timeD/time.Second) / 86400
}

func TimeStrSubtraction(str1 string, str2 string) time.Duration {

    t1, _ := TimeStr2Time(str1)
    t2, _ := TimeStr2Time(str2)

    return TimeSubtraction(t1, t2)
}

func TimeStrSubtractionDay(str1 string, str2 string) int64 {

    t1, _ := TimeStr2Time(str1)
    t2, _ := TimeStr2Time(str2)

    return TimeSubtractionDay(t1, t2)
}

// AddDate 增加年份 月份
func AddDate(t time.Time, years int, months int) time.Time {
    year, month, day := t.Date()
    hour, min, sec := t.Clock()

    // firstDayOfMonthAfterAddDate: years 年,months 月后的 那个月份的1号
    firstDayOfMonthAfterAddDate := time.Date(year+years, month+time.Month(months), 1,
        hour, min, sec, t.Nanosecond(), t.Location())
    // firstDayOfMonthAfterAddDate 月份的最后一天
    lastDay := now.New(firstDayOfMonthAfterAddDate).EndOfMonth().Day()

    // 如果 t 的天 > lastDay,则设置为lastDay
    // 如:t 为 2020-03-31 12:00:00 +0800,增加1个月,为4月31号
    // 但是4月没有31号,则设置为4月最后一天lastDay(30号)
    if day > lastDay {
        day = lastDay
    }

    return time.Date(year+years, month+time.Month(months), day,
        hour, min, sec, t.Nanosecond(), t.Location())
}
time_util.go

2 

package time_demo

import (
    "fmt"
    "strconv"
    "strings"
    "time"
)

var TIME_LOCATION_CST *time.Location

// 初始化时默认设置为 Asia/Shanghai
func init() {
    TIME_LOCATION_CST, _ = time.LoadLocation("Asia/Shanghai")
}


// 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)
}

func GetStartOfNextMonthMillisByTimeStamp(timeStamp int64) int64 {
    tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION_CST)
    return GetStartOfNextMonthMillis(tm, TIME_LOCATION_CST)
}

func GetStartOfMonthMillisByTimeStamp(timeStamp int64) int64 {
    tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION_CST)
    return GetStartOfMonthMillis(tm, TIME_LOCATION_CST)
}

func GetDataStorePartitionByTimeStamp(timeStamp int64) string {
    tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION_CST)
    return fmt.Sprintf("p%d", GetStartOfMonthMillis(tm, TIME_LOCATION_CST))
}

// 按东八区解析当前date, format,返回时间戳
func GetTimeStampsFromDateCST(date, format string) (int64, string) {
    date = PadDateStringZeros(date)
    resultTime, err := time.ParseInLocation(format, date, TIME_LOCATION_CST)
    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_CST)
    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_CST)
    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_CST)

    // 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)
}
time_util2.go

获取这个月/上个月/下个月1号0点的时间戳

package t9

import (
    "fmt"
    "testing"
    "time"
)

var TIME_LOCATION_CST *time.Location

func init() {
    // 时区默认取 东八区
    TIME_LOCATION_CST, _ = time.LoadLocation("Asia/Shanghai")
}

// 当前月份开始1号的时间戳
func GetDataStorePartitionByTimeStamp(timeStamp int64) int64 {
    tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION_CST)
    // return fmt.Sprintf("p%d", GetStartOfMonthMillis(tm, TIME_LOCATION_CST))
    return GetStartOfMonthMillis(tm, TIME_LOCATION_CST)
}

// 下一个月开始1号的时间戳
func GetStartOfNextMonthMillisByTimeStamp(timeStamp int64) int64 {
    tm := time.Unix(0, timeStamp*int64(1000*1000)).In(TIME_LOCATION_CST)
    return GetStartOfNextMonthMillis(tm, TIME_LOCATION_CST)
}

// 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)
}

// 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)
}

// 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)
}

// GetMillis is a convenience method to get milliseconds since epoch.
func GetMillis() int64 {
    return time.Now().UnixNano() / int64(time.Millisecond)
}

func TestTTime(t *testing.T) {

    // 当前时间的时间戳 —— 毫秒级别
    now := GetMillis()
    fmt.Println("now: ", now) // 1608711786893  (2020/12/23 16:23:6)

    // 获取当前月份的开始时间的时间戳 —— 毫秒级别
    thisMonthStart := GetDataStorePartitionByTimeStamp(now)
    fmt.Println("thisMOnthStart: ", thisMonthStart) // 1606752000000  (2020/12/1 0:0:0)

    // 获取下一个月的开始时间的时间戳 —— 毫秒级别
    next := GetStartOfNextMonthMillisByTimeStamp(now)
    fmt.Println("next>> ", next) // 1609430400000  (2021/1/1 0:0:0)

    // 获取上一个月的开始时间的时间戳 —— 毫秒级别
    // 其实就是当前月份开始1号0点的时间戳减去一个数得到上个月的一个时间,再调用获取当前月份开始时间时间戳的函数即可
    beforeStamp := (thisMonthStart - 10)
    before := GetDataStorePartitionByTimeStamp(beforeStamp)
    fmt.Println("上个月开始时间戳: ", before) // 1604160000000  (2020/11/1 0:0:0)
}
ttime_test.go

~~~

原文地址:https://www.cnblogs.com/paulwhw/p/14154137.html