Go 去除时间时分秒:3种实用方法
最近做功能时,遇到个常见需求:把接口返回的带时分秒的时间(比如 2024-05-20 14:30:25)处理成只保留日期的格式。
Go 语言的 time 包功能很强,但时间处理细节不少,比如时区、格式匹配这些坑很容易踩。
本篇整理记录分享我用的三种可用方法,省得大家再走弯路。
Go 时间的结构认知
先明确一个关键点:Go 里的 time.Time 类型是包含日期、时间、时区等完整信息的结构体。
我们说的“去除时分秒”,本质是把时间的时、分、秒、纳秒字段置为0,或者通过格式化只提取日期部分。
不管用哪种方法,都要注意时区问题——如果不指定时区,Go 会用系统默认时区,可能导致跨环境时出现日期偏差(比如服务器是 UTC 时区,本地是东八区,处理后日期可能差一天)。
方法一:用 Truncate 函数
Go 1.12 及以上版本提供了 Truncate 方法,能按指定的时间单位截断时间,比如截断到“天”,就会自动把时分秒置为0。
这是最简洁高效的方法,优先推荐。
// 方法一:Truncate 截断到天(去除时分秒)
func removeTimeWithTruncate(t time.Time) time.Time {
// 按天截断,参数为 time.Hour * 24
return t.Truncate(24 * time.Hour)
}
// 在 main 函数中调用
func main() {
// 省略测试时间定义...
result1 := removeTimeWithTruncate(testTime)
fmt.Println("Truncate 处理后:", result1) // 输出:2024-05-20 00:00:00 +0800 CST
}注意事项:Truncate 的参数是“时间间隔”,这里用 24*time.Hour 表示按天截断。
如果参数传 time.Hour 就是截断到小时,传 time.Minute 就是截断到分钟,灵活度很高。
方法二:Format + Parse 格式化解析
如果项目需要兼容 Go 1.12 以下版本,Truncate 方法不可用,
就可以用“先格式化再解析”的思路:把时间按“年月日”格式转成字符串,再把字符串解析回 time.Time 类型,解析后时分秒会默认置为0。
// 方法二:Format 转字符串后再 Parse 解析
func removeTimeWithFormatParse(t time.Time) (time.Time, error) {
// 第一步:按年月日格式转成字符串
dateStr := t.Format("2006-01-02")
// 第二步:把字符串解析回 time.Time 类型,指定时区
return time.ParseInLocation("2006-01-02", dateStr, t.Location())
}
// 在 main 函数中调用
func main() {
// 省略测试时间定义...
result2, err := removeTimeWithFormatParse(testTime)
if err != nil {
fmt.Printf("Format+Parse 处理失败:%v\n", err)
return
}
fmt.Println("Format+Parse 处理后:", result2) // 输出:2024-05-20 00:00:00 +0800 CST
}Go 的时间格式化必须用“2006-01-02 15:04:05”这个固定的“参考时间”,不能用“yyyy-MM-dd”这种通用格式!很多人在这里踩坑。
另外解析时必须指定和原时间一致的时区,否则会出现时区偏差。
方法三:通过时间戳计算(时间戳场景)
如果拿到的是时间戳(比如从数据库或接口获取的 Unix 时间戳),可以先转成 time.Time 类型,再通过计算一天的秒数(86400秒)来剔除时分秒对应的时间戳,最后转回时间类型。
// 方法三:时间戳计算(假设输入为 Unix 时间戳,单位秒)
func removeTimeWithTimestamp(ts int64, loc *time.Location) time.Time {
// 一天的秒数:24*60*60=86400
daySeconds := int64(86400)
// 计算当天0点的时间戳:原时间戳 % 一天秒数,再用原时间戳减去余数
zeroTs := ts - (ts % daySeconds)
// 转成 time.Time 类型并指定时区
return time.Unix(zeroTs, 0).In(loc)
}
// 在 main 函数中调用
func main() {
// 省略测试时间定义...
// 先把测试时间转成 Unix 时间戳(单位秒)
ts := testTime.Unix()
result3 := removeTimeWithTimestamp(ts, testTime.Location())
fmt.Println("时间戳处理后:", result3) // 输出:2024-05-20 00:00:00 +0800 CST
}适用场景:适合从外部获取时间戳后直接处理的场景,比如日志分析、数据批量处理时,不用先转成时间类型再处理,效率更高。
对比建议
| 方法 | Go 版本要求 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Truncate 函数 | 1.12+ | 简洁高效,一行代码,支持任意时间单位截断 | 不兼容旧版本 | Go 1.12+ 版本的常规场景 |
| Format + Parse | 全版本 | 兼容性好,无版本限制 | 需两次转换,效率略低,易踩格式坑 | 旧版本项目,或需要获取日期字符串的场景 |
| 时间戳计算 | 全版本 | 处理时间戳时效率高,无需转时间类型 | 仅适用于时间戳输入场景 | 日志分析、批量数据处理等时间戳场景 |
常见问题
Q1. 处理后日期差一天(时区问题)
问题:本地测试时处理正常,部署到服务器后,时间变成前一天(比如本地是 2024-05-20,服务器是 2024-05-19)。
原因:本地是东八区(CST),服务器是 UTC 时区,处理时没有指定时区,Go 用了服务器默认的 UTC 时区,导致东八区的“14:30”在 UTC 时区是“06:30”,截断后变成前一天。
解决:所有时间处理都明确指定时区,不要依赖系统默认时区。
推荐用 time.FixedZone("CST", 8*3600) 明确指定东八区,或用 time.LoadLocation("Asia/Shanghai") 加载时区(需要服务器有对应的时区数据库)。
Q2. Truncate 方法在旧版本 Go 中报错
问题:在 Go 1.10 版本中调用 Truncate 方法,提示“undefined: time.Time.Truncate”。
原因:Truncate 方法是 Go 1.12 版本才新增的,旧版本不支持。
解决:改用“Format + Parse”的方法二,或升级 Go 版本(推荐升级,旧版本已停止维护)。
Q3. 格式化解析时返回“parsing time xxx: cannot parse ”错误
问题:用方法二时,提示类似“parsing time “2024-05-20”: cannot parse "" as “2006"” 的错误。
原因:格式化和解析时用的格式字符串不匹配,比如格式化用了“2006/01/02”,解析时却用了“2006-01-02”;或者格式字符串写错了(比如把“2006”写成“2024”)。
解决:确保格式化(Format)和解析(ParseInLocation)用的格式字符串完全一致,且严格遵循“2006-01-02 15:04:05”的参考时间规则。
Q4. 时间戳处理后出现“1970-01-01”(无效时间戳)
问题:用方法三处理后,时间变成“1970-01-01 08:00:00 +0800 CST”。
原因:输入的时间戳是无效值(比如 0 或负数),或者单位错误(比如把毫秒级时间戳当成秒级处理,导致数值过大溢出)。
解决:先校验时间戳的有效性,比如判断秒级时间戳是否在“1970-01-01”到未来合理时间范围内;如果是毫秒级时间戳,需要先除以 1000 转成秒级(或在 time.Unix 时用毫秒参数:time.Unix(0, ts*1e6))。
总结
Go 去除时间时分秒的核心是“将时分秒字段置为0”,3种方法各有侧重:
-
日常开发优先用 Truncate 方法,简洁又高效;
-
旧版本项目用 Format + Parse 兼容;
-
处理时间戳时直接用时间戳计算更省心。
最关键的一点是始终指定时区,这是避免跨环境时间偏差的核心。
如果在实际使用中遇到其他问题,欢迎在评论区交流,我会及时回复补充。
版权声明
未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!
本文原文链接: https://fiveyoboy.com/articles/go-remove-time-hour-minute-second/
备用原文链接: https://blog.fiveyoboy.com/articles/go-remove-time-hour-minute-second/