Go time.Time.IsZero 为什么不等于 Unix 时间戳为 0
来源:17golang原创
时间:2026-10-23 01:03:33 278浏览 收藏
我排查 Go 时间字段时,最容易被一个数字带偏:看到 Unix() == 0,就把它当成“没有时间”。但 time.Time.IsZero() 和 Unix 时间戳判断回答的是两个问题:前者判断 Go 的零值,后者判断某个瞬间是否落在 Unix epoch。
结论先说清楚:time.Time{} 表示公元 1 年 1 月 1 日 00:00:00 UTC;Unix 时间戳 0 表示 1970 年 1 月 1 日 00:00:00 UTC。两者不是同一个瞬间,所以不能互相替代。
判断“字段有没有赋值”用IsZero或指针是否为 nil;判断“协议值是不是 Unix epoch”才用Unix() == 0。
先拆开两个时间坐标
Go 的零值来自类型本身:声明一个没有初始化的 time.Time,得到的是公元 1 年的零时刻。Unix 时间则从 1970 年开始计秒,二者相差很远。

package main
import (
"fmt"
"time"
)
func main() {
var zero time.Time
epoch := time.Unix(0, 0).UTC()
// IsZero 判断是否为公元 1 年的 Go 零值,而不是判断 Unix 秒数。
fmt.Println(zero.IsZero()) // true
fmt.Println(epoch.IsZero()) // false
// Unix 判断距 1970-01-01 00:00:00 UTC 的秒数。
fmt.Println(zero.Unix()) // -62135596800
fmt.Println(epoch.Unix()) // 0
}
这里把 epoch 转成 UTC 只是为了让打印结果的时区表达更直观;Unix 的秒数定义本身就是相对于 UTC 的,时间值携带的 Location 不会改变同一瞬间的 Unix 秒数。
字段没有赋值时应该用 IsZero
在配置对象、数据库模型或接口响应里,“没有提供时间”和“明确提供了 1970 年 1 月 1 日”可能是两种业务状态。值类型字段无法表达缺失时,可以用 IsZero 识别未初始化的值;如果接口需要区分缺失、显式 null 和具体时间,优先把字段设计成 *time.Time。

package main
import (
"fmt"
"time"
)
type Request struct {
// 指针让 nil 表达“调用方没有提供”,epoch 则可以作为合法时间传入。
PublishedAt *time.Time `json:"published_at"`
}
func describe(r Request) string {
// 先判断缺失,再判断具体时间,避免把 epoch 误判成空值。
if r.PublishedAt == nil {
return "未提供时间"
}
if r.PublishedAt.IsZero() {
return "提供了 Go 零值"
}
if r.PublishedAt.Unix() == 0 {
return "明确提供了 Unix epoch"
}
return "提供了其他时间"
}
func main() {
epoch := time.Unix(0, 0).UTC()
fmt.Println(describe(Request{}))
fmt.Println(describe(Request{PublishedAt: &epoch}))
}
如果业务模型已经是非指针的 time.Time,可以把 value.IsZero() 作为“是否写入过”的判断,但要在接口协议中明确:零值是否允许被当成缺失。不要为了追求统一,把所有时间都先转 Unix 秒再判断。
Unix() == 0 只适合协议和存储语义
当外部协议明确规定 0 代表 epoch,或者数据库字段约定用 0 表示一个特殊时间点时,Unix() == 0 才是准确判断。它不负责判断 Go 值有没有初始化,也不负责判断 JSON 字段是否缺失。
func isEpoch(t time.Time) bool {
// 只有业务协议把 0 定义为 epoch 时,才使用 Unix 秒值比较。
return t.Unix() == 0
}
func isUnset(t time.Time) bool {
// 这里判断的是 Go 类型的零值,与 Unix epoch 是两条语义路径。
return t.IsZero()
}
反过来,如果数据库读取出来的整型时间戳是 0,先按数据库协议解释它,再决定是否转换成 time.Time。如果 0 代表“未设置”,转换后也不要把它伪装成普通的 epoch 时间,否则业务层会丢失原始状态。
比较时要把“瞬间”和“字段状态”分开
IsZero 是状态判断,Unix 是坐标转换。两者之外还有一个常见边界:比较两个时间是否是同一瞬间时使用 Equal,不要把 Location 是否相同误当成瞬间不同。时区只影响显示和日历解释,不能把零值变成 epoch。
func sameInstant(left, right time.Time) bool {
// Equal 比较时间瞬间,适合跨时区表示同一时刻的场景。
return left.Equal(right)
}
func validPublishedAt(value time.Time) bool {
// 业务需要“已填写”时先排除 Go 零值,不能用 Unix() == 0 代替。
return !value.IsZero()
}
我现在会把时间字段先分成三类:可选字段看 nil 或 IsZero,epoch 约定看 Unix() == 0,同一瞬间看 Equal。这三个判断口径分开后,接口默认值、数据库特殊值和日志展示就不容易互相污染。
常见问题
time.Time{} 的 Unix() 为什么不是 0?
因为它表示公元 1 年,而 Unix 计时从 1970 年开始,所以返回的是一个负的秒数。只有明确构造为 time.Unix(0, 0) 的时间才满足 Unix() == 0。
接口字段只用 time.Time 不用指针可以吗?
可以,但要接受“缺失”和“零值”共享同一个表示。需要区分缺失、null、epoch 和其他时间时,使用 *time.Time 或单独的存在标记更清楚。
-
502 收藏
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
172 收藏
-
370 收藏
-
206 收藏
-
385 收藏
-
126 收藏
-
218 收藏
-
279 收藏
-
342 收藏
-
372 收藏
-
369 收藏
-
300 收藏
-
216 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习