😺

goでの基本的なtimezoneの決定方法をしらべてみた

2022/12/30に公開
  • 2年ぐらい前に調べた物のメモ
  • 探すとき大変なのでこちらにもうつす

timezoneはどこで決定されるのか

  • /usr/local/go/src/time/zoneinfo.go
  • このgetが呼び出されたときに決定される
  • Onceは実行中一度しか呼び出されない
  • つまり最初にget()が呼び出された瞬間に決定される
 87 func (l *Location) get() *Location {
 88     if l == nil {
 89         return &utcLoc
 90     }
 91     if l == &localLoc {
 92         localOnce.Do(initLocal)
 93     }
 94     return l
 95 }

initLocalでは環境がどのように作用しているのか?

  • zoneinfo_unix.go
 28 func initLocal() {
 29     // consult $TZ to find the time zone to use.
 30     // no $TZ means use the system default /etc/localtime.
 31     // $TZ="" means use UTC.
 32     // $TZ="foo" means use /usr/share/zoneinfo/foo.
 33
 34     tz, ok := syscall.Getenv("TZ")
 35     switch {
 36     case !ok:
 37         z, err := loadLocation("localtime", []string{"/etc"})
 38         if err == nil {
 39             localLoc = *z
 40             localLoc.name = "Local"
 41             return
 42         }
 43     case tz != "" && tz != "UTC":
 44         if z, err := loadLocation(tz, zoneSources); err == nil {
 45             localLoc = *z
 46             return
 47         }
 48     }
 49
 50     // Fall back to UTC.
 51     localLoc.name = "UTC"
 52 }
  • 大きく分けて処理は2つに分かれる
    • 環境変数TZが無い場合
    • 環境変数TZが有る場合

環境変数TZが無い場合

  • 結論:無い場合は/etc/localtimeが適切に設定されていればそこに準拠した動作になります
  • この場合はシステムの
  • /etc/localtimeを見に行きます
  • 自分のMacの場合localtimeにAsia/Tokyoへのリンクが張られています
 ls -al /etc/localtime
lrwxr-xr-x 36 root  5 8 10:02 /etc/localtime -> /var/db/timezone/zoneinfo/Asia/Tokyo

環境変数TZが有る場合

  • 結論:有る場合は以下のzoneSourcesを順に探していきます
  • 例えばalpineの場合はtzdataをインストールした場合
  • /usr/share/zoneinfo/にAsia/Tokyoが存在することになるので
  • TZをAsia/TokyoにしておけばJSTになります
 21 var zoneSources = []string{
 22     "/usr/share/zoneinfo/",
 23     "/usr/share/lib/zoneinfo/",
 24     "/usr/lib/locale/TZ/",
 25     runtime.GOROOT() + "/lib/time/zoneinfo.zip",
 26 }

mysql driverの場合

  • mysql driverでlocを設定した場合
  • どうなるのか。
  • dsn.goでconfigに設定される
450         // Time Location
451         case "loc":
452             if value, err = url.QueryUnescape(value); err != nil {
453                 return
454             }
455             cfg.Loc, err = time.LoadLocation(value)
456             if err != nil {
457                 return
458             }
  • ZONEINFO環境変数などが設定されていない場合に関しては
  • locに設定した値を元に
  • loadLocationが呼び出される
.656     if z, err := loadLocation(name, zoneSources); err == nil {
657         return z, nil
658     } else if firstErr == nil {
659         firstErr = err
660     }
661     return nil, firstErr

locの設定はどこで使用されているか

 774                 if !mc.parseTime {
 775                     continue
 776                 } else {
 777                     switch rows.rs.columns[i].fieldType {
 778                     case fieldTypeTimestamp, fieldTypeDateTime,
 779                         fieldTypeDate, fieldTypeNewDate:
 780                         dest[i], err = parseDateTime(
 781                             dest[i].([]byte),
 782                             mc.cfg.Loc,
 783                         )
 784                         if err == nil {
 785                             continue
 786                         }
 787                     default:
 788                         continue
 789                     }

Discussion