Go の Timezone と Go 1.15 の tzdata 埋め込み

Go の Timezone と Go 1.15 の tzdata 埋め込み

某所のLT

Db2247a5b4226e1f6f74a9bd87810090?s=128

Hiroaki Sano

July 21, 2020
Tweet

Transcript

  1. Go ͷ Timezone ͱ Go 1.15 ͷ tzdata ຒΊࠐΈ
 Remote.go

    #1
 Jul 17, 2020 Kanmu, Inc. Hiroaki Sano
  2. 2 ࠤ໺༟ষ Backend Engineer at Kanmu, Inc. @hiroakis hiroakis (@la_luna_azul)

  3. 3 όϯυϧΧʔυͭͬͯ͘·͢

  4. Go 1.15 1

  5. Go 1.15 5 1 • 2020೥8݄ʹϦϦʔε༧ఆ • ݱࡏ Go 1.15

    Beta1 ͕ར༻Մೳ IUUQTUXJUUFSDPNHPMBOHTUBUVT
  6. Go 1.15: Beta1 6 1 $ go get golang.org/dl/go1.15beta1 $

    go1.15beta1 download $ go1.15beta1 version go version go1.15beta1 darwin/amd64
  7. Go 1.15: DRAFT RELEASE NOTES 7 1 IUUQTUJQHPMBOHPSHEPDHP

  8. Go ͷ Timezone 2

  9. Go ͷ Timezone: Time ߏ଄ମͱ Now() 9 2 // Now

    returns the current local time. func Now() Time { sec, nsec, mono := now() mono -= startNano sec += unixToInternal - minWall if uint64(sec)>>33 != 0 { return Time{uint64(nsec), sec + minWall, Local} } return Time{hasMonotonic | uint64(sec)<<nsecShift | uint64(nsec), mono, Local} } type Time struct { wall uint64 ext int64 loc *Location }
  10. 10 2 // Now returns the current local time. func

    Now() Time { sec, nsec, mono := now() mono -= startNano sec += unixToInternal - minWall if uint64(sec)>>33 != 0 { return Time{uint64(nsec), sec + minWall, Local} } return Time{hasMonotonic | uint64(sec)<<nsecShift | uint64(nsec), mono, Local} } type Time struct { wall uint64 ext int64 loc *Location } Go ͷ Timezone: Time ߏ଄ମͱ Now()
  11. Go ͷ Timezone: Location ߏ଄ମ 11 2 var Local *Location

    = &localLoc
 var localLoc Location type Location struct { name string []zone []zone tx []zoneTrans cacheStart int64 cacheEnd []zoneint64 cacheZone *zone } var localOnce sync.Once func (l *Location) get() *Location { if l == nil { return &utcLoc } if l == &localLoc { localOnce.Do(initLocal) // <— initLocal ͸zoneinfo_xxx.go Ͱ֤؀ڥ͝ͱʹఆٛ } return l }
  12. Go ͷ Timezone: Linux ͷ initLocal 12 2 var zoneSources

    = []string{ "/usr/share/zoneinfo/", "/usr/share/lib/zoneinfo/", "/usr/lib/locale/TZ/", runtime.GOROOT() + "/lib/time/zoneinfo.zip", } func initLocal() { tz, ok := syscall.Getenv("TZ") switch { case !ok: z, err := loadLocation("localtime", []string{"/etc/"}) if err == nil { localLoc = *z localLoc.name = "Local" return } case tz != "" && tz != "UTC": if z, err := loadLocation(tz, zoneSources); err == nil { localLoc = *z return } } localLoc.name = "UTC" } UJNF[POFJOGP@VOJYHP
  13. Go ͷ Timezone: Windows ͷ initLocal 13 2 var zoneSources

    = []string{ runtime.GOROOT() + "/lib/time/zoneinfo.zip", } func initLocal() { var i syscall.Timezoneinformation if _, err := syscall.GetTimeZoneInformation(&i); err != nil { localLoc.name = "UTC" return } initLocalFromTZI(&i) } UJNF[POFJOGP@XJOEPXTHP
  14. Go ͷ Timezone: Local Time ͷܾఆ 14 2 • ϩʔΧϧλΠϜ͸֤؀ڥͷ࣮૷

    (zoneinfo_xxx.go) ͰॳظԽ • func (l *Location) get() ͕ݺ͹Εͨͱ͖ʹͦΕ͕ηοτ͞ΕΔ • l.get() ͸͍ͭݺ͹Ε͍ͯΔͷ͔ʁ • ͍͔ͭ͘ͷ৔ॴͰݺ͹Ε͍ͯΔɻtime.Time ΍ time.Location ͷ Stringer ΠϯλʔϑΣΠεͷ࣮૷ͳͲɻ • Linux ΛྫʹऔΔͱɺl.get() ͕ݺ͹Εͨͱ͖ʹ TZ ؀ڥม਺Λݩ ʹ /usr/share… ͳͲΛ୳͠ʹߦ͘ɻ
  15. Timezoneͷมߋ 2

  16. Timezone ͷมߋ 16 2 • ͍͔ͭ͘ͷखஈ • ϩʔΧϧλΠϜΛมߋ͢Δ • *Location

    Λඞཁͳͱ͖ʹ࡞ͬͯར༻͢Δ
  17. Timezone ͷมߋ: Local Time Λมߋ͢Δ 17 2 • Linux ͷ৔߹

    TZ ؀ڥม਺Λઃఆ͢Δ • ͨͩ͠ॳճͷΈ • sync.Once(initLocal) Ͱηοτ͞ΕΔͨΊ • ΞϓϦέʔγϣϯͷ࣮૷Ͱ var time.Local άϩʔόϧม਺ʹ *Location Λ୅ೖͯ͠͠·͏
  18. Timezone ͷมߋ: *Location Λඞཁͳͱ͖ʹ࡞ͬͯར༻͢Δ 18 2 • *Location ΛಘΔ͍͔ͭ͘ͷAPI •

    func LoadLocation(name string) • func LoadLocationFromTZData(name string, data []byte) • func FixedZone(name string, offset int) • *Location ͷ࢖͍ํ • In(loc) • time.Now().In(loc) • time.Date(xxxx).In(loc) • …
  19. Timezone ͷมߋ: func LoadLocation 19 2 func LoadLocation(name string) (*Location,

    error) { zoneinfoOnce.Do(func() { env, _ := syscall.Getenv("ZONEINFO") zoneinfo = &env }) var firstErr error if *zoneinfo != "" { if zoneData, err := loadTzinfoFromDirOrZip(*zoneinfo, name); err == nil { if z, err := LoadLocationFromTZData(name, zoneData); err == nil { return z, nil } firstErr = err } else if err != syscall.ENOENT { firstErr = err } } if z, err := loadLocation(name, zoneSources); err == nil { return z, nil } else if firstErr == nil { firstErr = err } return nil, firstErr }
  20. 20 2 • zoneSources Λ୳͠ʹ͍͘ • ࠷ॳʹݟ͔ͭͬͨ΍͕ͭద༻͞ΕΔ • ͳ͚Ε͹ΤϥʔʹͳΔ //

    time/zoneinfo_unix.go var zoneSources = []string{ "/usr/share/zoneinfo/", "/usr/share/lib/zoneinfo/", "/usr/lib/locale/TZ/", runtime.GOROOT() + "/lib/time/zoneinfo.zip", } // time/zoneinfo_windows.go var zoneSources = []string{ runtime.GOROOT() + "/lib/time/zoneinfo.zip", } Timezone ͷมߋ: func LoadLocation
  21. Timezone ͷมߋ: ଞͷखஈ 21 2 • func LoadLocationFromTZData(name string, data

    []byte) • ࣗ෼Ͱ tzdata ͷੜσʔλΛ༻ҙ͓͍ͯͯ͠ಡ·ͤΔ • func FixedZone(name string, offset int) • ϩʔΧϧλΠϜ͔Βͷ offset Λϋʔυίʔυ͢Δ
  22. Timezoneͷ໰୊఺ 3

  23. 23 3 • ؀ڥґଘ • Linux ͸ tzdata ͕ଘࡏ͍ͯ͠Δ͔ Go

    ͕Πϯετʔϧ͞Ε͍ͯ Δඞཁ͕͋Δ • Windows ͸ Go ͕Πϯετʔϧ͞Ε͍ͯΔඞཁ͕͋Δ Timezoneͷ໰୊఺
  24. 24 3 • ΞϓϦέʔγϣϯ഑෍ઌʹ tzdata ΛΠϯετʔϧ͓ͯ͘͠ • ΞϓϦέʔγϣϯ഑෍ઌʹ Go ΋Πϯετʔϧ͓ͯ͘͠

    • ࣗ෼Ͱ tzdata Λ༻ҙͯ͠ LoadLocationFromTZData ͰಡΉ • FixedZone(name string, offset int)Λ࢖͏(ͨͩ͠αϚʔλΠϜ͕ ߟྀ͞Εͳ͍) Timezoneͷ໰୊఺: ղܾࡦ
  25. Go 1.15 embedded tzdata package 4

  26. Embedded tzdata package 26 4 IUUQTHJUIVCDPNHPMBOHHPJTTVFT

  27. 27 4 • Timezone ͷѻ͍ʹखஈ͕૿͑ͨ • ΞϓϦέʔγϣϯ഑෍ઌʹ tzdata ΛΠϯετʔϧ͓ͯ͘͠ •

    ΞϓϦέʔγϣϯ഑෍ઌʹ Go ΋Πϯετʔϧ͓ͯ͘͠ • ࣗ෼Ͱ tzdata Λ༻ҙͯ͠ LoadLocationFromTZData ͰಡΉ • Go 1.15 ͷ tzdata ຒΊࠐΈ Embedded tzdata package
  28. 28 4 IUUQTHPSFWJFXHPPHMFTPVSDFDPNDHP TSDUJNF[POFJOGP@SFBEHP Embedded tzdata package

  29. 29 4 IUUQTHPSFWJFXHPPHMFTPVSDFDPNDHP TSDUJNFU[EBUB[JQEBUBHP Embedded tzdata package

  30. 30 package main import ( "fmt" "log" "time" ) func

    main() { jst, err := time.LoadLocation("Asia/Tokyo") if err != nil { log.Fatal(err) } tm := time.Now().In(jst) fmt.Println(tm) } 4 Embedded tzdata package: σϞ
  31. 31 $ go build -o jst main.go $ ./jst 2020-07-11

    16:42:00.588488 +0900 JST 4 Embedded tzdata package: σϞ
  32. 32 FROM alpine:latest WORKDIR /app COPY jst /app CMD ["/app/jst"]

    4 Embedded tzdata package: σϞ
  33. 33 $ GOOS=linux go build -o jst main.go $ docker

    build -t jst:latest . $ docker run --rm jst:latest 2020/07/11 07:45:07 unknown time zone Asia/Tokyo 4 Embedded tzdata package: σϞ
  34. 34 package main import ( "fmt" "log" "time" _ "time/tzdata"

    ) func main() { jst, err := time.LoadLocation("Asia/Tokyo") if err != nil { log.Fatal(err) } tm := time.Now().In(jst) fmt.Println(tm) } 4 Embedded tzdata package: σϞ
  35. 35 $ GOOS=linux go1.15beta1 build -o jst main.go $ docker

    build -t jst:latest . $ docker run --rm jst:latest 2020-07-11 16:52:27.389673074 +0900 JST 4 Embedded tzdata package: σϞ
  36. 36 • छʑͷ؀ڥͰ࢖ΘΕΔιϑτ΢ΣΞΛ഑෍͢Δͱ͖ʹબ୒ࢶͱͯ͠ ༗༻ʹͳΓಘΔ • Α͘࢖ΘΕΔύοέʔδͷ಺෦Ͱ΋ LoadLocation ͸࢖ΘΕ͍ͯΔ • github.com/go-sql-driver/mysql

    • github.com/lib/pq • …etc • tzdata ͸͠͹͠͹ߋ৽͞ΕΔ • OSͳͲ౔୆ͰέΞ͢Δ͔ɺΞϓϦέʔγϣϯଆͰέΞ͢Δ͔ͷ ໰୊ͳͷͰಛੑΛΘ͔͍ͬͯΕ͹Α͍ 4 Embedded tzdata package
  37. ·ͱΊ 37 5 ·ͱΊ • Timezone ͸౔୆ͷ؀ڥʹґଘ͍ͯ͠Δ • Go1.15 ͔Β

    tzdata ຒΊࠐΈ͕Ͱ͖ΔΑ͏ʹͳΓ؀ڥґଘΛٵ ऩ͢Δखஈ͕૿͑Δ
  38. ͓ΘΓ