PAYATHON 2026

Go 升级后 time.LoadLocation("Turkey") 为何提示未知时区?

数据小许

结论

建议使用 Europe/Istanbul 表示土耳其时区,并通过空白导入 time/tzdata。这样,即使部署环境缺少外部时区数据,程序仍有可用的数据来源。

现有信息只能说明 time.LoadLocation("Turkey") 未能在部署环境中加载该名称对应的数据,尚不能判断故障是由 Ubuntu 升级或 Go 的某个版本缺陷引起的。错误返回后,代码中的 panic(err) 导致程序发生 panic。

为什么本地正常,部署后失败?

time.LoadLocation 需要读取时区数据库。数据来源包括 ZONEINFO 环境变量指定的位置、系统时区目录、$GOROOT/lib/time/zoneinfo.zip,以及通过 time/tzdata 嵌入的数据。

本地开发环境和托管运行环境提供的时区数据可能不同。即使开发机器上的 zoneinfo.zip 包含 Turkey,也不能说明部署后的进程能访问这份文件。

Turkey 是兼容性时区别名,对应 Europe/Istanbul。如果部署环境的数据缺少该别名,可能出现 Europe/Istanbul 能加载而 Turkey 无法加载的情况。如果时区数据整体缺失,两者都可能加载失败。

推荐修改

在程序中导入 time/tzdata,并使用 Europe/Istanbul:

package main

import (
    "log"
    "time"
    _ "time/tzdata"
)

func main() {
    turkey, err := time.LoadLocation("Europe/Istanbul")
    if err != nil {
        log.Fatalf("load Europe/Istanbul: %v", err)
    }

    log.Println(time.Now().In(turkey))
}

空白导入会把时区数据库嵌入可执行文件,供 time 包在其他数据来源无法提供所需时区时使用。对于 App Engine 应用,在实际部署的服务程序中加入该导入后,需要重新构建并部署。

如果业务必须继续接受 Turkey 这个字符串,也可以保留原调用并加入上述导入,再在目标环境验证加载结果。

如何进一步定位?

可以在部署环境中临时记录两个名称的加载结果:

for _, name := range []string{"Turkey", "Europe/Istanbul"} {
    _, err := time.LoadLocation(name)
    log.Printf("LoadLocation(%q): %v", name, err)
}
  • 只有 Turkey 失败:优先检查部署环境是否缺少该兼容性别名。
  • 两者都失败:优先检查时区数据是否存在,以及进程是否能访问它们。
  • 加入 time/tzdata 后成功:说明嵌入的数据能够满足加载需求,但仍不足以证明原先具体缺失的是哪个文件。

同时检查部署配置是否设置了 ZONEINFO。如果设置了该变量,应核对它指向的数据是否随应用部署,以及是否包含需要的时区。

注意事项

基础系统升级可能改变时区数据的内容或安装方式。题目中涉及的 Go、Ubuntu 版本对应关系,以及它们与故障的因果关系,仍需在实际运行环境中确认。

不要用固定 UTC 偏移来掩盖时区加载失败。固定偏移无法完整表达地区的历史时间规则;处理土耳其地区时间时,应优先使用 Europe/Istanbul。

备注:内容仅供参考。