2011-09-16 17:47:21 +02:00
|
|
|
// Copyright 2011 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Parse Plan 9 timezone(2) files.
|
|
|
|
|
|
|
|
package time
|
|
|
|
|
2012-01-12 02:31:45 +01:00
|
|
|
import (
|
2012-03-02 21:01:37 +01:00
|
|
|
"runtime"
|
2012-01-12 02:31:45 +01:00
|
|
|
"syscall"
|
|
|
|
)
|
2011-09-16 17:47:21 +02:00
|
|
|
|
2018-01-09 02:23:08 +01:00
|
|
|
var zoneSources = []string{
|
|
|
|
runtime.GOROOT() + "/lib/time/zoneinfo.zip",
|
|
|
|
}
|
|
|
|
|
2012-01-12 02:31:45 +01:00
|
|
|
func isSpace(r rune) bool {
|
|
|
|
return r == ' ' || r == '\t' || r == '\n'
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copied from strings to avoid a dependency.
|
|
|
|
func fields(s string) []string {
|
|
|
|
// First count the fields.
|
|
|
|
n := 0
|
|
|
|
inField := false
|
|
|
|
for _, rune := range s {
|
|
|
|
wasInField := inField
|
|
|
|
inField = !isSpace(rune)
|
|
|
|
if inField && !wasInField {
|
|
|
|
n++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now create them.
|
|
|
|
a := make([]string, n)
|
|
|
|
na := 0
|
|
|
|
fieldStart := -1 // Set to -1 when looking for start of field.
|
|
|
|
for i, rune := range s {
|
|
|
|
if isSpace(rune) {
|
|
|
|
if fieldStart >= 0 {
|
|
|
|
a[na] = s[fieldStart:i]
|
|
|
|
na++
|
|
|
|
fieldStart = -1
|
|
|
|
}
|
|
|
|
} else if fieldStart == -1 {
|
|
|
|
fieldStart = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if fieldStart >= 0 { // Last field might end at EOF.
|
|
|
|
a[na] = s[fieldStart:]
|
|
|
|
}
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
2012-03-02 21:01:37 +01:00
|
|
|
func loadZoneDataPlan9(s string) (l *Location, err error) {
|
2012-01-12 02:31:45 +01:00
|
|
|
f := fields(s)
|
2011-09-16 17:47:21 +02:00
|
|
|
if len(f) < 4 {
|
2012-01-12 02:31:45 +01:00
|
|
|
if len(f) == 2 && f[0] == "GMT" {
|
|
|
|
return UTC, nil
|
|
|
|
}
|
|
|
|
return nil, badData
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
|
|
|
|
2012-01-12 02:31:45 +01:00
|
|
|
var zones [2]zone
|
|
|
|
|
2011-09-16 17:47:21 +02:00
|
|
|
// standard timezone offset
|
2012-01-12 02:31:45 +01:00
|
|
|
o, err := atoi(f[1])
|
2011-09-16 17:47:21 +02:00
|
|
|
if err != nil {
|
2012-01-12 02:31:45 +01:00
|
|
|
return nil, badData
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
2012-01-12 02:31:45 +01:00
|
|
|
zones[0] = zone{name: f[0], offset: o, isDST: false}
|
2011-09-16 17:47:21 +02:00
|
|
|
|
|
|
|
// alternate timezone offset
|
2012-01-12 02:31:45 +01:00
|
|
|
o, err = atoi(f[3])
|
2011-09-16 17:47:21 +02:00
|
|
|
if err != nil {
|
2012-01-12 02:31:45 +01:00
|
|
|
return nil, badData
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
2012-01-12 02:31:45 +01:00
|
|
|
zones[1] = zone{name: f[2], offset: o, isDST: true}
|
2011-09-16 17:47:21 +02:00
|
|
|
|
|
|
|
// transition time pairs
|
2012-01-12 02:31:45 +01:00
|
|
|
var tx []zoneTrans
|
2011-09-16 17:47:21 +02:00
|
|
|
f = f[4:]
|
|
|
|
for i := 0; i < len(f); i++ {
|
2012-01-12 02:31:45 +01:00
|
|
|
zi := 0
|
2011-09-16 17:47:21 +02:00
|
|
|
if i%2 == 0 {
|
2012-01-12 02:31:45 +01:00
|
|
|
zi = 1
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
2012-01-12 02:31:45 +01:00
|
|
|
t, err := atoi(f[i])
|
2011-09-16 17:47:21 +02:00
|
|
|
if err != nil {
|
2012-01-12 02:31:45 +01:00
|
|
|
return nil, badData
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
2012-01-12 02:31:45 +01:00
|
|
|
t -= zones[0].offset
|
|
|
|
tx = append(tx, zoneTrans{when: int64(t), index: uint8(zi)})
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
2012-01-12 02:31:45 +01:00
|
|
|
|
|
|
|
// Committed to succeed.
|
|
|
|
l = &Location{zone: zones[:], tx: tx}
|
|
|
|
|
|
|
|
// Fill in the cache with information about right now,
|
|
|
|
// since that will be the most common lookup.
|
2017-09-14 19:11:35 +02:00
|
|
|
sec, _, _ := now()
|
2012-01-12 02:31:45 +01:00
|
|
|
for i := range tx {
|
|
|
|
if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
|
|
|
|
l.cacheStart = tx[i].when
|
2014-06-07 00:37:27 +02:00
|
|
|
l.cacheEnd = omega
|
2012-01-12 02:31:45 +01:00
|
|
|
if i+1 < len(tx) {
|
|
|
|
l.cacheEnd = tx[i+1].when
|
|
|
|
}
|
|
|
|
l.cacheZone = &l.zone[tx[i].index]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return l, nil
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
|
|
|
|
2012-03-02 21:01:37 +01:00
|
|
|
func loadZoneFilePlan9(name string) (*Location, error) {
|
2012-01-12 02:31:45 +01:00
|
|
|
b, err := readFile(name)
|
2011-09-16 17:47:21 +02:00
|
|
|
if err != nil {
|
2012-01-12 02:31:45 +01:00
|
|
|
return nil, err
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
2012-03-02 21:01:37 +01:00
|
|
|
return loadZoneDataPlan9(string(b))
|
2011-09-16 17:47:21 +02:00
|
|
|
}
|
2011-10-27 01:57:58 +02:00
|
|
|
|
2012-01-12 02:31:45 +01:00
|
|
|
func initLocal() {
|
|
|
|
t, ok := syscall.Getenv("timezone")
|
|
|
|
if ok {
|
2012-03-02 21:01:37 +01:00
|
|
|
if z, err := loadZoneDataPlan9(t); err == nil {
|
2012-01-12 02:31:45 +01:00
|
|
|
localLoc = *z
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
2012-03-02 21:01:37 +01:00
|
|
|
if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
|
2012-01-12 02:31:45 +01:00
|
|
|
localLoc = *z
|
|
|
|
localLoc.name = "Local"
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall back to UTC.
|
|
|
|
localLoc.name = "UTC"
|
|
|
|
}
|