blob: 26637a151f06af18dcfdb09d0c9891ec8e4062f3 [file] [log] [blame]
// 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
import (
"runtime"
"syscall"
)
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
}
func loadZoneDataPlan9(s string) (l *Location, err error) {
f := fields(s)
if len(f) < 4 {
if len(f) == 2 && f[0] == "GMT" {
return UTC, nil
}
return nil, badData
}
var zones [2]zone
// standard timezone offset
o, err := atoi(f[1])
if err != nil {
return nil, badData
}
zones[0] = zone{name: f[0], offset: o, isDST: false}
// alternate timezone offset
o, err = atoi(f[3])
if err != nil {
return nil, badData
}
zones[1] = zone{name: f[2], offset: o, isDST: true}
// transition time pairs
var tx []zoneTrans
f = f[4:]
for i := 0; i < len(f); i++ {
zi := 0
if i%2 == 0 {
zi = 1
}
t, err := atoi(f[i])
if err != nil {
return nil, badData
}
t -= zones[0].offset
tx = append(tx, zoneTrans{when: int64(t), index: uint8(zi)})
}
// 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.
sec, _, _ := now()
for i := range tx {
if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
l.cacheStart = tx[i].when
l.cacheEnd = omega
if i+1 < len(tx) {
l.cacheEnd = tx[i+1].when
}
l.cacheZone = &l.zone[tx[i].index]
}
}
return l, nil
}
func loadZoneFilePlan9(name string) (*Location, error) {
b, err := readFile(name)
if err != nil {
return nil, err
}
return loadZoneDataPlan9(string(b))
}
func initTestingZone() {
z, err := loadLocation("America/Los_Angeles")
if err != nil {
panic("cannot load America/Los_Angeles for testing: " + err.Error())
}
z.name = "Local"
localLoc = *z
}
func initLocal() {
t, ok := syscall.Getenv("timezone")
if ok {
if z, err := loadZoneDataPlan9(t); err == nil {
localLoc = *z
return
}
} else {
if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
localLoc = *z
localLoc.name = "Local"
return
}
}
// Fall back to UTC.
localLoc.name = "UTC"
}
func loadLocation(name string) (*Location, error) {
z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name)
if err != nil {
return nil, err
}
z.name = name
return z, nil
}
func forceZipFileForTesting(zipOnly bool) {
// We only use the zip file anyway.
}