summaryrefslogtreecommitdiff
path: root/src/pkg/time/zoneinfo.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/time/zoneinfo.go')
-rw-r--r--src/pkg/time/zoneinfo.go87
1 files changed, 80 insertions, 7 deletions
diff --git a/src/pkg/time/zoneinfo.go b/src/pkg/time/zoneinfo.go
index 1c6186258..c8e53a27c 100644
--- a/src/pkg/time/zoneinfo.go
+++ b/src/pkg/time/zoneinfo.go
@@ -45,6 +45,13 @@ type zoneTrans struct {
isstd, isutc bool // ignored - no idea what these mean
}
+// alpha and omega are the beginning and end of time for zone
+// transitions.
+const (
+ alpha = -1 << 63 // math.MinInt64
+ omega = 1<<63 - 1 // math.MaxInt64
+)
+
// UTC represents Universal Coordinated Time (UTC).
var UTC *Location = &utcLoc
@@ -83,9 +90,9 @@ func FixedZone(name string, offset int) *Location {
l := &Location{
name: name,
zone: []zone{{name, offset, false}},
- tx: []zoneTrans{{-1 << 63, 0, false, false}},
- cacheStart: -1 << 63,
- cacheEnd: 1<<63 - 1,
+ tx: []zoneTrans{{alpha, 0, false, false}},
+ cacheStart: alpha,
+ cacheEnd: omega,
}
l.cacheZone = &l.zone[0]
return l
@@ -101,12 +108,12 @@ func FixedZone(name string, offset int) *Location {
func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start, end int64) {
l = l.get()
- if len(l.tx) == 0 {
+ if len(l.zone) == 0 {
name = "UTC"
offset = 0
isDST = false
- start = -1 << 63
- end = 1<<63 - 1
+ start = alpha
+ end = omega
return
}
@@ -119,10 +126,24 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start
return
}
+ if len(l.tx) == 0 || sec < l.tx[0].when {
+ zone := &l.zone[l.lookupFirstZone()]
+ name = zone.name
+ offset = zone.offset
+ isDST = zone.isDST
+ start = alpha
+ if len(l.tx) > 0 {
+ end = l.tx[0].when
+ } else {
+ end = omega
+ }
+ return
+ }
+
// Binary search for entry with largest time <= sec.
// Not using sort.Search to avoid dependencies.
tx := l.tx
- end = 1<<63 - 1
+ end = omega
lo := 0
hi := len(tx)
for hi-lo > 1 {
@@ -144,6 +165,58 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start
return
}
+// lookupFirstZone returns the index of the time zone to use for times
+// before the first transition time, or when there are no transition
+// times.
+//
+// The reference implementation in localtime.c from
+// http://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz
+// implements the following algorithm for these cases:
+// 1) If the first zone is unused by the transitions, use it.
+// 2) Otherwise, if there are transition times, and the first
+// transition is to a zone in daylight time, find the first
+// non-daylight-time zone before and closest to the first transition
+// zone.
+// 3) Otherwise, use the first zone that is not daylight time, if
+// there is one.
+// 4) Otherwise, use the first zone.
+func (l *Location) lookupFirstZone() int {
+ // Case 1.
+ if !l.firstZoneUsed() {
+ return 0
+ }
+
+ // Case 2.
+ if len(l.tx) > 0 && l.zone[l.tx[0].index].isDST {
+ for zi := int(l.tx[0].index) - 1; zi >= 0; zi-- {
+ if !l.zone[zi].isDST {
+ return zi
+ }
+ }
+ }
+
+ // Case 3.
+ for zi := range l.zone {
+ if !l.zone[zi].isDST {
+ return zi
+ }
+ }
+
+ // Case 4.
+ return 0
+}
+
+// firstZoneUsed returns whether the first zone is used by some
+// transition.
+func (l *Location) firstZoneUsed() bool {
+ for _, tx := range l.tx {
+ if tx.index == 0 {
+ return true
+ }
+ }
+ return false
+}
+
// lookupName returns information about the time zone with
// the given name (such as "EST") at the given pseudo-Unix time
// (what the given time of day would be in UTC).