diff options
author | Ian Lance Taylor <iant@golang.org> | 2017-09-14 17:11:35 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2017-09-14 17:11:35 +0000 |
commit | bc998d034f45d1828a8663b2eed928faf22a7d01 (patch) | |
tree | 8d262a22ca7318f4bcd64269fe8fe9e45bcf8d0f /libgo/go/time | |
parent | a41a6142df74219f596e612d3a7775f68ca6e96f (diff) | |
download | gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.zip gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.gz gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.bz2 |
libgo: update to go1.9
Reviewed-on: https://go-review.googlesource.com/63753
From-SVN: r252767
Diffstat (limited to 'libgo/go/time')
-rw-r--r-- | libgo/go/time/example_test.go | 3 | ||||
-rw-r--r-- | libgo/go/time/export_test.go | 13 | ||||
-rw-r--r-- | libgo/go/time/format.go | 50 | ||||
-rw-r--r-- | libgo/go/time/format_test.go | 4 | ||||
-rw-r--r-- | libgo/go/time/genzabbrs.go | 4 | ||||
-rw-r--r-- | libgo/go/time/mono_test.go | 261 | ||||
-rw-r--r-- | libgo/go/time/sleep_test.go | 4 | ||||
-rw-r--r-- | libgo/go/time/sys_plan9.go | 4 | ||||
-rw-r--r-- | libgo/go/time/sys_unix.go | 4 | ||||
-rw-r--r-- | libgo/go/time/sys_windows.go | 4 | ||||
-rw-r--r-- | libgo/go/time/time.go | 397 | ||||
-rw-r--r-- | libgo/go/time/time_test.go | 74 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo.go | 34 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_abbrs_windows.go | 176 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_plan9.go | 2 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_read.go | 13 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_test.go | 50 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_windows.go | 2 |
18 files changed, 921 insertions, 178 deletions
diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go index 7dc2bb5..aeb63ca 100644 --- a/libgo/go/time/example_test.go +++ b/libgo/go/time/example_test.go @@ -256,6 +256,9 @@ func ExampleTime_Truncate() { for _, d := range trunc { fmt.Printf("t.Truncate(%5s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999")) } + // To round to the last midnight in the local timezone, create a new Date. + midnight := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.Local) + _ = midnight // Output: // t.Truncate( 1ns) = 12:15:30.918273645 diff --git a/libgo/go/time/export_test.go b/libgo/go/time/export_test.go index 6cd535f..4c08ab1 100644 --- a/libgo/go/time/export_test.go +++ b/libgo/go/time/export_test.go @@ -18,7 +18,20 @@ func ForceUSPacificForTesting() { localOnce.Do(initTestingZone) } +func ZoneinfoForTesting() *string { + return zoneinfo +} + +func ResetZoneinfoForTesting() { + zoneinfo = nil + zoneinfoOnce = sync.Once{} +} + var ( ForceZipFileForTesting = forceZipFileForTesting ParseTimeZone = parseTimeZone + SetMono = (*Time).setMono + GetMono = (*Time).mono + ErrLocation = errLocation + ReadFile = readFile ) diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index b903e14..8c16e87 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -61,6 +61,8 @@ import "errors" // RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting; // when used with time.Parse they do not accept all the time formats // permitted by the RFCs. +// The RFC3339Nano format removes trailing zeros from the seconds field +// and thus may not sort correctly once formatted. const ( ANSIC = "Mon Jan _2 15:04:05 2006" UnixDate = "Mon Jan _2 15:04:05 MST 2006" @@ -424,8 +426,41 @@ func formatNano(b []byte, nanosec uint, n int, trim bool) []byte { // String returns the time formatted using the format string // "2006-01-02 15:04:05.999999999 -0700 MST" +// +// If the time has a monotonic clock reading, the returned string +// includes a final field "m=±<value>", where value is the monotonic +// clock reading formatted as a decimal number of seconds. +// +// The returned string is meant for debugging; for a stable serialized +// representation, use t.MarshalText, t.MarshalBinary, or t.Format +// with an explicit format string. func (t Time) String() string { - return t.Format("2006-01-02 15:04:05.999999999 -0700 MST") + s := t.Format("2006-01-02 15:04:05.999999999 -0700 MST") + + // Format monotonic clock reading as m=±ddd.nnnnnnnnn. + if t.wall&hasMonotonic != 0 { + m2 := uint64(t.ext) + sign := byte('+') + if t.ext < 0 { + sign = '-' + m2 = -m2 + } + m1, m2 := m2/1e9, m2%1e9 + m0, m1 := m1/1e9, m1%1e9 + var buf []byte + buf = append(buf, " m="...) + buf = append(buf, sign) + wid := 0 + if m0 != 0 { + buf = appendInt(buf, int(m0), 0) + wid = 9 + } + buf = appendInt(buf, int(m1), wid) + buf = append(buf, '.') + buf = appendInt(buf, int(m2), 9) + s += string(buf) + } + return s } // Format returns a textual representation of the time value formatted @@ -725,11 +760,6 @@ func skip(value, prefix string) (string, error) { // location and zone in the returned time. Otherwise it records the time as // being in a fabricated location with time fixed at the given zone offset. // -// No checking is done that the day of the month is within the month's -// valid dates; any one- or two-digit value is accepted. For example -// February 31 and even February 99 are valid dates, specifying dates -// in March and May. This behavior is consistent with time.Date. -// // When parsing a time with a zone abbreviation like MST, if the zone abbreviation // has a defined offset in the current location, then that offset is used. // The zone abbreviation "UTC" is recognized as UTC regardless of location. @@ -1022,11 +1052,11 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) if zoneOffset != -1 { t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) - t.sec -= int64(zoneOffset) + t.addSec(-int64(zoneOffset)) // Look for local zone with the given offset. // If that zone was in effect at the given time, use it. - name, offset, _, _, _ := local.lookup(t.sec + internalToUnix) + name, offset, _, _, _ := local.lookup(t.unixSec()) if offset == zoneOffset && (zoneName == "" || name == zoneName) { t.setLoc(local) return t, nil @@ -1041,9 +1071,9 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) // Look for local zone with the given offset. // If that zone was in effect at the given time, use it. - offset, _, ok := local.lookupName(zoneName, t.sec+internalToUnix) + offset, _, ok := local.lookupName(zoneName, t.unixSec()) if ok { - t.sec -= int64(offset) + t.addSec(-int64(offset)) t.setLoc(local) return t, nil } diff --git a/libgo/go/time/format_test.go b/libgo/go/time/format_test.go index 0e4a417..abaeb50 100644 --- a/libgo/go/time/format_test.go +++ b/libgo/go/time/format_test.go @@ -380,8 +380,8 @@ func checkTime(time Time, test *ParseTest, t *testing.T) { func TestFormatAndParse(t *testing.T) { const fmt = "Mon MST " + RFC3339 // all fields f := func(sec int64) bool { - t1 := Unix(sec, 0) - if t1.Year() < 1000 || t1.Year() > 9999 { + t1 := Unix(sec/2, 0) + if t1.Year() < 1000 || t1.Year() > 9999 || t1.Unix() != sec { // not required to work return true } diff --git a/libgo/go/time/genzabbrs.go b/libgo/go/time/genzabbrs.go index 6281f73..824a67f 100644 --- a/libgo/go/time/genzabbrs.go +++ b/libgo/go/time/genzabbrs.go @@ -142,8 +142,8 @@ const prog = ` // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// generated by genzabbrs.go from -// {{.URL}} +// Code generated by genzabbrs.go; DO NOT EDIT. +// Based on information from {{.URL}} package time diff --git a/libgo/go/time/mono_test.go b/libgo/go/time/mono_test.go new file mode 100644 index 0000000..8778ab7 --- /dev/null +++ b/libgo/go/time/mono_test.go @@ -0,0 +1,261 @@ +// Copyright 2017 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. + +package time_test + +import ( + "strings" + "testing" + . "time" +) + +func TestHasMonotonicClock(t *testing.T) { + yes := func(expr string, tt Time) { + if GetMono(&tt) == 0 { + t.Errorf("%s: missing monotonic clock reading", expr) + } + } + no := func(expr string, tt Time) { + if GetMono(&tt) != 0 { + t.Errorf("%s: unexpected monotonic clock reading", expr) + } + } + + yes("<-After(1)", <-After(1)) + ticker := NewTicker(1) + yes("<-Tick(1)", <-ticker.C) + ticker.Stop() + no("Date(2009, 11, 23, 0, 0, 0, 0, UTC)", Date(2009, 11, 23, 0, 0, 0, 0, UTC)) + tp, _ := Parse(UnixDate, "Sat Mar 7 11:06:39 PST 2015") + no(`Parse(UnixDate, "Sat Mar 7 11:06:39 PST 2015")`, tp) + no("Unix(1486057371, 0)", Unix(1486057371, 0)) + + yes("Now()", Now()) + + tu := Unix(1486057371, 0) + tm := tu + SetMono(&tm, 123456) + no("tu", tu) + yes("tm", tm) + + no("tu.Add(1)", tu.Add(1)) + no("tu.In(UTC)", tu.In(UTC)) + no("tu.AddDate(1, 1, 1)", tu.AddDate(1, 1, 1)) + no("tu.AddDate(0, 0, 0)", tu.AddDate(0, 0, 0)) + no("tu.Local()", tu.Local()) + no("tu.UTC()", tu.UTC()) + no("tu.Round(2)", tu.Round(2)) + no("tu.Truncate(2)", tu.Truncate(2)) + + yes("tm.Add(1)", tm.Add(1)) + no("tm.AddDate(1, 1, 1)", tm.AddDate(1, 1, 1)) + no("tm.AddDate(0, 0, 0)", tm.AddDate(0, 0, 0)) + no("tm.In(UTC)", tm.In(UTC)) + no("tm.Local()", tm.Local()) + no("tm.UTC()", tm.UTC()) + no("tm.Round(2)", tm.Round(2)) + no("tm.Truncate(2)", tm.Truncate(2)) +} + +func TestMonotonicAdd(t *testing.T) { + tm := Unix(1486057371, 123456) + SetMono(&tm, 123456789012345) + + t2 := tm.Add(1e8) + if t2.Nanosecond() != 100123456 { + t.Errorf("t2.Nanosecond() = %d, want 100123456", t2.Nanosecond()) + } + if GetMono(&t2) != 123456889012345 { + t.Errorf("t2.mono = %d, want 123456889012345", GetMono(&t2)) + } + + t3 := tm.Add(-9e18) // wall now out of range + if t3.Nanosecond() != 123456 { + t.Errorf("t3.Nanosecond() = %d, want 123456", t3.Nanosecond()) + } + if GetMono(&t3) != 0 { + t.Errorf("t3.mono = %d, want 0 (wall time out of range for monotonic reading)", GetMono(&t3)) + } + + t4 := tm.Add(+9e18) // wall now out of range + if t4.Nanosecond() != 123456 { + t.Errorf("t4.Nanosecond() = %d, want 123456", t4.Nanosecond()) + } + if GetMono(&t4) != 0 { + t.Errorf("t4.mono = %d, want 0 (wall time out of range for monotonic reading)", GetMono(&t4)) + } + + tn := Now() + tn1 := tn.Add(1 * Hour) + Sleep(100 * Millisecond) + d := Until(tn1) + if d < 59*Minute { + t.Errorf("Until(Now().Add(1*Hour)) = %v, wanted at least 59m", d) + } + now := Now() + if now.After(tn1) { + t.Errorf("Now().After(Now().Add(1*Hour)) = true, want false") + } + if !tn1.After(now) { + t.Errorf("Now().Add(1*Hour).After(now) = false, want true") + } + if tn1.Before(now) { + t.Errorf("Now().Add(1*Hour).Before(Now()) = true, want false") + } + if !now.Before(tn1) { + t.Errorf("Now().Before(Now().Add(1*Hour)) = false, want true") + } +} + +func TestMonotonicSub(t *testing.T) { + t1 := Unix(1483228799, 995e6) + SetMono(&t1, 123456789012345) + + t2 := Unix(1483228799, 5e6) + SetMono(&t2, 123456789012345+10e6) + + t3 := Unix(1483228799, 995e6) + SetMono(&t3, 123456789012345+1e9) + + t1w := t1.AddDate(0, 0, 0) + if GetMono(&t1w) != 0 { + t.Fatalf("AddDate didn't strip monotonic clock reading") + } + t2w := t2.AddDate(0, 0, 0) + if GetMono(&t2w) != 0 { + t.Fatalf("AddDate didn't strip monotonic clock reading") + } + t3w := t3.AddDate(0, 0, 0) + if GetMono(&t3w) != 0 { + t.Fatalf("AddDate didn't strip monotonic clock reading") + } + + sub := func(txs, tys string, tx, txw, ty, tyw Time, d, dw Duration) { + check := func(expr string, d, want Duration) { + if d != want { + t.Errorf("%s = %v, want %v", expr, d, want) + } + } + check(txs+".Sub("+tys+")", tx.Sub(ty), d) + check(txs+"w.Sub("+tys+")", txw.Sub(ty), dw) + check(txs+".Sub("+tys+"w)", tx.Sub(tyw), dw) + check(txs+"w.Sub("+tys+"w)", txw.Sub(tyw), dw) + } + sub("t1", "t1", t1, t1w, t1, t1w, 0, 0) + sub("t1", "t2", t1, t1w, t2, t2w, -10*Millisecond, 990*Millisecond) + sub("t1", "t3", t1, t1w, t3, t3w, -1000*Millisecond, 0) + + sub("t2", "t1", t2, t2w, t1, t1w, 10*Millisecond, -990*Millisecond) + sub("t2", "t2", t2, t2w, t2, t2w, 0, 0) + sub("t2", "t3", t2, t2w, t3, t3w, -990*Millisecond, -990*Millisecond) + + sub("t3", "t1", t3, t3w, t1, t1w, 1000*Millisecond, 0) + sub("t3", "t2", t3, t3w, t2, t2w, 990*Millisecond, 990*Millisecond) + sub("t3", "t3", t3, t3w, t3, t3w, 0, 0) + + cmp := func(txs, tys string, tx, txw, ty, tyw Time, c, cw int) { + check := func(expr string, b, want bool) { + if b != want { + t.Errorf("%s = %v, want %v", expr, b, want) + } + } + check(txs+".After("+tys+")", tx.After(ty), c > 0) + check(txs+"w.After("+tys+")", txw.After(ty), cw > 0) + check(txs+".After("+tys+"w)", tx.After(tyw), cw > 0) + check(txs+"w.After("+tys+"w)", txw.After(tyw), cw > 0) + + check(txs+".Before("+tys+")", tx.Before(ty), c < 0) + check(txs+"w.Before("+tys+")", txw.Before(ty), cw < 0) + check(txs+".Before("+tys+"w)", tx.Before(tyw), cw < 0) + check(txs+"w.Before("+tys+"w)", txw.Before(tyw), cw < 0) + + check(txs+".Equal("+tys+")", tx.Equal(ty), c == 0) + check(txs+"w.Equal("+tys+")", txw.Equal(ty), cw == 0) + check(txs+".Equal("+tys+"w)", tx.Equal(tyw), cw == 0) + check(txs+"w.Equal("+tys+"w)", txw.Equal(tyw), cw == 0) + } + + cmp("t1", "t1", t1, t1w, t1, t1w, 0, 0) + cmp("t1", "t2", t1, t1w, t2, t2w, -1, +1) + cmp("t1", "t3", t1, t1w, t3, t3w, -1, 0) + + cmp("t2", "t1", t2, t2w, t1, t1w, +1, -1) + cmp("t2", "t2", t2, t2w, t2, t2w, 0, 0) + cmp("t2", "t3", t2, t2w, t3, t3w, -1, -1) + + cmp("t3", "t1", t3, t3w, t1, t1w, +1, 0) + cmp("t3", "t2", t3, t3w, t2, t2w, +1, +1) + cmp("t3", "t3", t3, t3w, t3, t3w, 0, 0) +} + +func TestMonotonicOverflow(t *testing.T) { + t1 := Now().Add(-30 * Second) + d := Until(t1) + if d < -35*Second || -30*Second < d { + t.Errorf("Until(Now().Add(-30s)) = %v, want roughly -30s (-35s to -30s)", d) + } + + t1 = Now().Add(30 * Second) + d = Until(t1) + if d < 25*Second || 30*Second < d { + t.Errorf("Until(Now().Add(-30s)) = %v, want roughly 30s (25s to 30s)", d) + } + + t0 := Now() + t1 = t0.Add(Duration(1<<63 - 1)) + if GetMono(&t1) != 0 { + t.Errorf("Now().Add(maxDuration) has monotonic clock reading (%v => %v %d %d)", t0.String(), t1.String(), t0.Unix(), t1.Unix()) + } + t2 := t1.Add(-Duration(1<<63 - 1)) + d = Since(t2) + if d < -10*Second || 10*Second < d { + t.Errorf("Since(Now().Add(max).Add(-max)) = %v, want [-10s, 10s]", d) + } + + t0 = Now() + t1 = t0.Add(1 * Hour) + Sleep(100 * Millisecond) + t2 = Now().Add(-5 * Second) + if !t1.After(t2) { + t.Errorf("Now().Add(1*Hour).After(Now().Add(-5*Second)) = false, want true\nt1=%v\nt2=%v", t1, t2) + } + if t2.After(t1) { + t.Errorf("Now().Add(-5*Second).After(Now().Add(1*Hour)) = true, want false\nt1=%v\nt2=%v", t1, t2) + } + if t1.Before(t2) { + t.Errorf("Now().Add(1*Hour).Before(Now().Add(-5*Second)) = true, want false\nt1=%v\nt2=%v", t1, t2) + } + if !t2.Before(t1) { + t.Errorf("Now().Add(-5*Second).Before(Now().Add(1*Hour)) = false, want true\nt1=%v\nt2=%v", t1, t2) + } +} + +var monotonicStringTests = []struct { + mono int64 + want string +}{ + {0, "m=+0.000000000"}, + {123456789, "m=+0.123456789"}, + {-123456789, "m=-0.123456789"}, + {123456789000, "m=+123.456789000"}, + {-123456789000, "m=-123.456789000"}, + {9e18, "m=+9000000000.000000000"}, + {-9e18, "m=-9000000000.000000000"}, + {-1 << 63, "m=-9223372036.854775808"}, +} + +func TestMonotonicString(t *testing.T) { + t1 := Now() + t.Logf("Now() = %v", t1) + + for _, tt := range monotonicStringTests { + t1 := Now() + SetMono(&t1, tt.mono) + s := t1.String() + got := s[strings.LastIndex(s, " ")+1:] + if got != tt.want { + t.Errorf("with mono=%d: got %q; want %q", tt.mono, got, tt.want) + } + } +} diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go index c286bd0..546b28a 100644 --- a/libgo/go/time/sleep_test.go +++ b/libgo/go/time/sleep_test.go @@ -227,7 +227,7 @@ func TestAfterQueuing(t *testing.T) { err := errors.New("!=nil") for i := 0; i < attempts && err != nil; i++ { delta := Duration(20+i*50) * Millisecond - if err = testAfterQueuing(t, delta); err != nil { + if err = testAfterQueuing(delta); err != nil { t.Logf("attempt %v failed: %v", i, err) } } @@ -248,7 +248,7 @@ func await(slot int, result chan<- afterResult, ac <-chan Time) { result <- afterResult{slot, <-ac} } -func testAfterQueuing(t *testing.T, delta Duration) error { +func testAfterQueuing(delta Duration) error { // make the result channel buffered because we don't want // to depend on channel queueing semantics that might // possibly change in the future. diff --git a/libgo/go/time/sys_plan9.go b/libgo/go/time/sys_plan9.go index 11365a7..9086a6e 100644 --- a/libgo/go/time/sys_plan9.go +++ b/libgo/go/time/sys_plan9.go @@ -19,6 +19,7 @@ func interrupt() { // readFile reads and returns the content of the named file. // It is a trivial implementation of ioutil.ReadFile, reimplemented // here to avoid depending on io/ioutil or os. +// It returns an error if name exceeds maxFileSize bytes. func readFile(name string) ([]byte, error) { f, err := syscall.Open(name, syscall.O_RDONLY) if err != nil { @@ -38,6 +39,9 @@ func readFile(name string) ([]byte, error) { if n == 0 || err != nil { break } + if len(ret) > maxFileSize { + return nil, fileSizeError(name) + } } return ret, err } diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go index 4c68bbd..5715275 100644 --- a/libgo/go/time/sys_unix.go +++ b/libgo/go/time/sys_unix.go @@ -19,6 +19,7 @@ func interrupt() { // readFile reads and returns the content of the named file. // It is a trivial implementation of ioutil.ReadFile, reimplemented // here to avoid depending on io/ioutil or os. +// It returns an error if name exceeds maxFileSize bytes. func readFile(name string) ([]byte, error) { f, err := syscall.Open(name, syscall.O_RDONLY, 0) if err != nil { @@ -38,6 +39,9 @@ func readFile(name string) ([]byte, error) { if n == 0 || err != nil { break } + if len(ret) > maxFileSize { + return nil, fileSizeError(name) + } } return ret, err } diff --git a/libgo/go/time/sys_windows.go b/libgo/go/time/sys_windows.go index a4a068f..9e38165 100644 --- a/libgo/go/time/sys_windows.go +++ b/libgo/go/time/sys_windows.go @@ -16,6 +16,7 @@ func interrupt() { // readFile reads and returns the content of the named file. // It is a trivial implementation of ioutil.ReadFile, reimplemented // here to avoid depending on io/ioutil or os. +// It returns an error if name exceeds maxFileSize bytes. func readFile(name string) ([]byte, error) { f, err := syscall.Open(name, syscall.O_RDONLY, 0) if err != nil { @@ -35,6 +36,9 @@ func readFile(name string) ([]byte, error) { if n == 0 || err != nil { break } + if len(ret) > maxFileSize { + return nil, fileSizeError(name) + } } return ret, err } diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index 10b3246..8a29eef 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -6,6 +6,69 @@ // // The calendrical calculations always assume a Gregorian calendar, with // no leap seconds. +// +// Monotonic Clocks +// +// Operating systems provide both a “wall clock,” which is subject to +// changes for clock synchronization, and a “monotonic clock,” which is +// not. The general rule is that the wall clock is for telling time and +// the monotonic clock is for measuring time. Rather than split the API, +// in this package the Time returned by time.Now contains both a wall +// clock reading and a monotonic clock reading; later time-telling +// operations use the wall clock reading, but later time-measuring +// operations, specifically comparisons and subtractions, use the +// monotonic clock reading. +// +// For example, this code always computes a positive elapsed time of +// approximately 20 milliseconds, even if the wall clock is changed during +// the operation being timed: +// +// start := time.Now() +// ... operation that takes 20 milliseconds ... +// t := time.Now() +// elapsed := t.Sub(start) +// +// Other idioms, such as time.Since(start), time.Until(deadline), and +// time.Now().Before(deadline), are similarly robust against wall clock +// resets. +// +// The rest of this section gives the precise details of how operations +// use monotonic clocks, but understanding those details is not required +// to use this package. +// +// The Time returned by time.Now contains a monotonic clock reading. +// If Time t has a monotonic clock reading, t.Add adds the same duration to +// both the wall clock and monotonic clock readings to compute the result. +// Because t.AddDate(y, m, d), t.Round(d), and t.Truncate(d) are wall time +// computations, they always strip any monotonic clock reading from their results. +// Because t.In, t.Local, and t.UTC are used for their effect on the interpretation +// of the wall time, they also strip any monotonic clock reading from their results. +// The canonical way to strip a monotonic clock reading is to use t = t.Round(0). +// +// If Times t and u both contain monotonic clock readings, the operations +// t.After(u), t.Before(u), t.Equal(u), and t.Sub(u) are carried out +// using the monotonic clock readings alone, ignoring the wall clock +// readings. If either t or u contains no monotonic clock reading, these +// operations fall back to using the wall clock readings. +// +// Because the monotonic clock reading has no meaning outside +// the current process, the serialized forms generated by t.GobEncode, +// t.MarshalBinary, t.MarshalJSON, and t.MarshalText omit the monotonic +// clock reading, and t.Format provides no format for it. Similarly, the +// constructors time.Date, time.Parse, time.ParseInLocation, and time.Unix, +// as well as the unmarshalers t.GobDecode, t.UnmarshalBinary. +// t.UnmarshalJSON, and t.UnmarshalText always create times with +// no monotonic clock reading. +// +// Note that the Go == operator compares not just the time instant but +// also the Location and the monotonic clock reading. See the +// documentation for the Time type for a discussion of equality +// testing for Time values. +// +// For debugging, the result of t.String does include the monotonic +// clock reading if present. If t != u because of different monotonic clock readings, +// that difference will be visible when printing t.String() and u.String(). +// package time import "errors" @@ -14,8 +77,11 @@ import "errors" // // Programs using times should typically store and pass them as values, // not pointers. That is, time variables and struct fields should be of -// type time.Time, not *time.Time. A Time value can be used by -// multiple goroutines simultaneously. +// type time.Time, not *time.Time. +// +// A Time value can be used by multiple goroutines simultaneously except +// that the methods GobDecode, UnmarshalBinary, UnmarshalJSON and +// UnmarshalText are not concurrency-safe. // // Time instants can be compared using the Before, After, and Equal methods. // The Sub method subtracts two instants, producing a Duration. @@ -33,19 +99,34 @@ import "errors" // computations described in earlier paragraphs. // // Note that the Go == operator compares not just the time instant but also the -// Location. Therefore, Time values should not be used as map or database keys -// without first guaranteeing that the identical Location has been set for all -// values, which can be achieved through use of the UTC or Local method. +// Location and the monotonic clock reading. Therefore, Time values should not +// be used as map or database keys without first guaranteeing that the +// identical Location has been set for all values, which can be achieved +// through use of the UTC or Local method, and that the monotonic clock reading +// has been stripped by setting t = t.Round(0). In general, prefer t.Equal(u) +// to t == u, since t.Equal uses the most accurate comparison available and +// correctly handles the case when only one of its arguments has a monotonic +// clock reading. +// +// In addition to the required “wall clock” reading, a Time may contain an optional +// reading of the current process's monotonic clock, to provide additional precision +// for comparison or subtraction. +// See the “Monotonic Clocks” section in the package documentation for details. // type Time struct { - // sec gives the number of seconds elapsed since - // January 1, year 1 00:00:00 UTC. - sec int64 - - // nsec specifies a non-negative nanosecond - // offset within the second named by Seconds. - // It must be in the range [0, 999999999]. - nsec int32 + // wall and ext encode the wall time seconds, wall time nanoseconds, + // and optional monotonic clock reading in nanoseconds. + // + // From high to low bit position, wall encodes a 1-bit flag (hasMonotonic), + // a 33-bit seconds field, and a 30-bit wall time nanoseconds field. + // The nanoseconds field is in the range [0, 999999999]. + // If the hasMonotonic bit is 0, then the 33-bit field must be zero + // and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext. + // If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit + // unsigned wall seconds since Jan 1 year 1885, and ext holds a + // signed 64-bit monotonic clock reading, nanoseconds since process start. + wall uint64 + ext int64 // loc specifies the Location that should be used to // determine the minute, hour, month, day, and year @@ -55,29 +136,124 @@ type Time struct { loc *Location } +const ( + hasMonotonic = 1 << 63 + maxWall = wallToInternal + (1<<33 - 1) // year 2157 + minWall = wallToInternal // year 1885 + nsecMask = 1<<30 - 1 + nsecShift = 30 +) + +// These helpers for manipulating the wall and monotonic clock readings +// take pointer receivers, even when they don't modify the time, +// to make them cheaper to call. + +// nsec returns the time's nanoseconds. +func (t *Time) nsec() int32 { + return int32(t.wall & nsecMask) +} + +// sec returns the time's seconds since Jan 1 year 1. +func (t *Time) sec() int64 { + if t.wall&hasMonotonic != 0 { + return wallToInternal + int64(t.wall<<1>>(nsecShift+1)) + } + return int64(t.ext) +} + +// unixSec returns the time's seconds since Jan 1 1970 (Unix time). +func (t *Time) unixSec() int64 { return t.sec() + internalToUnix } + +// addSec adds d seconds to the time. +func (t *Time) addSec(d int64) { + if t.wall&hasMonotonic != 0 { + sec := int64(t.wall << 1 >> (nsecShift + 1)) + dsec := sec + d + if 0 <= dsec && dsec <= 1<<33-1 { + t.wall = t.wall&nsecMask | uint64(dsec)<<nsecShift | hasMonotonic + return + } + // Wall second now out of range for packed field. + // Move to ext. + t.stripMono() + } + + // TODO: Check for overflow. + t.ext += d +} + +// setLoc sets the location associated with the time. func (t *Time) setLoc(loc *Location) { if loc == &utcLoc { loc = nil } + t.stripMono() t.loc = loc } +// stripMono strips the monotonic clock reading in t. +func (t *Time) stripMono() { + if t.wall&hasMonotonic != 0 { + t.ext = t.sec() + t.wall &= nsecMask + } +} + +// setMono sets the monotonic clock reading in t. +// If t cannot hold a monotonic clock reading, +// because its wall time is too large, +// setMono is a no-op. +func (t *Time) setMono(m int64) { + if t.wall&hasMonotonic == 0 { + sec := int64(t.ext) + if sec < minWall || maxWall < sec { + return + } + t.wall |= hasMonotonic | uint64(sec-minWall)<<nsecShift + } + t.ext = m +} + +// mono returns t's monotonic clock reading. +// It returns 0 for a missing reading. +// This function is used only for testing, +// so it's OK that technically 0 is a valid +// monotonic clock reading as well. +func (t *Time) mono() int64 { + if t.wall&hasMonotonic == 0 { + return 0 + } + return t.ext +} + // After reports whether the time instant t is after u. func (t Time) After(u Time) bool { - return t.sec > u.sec || t.sec == u.sec && t.nsec > u.nsec + if t.wall&u.wall&hasMonotonic != 0 { + return t.ext > u.ext + } + ts := t.sec() + us := u.sec() + return ts > us || ts == us && t.nsec() > u.nsec() } // Before reports whether the time instant t is before u. func (t Time) Before(u Time) bool { - return t.sec < u.sec || t.sec == u.sec && t.nsec < u.nsec + if t.wall&u.wall&hasMonotonic != 0 { + return t.ext < u.ext + } + return t.sec() < u.sec() || t.sec() == u.sec() && t.nsec() < u.nsec() } // Equal reports whether t and u represent the same time instant. // Two times can be equal even if they are in different locations. // For example, 6:00 +0200 CEST and 4:00 UTC are Equal. -// Do not use == with Time values. +// See the documentation on the Time type for the pitfalls of using == with +// Time values; most code should use Equal instead. func (t Time) Equal(u Time) bool { - return t.sec == u.sec && t.nsec == u.nsec + if t.wall&u.wall&hasMonotonic != 0 { + return t.ext == u.ext + } + return t.sec() == u.sec() && t.nsec() == u.nsec() } // A Month specifies a month of the year (January = 1, ...). @@ -162,7 +338,7 @@ func (d Weekday) String() string { return days[d] } // The zero Time value does not force a specific epoch for the time // representation. For example, to use the Unix epoch internally, we // could define that to distinguish a zero value from Jan 1 1970, that -// time would be represented by sec=-1, nsec=1e9. However, it does +// time would be represented by sec=-1, nsec=1e9. However, it does // suggest a representation, namely using 1-1-1 00:00:00 UTC as the // epoch, and that's what we do. // @@ -194,7 +370,7 @@ func (d Weekday) String() string { return days[d] } // everywhere. // // The calendar runs on an exact 400 year cycle: a 400-year calendar -// printed for 1970-2469 will apply as well to 2370-2769. Even the days +// printed for 1970-2369 will apply as well to 2370-2769. Even the days // of the week match up. It simplifies the computations to choose the // cycle boundaries so that the exceptional years are always delayed as // long as possible. That means choosing a year equal to 1 mod 400, so @@ -208,7 +384,7 @@ func (d Weekday) String() string { return days[d] } // // These three considerations—choose an epoch as early as possible, that // uses a year equal to 1 mod 400, and that is no more than 2⁶³ seconds -// earlier than 1970—bring us to the year -292277022399. We refer to +// earlier than 1970—bring us to the year -292277022399. We refer to // this year as the absolute zero year, and to times measured as a uint64 // seconds since this year as absolute times. // @@ -219,9 +395,9 @@ func (d Weekday) String() string { return days[d] } // times. // // It is tempting to just use the year 1 as the absolute epoch, defining -// that the routines are only valid for years >= 1. However, the +// that the routines are only valid for years >= 1. However, the // routines would then be invalid when displaying the epoch in time zones -// west of UTC, since it is year 0. It doesn't seem tenable to say that +// west of UTC, since it is year 0. It doesn't seem tenable to say that // printing the zero time correctly isn't supported in half the time // zones. By comparison, it's reasonable to mishandle some times in // the year -292277022399. @@ -245,12 +421,15 @@ const ( unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay internalToUnix int64 = -unixToInternal + + wallToInternal int64 = (1884*365 + 1884/4 - 1884/100 + 1884/400) * secondsPerDay + internalToWall int64 = -wallToInternal ) // IsZero reports whether t represents the zero time instant, // January 1, year 1, 00:00:00 UTC. func (t Time) IsZero() bool { - return t.sec == 0 && t.nsec == 0 + return t.sec() == 0 && t.nsec() == 0 } // abs returns the time t as an absolute time, adjusted by the zone offset. @@ -261,7 +440,7 @@ func (t Time) abs() uint64 { if l == nil || l == &localLoc { l = l.get() } - sec := t.sec + internalToUnix + sec := t.unixSec() if l != &utcLoc { if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd { sec += int64(l.cacheZone.offset) @@ -281,7 +460,7 @@ func (t Time) locabs() (name string, offset int, abs uint64) { l = l.get() } // Avoid function call if we hit the local time cache. - sec := t.sec + internalToUnix + sec := t.unixSec() if l != &utcLoc { if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd { name = l.cacheZone.name @@ -425,7 +604,7 @@ func (t Time) Second() int { // Nanosecond returns the nanosecond offset within the second specified by t, // in the range [0, 999999999]. func (t Time) Nanosecond() int { - return int(t.nsec) + return int(t.nsec()) } // YearDay returns the day of the year specified by t, in the range [1,365] for non-leap years, @@ -543,8 +722,8 @@ func (d Duration) String() string { } // fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the -// tail of buf, omitting trailing zeros. it omits the decimal -// point too when the fraction is 0. It returns the index where the +// tail of buf, omitting trailing zeros. it omits the decimal +// point too when the fraction is 0. It returns the index where the // output bytes begin and the value v/10**prec. func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) { // Omit trailing zeros up to and including decimal point. @@ -616,18 +795,73 @@ func (d Duration) Hours() float64 { return float64(hour) + float64(nsec)/(60*60*1e9) } +// Truncate returns the result of rounding d toward zero to a multiple of m. +// If m <= 0, Truncate returns d unchanged. +func (d Duration) Truncate(m Duration) Duration { + if m <= 0 { + return d + } + return d - d%m +} + +// lessThanHalf reports whether x+x < y but avoids overflow, +// assuming x and y are both positive (Duration is signed). +func lessThanHalf(x, y Duration) bool { + return uint64(x)+uint64(x) < uint64(y) +} + +// Round returns the result of rounding d to the nearest multiple of m. +// The rounding behavior for halfway values is to round away from zero. +// If the result exceeds the maximum (or minimum) +// value that can be stored in a Duration, +// Round returns the maximum (or minimum) duration. +// If m <= 0, Round returns d unchanged. +func (d Duration) Round(m Duration) Duration { + if m <= 0 { + return d + } + r := d % m + if d < 0 { + r = -r + if lessThanHalf(r, m) { + return d + r + } + if d1 := d - m + r; d1 < d { + return d1 + } + return minDuration // overflow + } + if lessThanHalf(r, m) { + return d - r + } + if d1 := d + m - r; d1 > d { + return d1 + } + return maxDuration // overflow +} + // Add returns the time t+d. func (t Time) Add(d Duration) Time { - t.sec += int64(d / 1e9) - nsec := t.nsec + int32(d%1e9) + dsec := int64(d / 1e9) + nsec := t.nsec() + int32(d%1e9) if nsec >= 1e9 { - t.sec++ + dsec++ nsec -= 1e9 } else if nsec < 0 { - t.sec-- + dsec-- nsec += 1e9 } - t.nsec = nsec + t.wall = t.wall&^nsecMask | uint64(nsec) // update nsec + t.addSec(dsec) + if t.wall&hasMonotonic != 0 { + te := t.ext + int64(d) + if d < 0 && te > int64(t.ext) || d > 0 && te < int64(t.ext) { + // Monotonic clock reading now out of range; degrade to wall-only. + t.stripMono() + } else { + t.ext = te + } + } return t } @@ -636,7 +870,19 @@ func (t Time) Add(d Duration) Time { // will be returned. // To compute t-d for a duration d, use t.Add(-d). func (t Time) Sub(u Time) Duration { - d := Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec) + if t.wall&u.wall&hasMonotonic != 0 { + te := int64(t.ext) + ue := int64(u.ext) + d := Duration(te - ue) + if d < 0 && te > ue { + return maxDuration // t - u is positive out of range + } + if d > 0 && te < ue { + return minDuration // t - u is negative out of range + } + return d + } + d := Duration(t.sec()-u.sec())*Second + Duration(t.nsec()-u.nsec()) // Check for overflow or underflow. switch { case u.Add(d).Equal(t): @@ -671,7 +917,7 @@ func Until(t Time) Duration { func (t Time) AddDate(years int, months int, days int) Time { year, month, day := t.Date() hour, min, sec := t.Clock() - return Date(year+years, month+Month(months), day+days, hour, min, sec, int(t.nsec), t.Location()) + return Date(year+years, month+Month(months), day+days, hour, min, sec, int(t.nsec()), t.Location()) } const ( @@ -718,7 +964,7 @@ func absDate(abs uint64, full bool) (year int, month Month, day int, yday int) { // Cut off years within a 4-year cycle. // The last year is a leap year, so on the last day of that year, - // day / 365 will be 4 instead of 3. Cut it back down to 3 + // day / 365 will be 4 instead of 3. Cut it back down to 3 // by subtracting n>>2. n = d / 365 n -= n >> 2 @@ -791,12 +1037,20 @@ func daysIn(m Month, year int) int { } // Provided by package runtime. -func now() (sec int64, nsec int32) +func now() (sec int64, nsec int32, mono int64) // Now returns the current local time. func Now() Time { - sec, nsec := now() - return Time{sec + unixToInternal, nsec, Local} + sec, nsec, mono := now() + 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} +} + +func unixTime(sec int64, nsec int32) Time { + return Time{uint64(nsec), sec + unixToInternal, Local} } // UTC returns t with the location set to UTC. @@ -834,14 +1088,14 @@ func (t Time) Location() *Location { // Zone computes the time zone in effect at time t, returning the abbreviated // name of the zone (such as "CET") and its offset in seconds east of UTC. func (t Time) Zone() (name string, offset int) { - name, offset, _, _, _ = t.loc.lookup(t.sec + internalToUnix) + name, offset, _, _, _ = t.loc.lookup(t.unixSec()) return } // Unix returns t as a Unix time, the number of seconds elapsed // since January 1, 1970 UTC. func (t Time) Unix() int64 { - return t.sec + internalToUnix + return t.unixSec() } // UnixNano returns t as a Unix time, the number of nanoseconds elapsed @@ -850,7 +1104,7 @@ func (t Time) Unix() int64 { // 1678 or after 2262). Note that this means the result of calling UnixNano // on the zero Time is undefined. func (t Time) UnixNano() int64 { - return (t.sec+internalToUnix)*1e9 + int64(t.nsec) + return (t.unixSec())*1e9 + int64(t.nsec()) } const timeBinaryVersion byte = 1 @@ -873,20 +1127,22 @@ func (t Time) MarshalBinary() ([]byte, error) { offsetMin = int16(offset) } + sec := t.sec() + nsec := t.nsec() enc := []byte{ timeBinaryVersion, // byte 0 : version - byte(t.sec >> 56), // bytes 1-8: seconds - byte(t.sec >> 48), - byte(t.sec >> 40), - byte(t.sec >> 32), - byte(t.sec >> 24), - byte(t.sec >> 16), - byte(t.sec >> 8), - byte(t.sec), - byte(t.nsec >> 24), // bytes 9-12: nanoseconds - byte(t.nsec >> 16), - byte(t.nsec >> 8), - byte(t.nsec), + byte(sec >> 56), // bytes 1-8: seconds + byte(sec >> 48), + byte(sec >> 40), + byte(sec >> 32), + byte(sec >> 24), + byte(sec >> 16), + byte(sec >> 8), + byte(sec), + byte(nsec >> 24), // bytes 9-12: nanoseconds + byte(nsec >> 16), + byte(nsec >> 8), + byte(nsec), byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes byte(offsetMin), } @@ -910,18 +1166,22 @@ func (t *Time) UnmarshalBinary(data []byte) error { } buf = buf[1:] - t.sec = int64(buf[7]) | int64(buf[6])<<8 | int64(buf[5])<<16 | int64(buf[4])<<24 | + sec := int64(buf[7]) | int64(buf[6])<<8 | int64(buf[5])<<16 | int64(buf[4])<<24 | int64(buf[3])<<32 | int64(buf[2])<<40 | int64(buf[1])<<48 | int64(buf[0])<<56 buf = buf[8:] - t.nsec = int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24 + nsec := int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24 buf = buf[4:] offset := int(int16(buf[1])|int16(buf[0])<<8) * 60 + *t = Time{} + t.wall = uint64(nsec) + t.ext = sec + if offset == -1*60 { t.setLoc(&utcLoc) - } else if _, localoff, _, _, _ := Local.lookup(t.sec + internalToUnix); offset == localoff { + } else if _, localoff, _, _, _ := Local.lookup(t.unixSec()); offset == localoff { t.setLoc(Local) } else { t.setLoc(FixedZone("", offset)) @@ -1008,7 +1268,7 @@ func Unix(sec int64, nsec int64) Time { sec-- } } - return Time{sec + unixToInternal, int32(nsec), Local} + return unixTime(sec, int32(nsec)) } func isLeap(year int) bool { @@ -1117,7 +1377,7 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T unix -= int64(offset) } - t := Time{unix + unixToInternal, int32(nsec), nil} + t := unixTime(unix, int32(nsec)) t.setLoc(loc) return t } @@ -1130,6 +1390,7 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T // time. Thus, Truncate(Hour) may return a time with a non-zero // minute, depending on the time's Location. func (t Time) Truncate(d Duration) Time { + t.stripMono() if d <= 0 { return t } @@ -1146,11 +1407,12 @@ func (t Time) Truncate(d Duration) Time { // time. Thus, Round(Hour) may return a time with a non-zero // minute, depending on the time's Location. func (t Time) Round(d Duration) Time { + t.stripMono() if d <= 0 { return t } _, r := div(t, d) - if r+r < d { + if lessThanHalf(r, d) { return t.Add(-r) } return t.Add(d - r) @@ -1161,15 +1423,16 @@ func (t Time) Round(d Duration) Time { // but it's still here in case we change our minds. func div(t Time, d Duration) (qmod2 int, r Duration) { neg := false - nsec := t.nsec - if t.sec < 0 { + nsec := t.nsec() + sec := t.sec() + if sec < 0 { // Operate on absolute value. neg = true - t.sec = -t.sec + sec = -sec nsec = -nsec if nsec < 0 { nsec += 1e9 - t.sec-- // t.sec >= 1 before the -- so safe + sec-- // sec >= 1 before the -- so safe } } @@ -1182,8 +1445,8 @@ func div(t Time, d Duration) (qmod2 int, r Duration) { // Special case: d is a multiple of 1 second. case d%Second == 0: d1 := int64(d / Second) - qmod2 = int(t.sec/d1) & 1 - r = Duration(t.sec%d1)*Second + Duration(nsec) + qmod2 = int(sec/d1) & 1 + r = Duration(sec%d1)*Second + Duration(nsec) // General case. // This could be faster if more cleverness were applied, @@ -1191,7 +1454,7 @@ func div(t Time, d Duration) (qmod2 int, r Duration) { // No one will care about these cases. default: // Compute nanoseconds as 128-bit number. - sec := uint64(t.sec) + sec := uint64(sec) tmp := (sec >> 32) * 1e9 u1 := tmp >> 32 u0 := tmp << 32 diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index 2922560..dba8e0d 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -11,7 +11,9 @@ import ( "fmt" "math/big" "math/rand" + "os" "runtime" + "strings" "testing" "testing/quick" . "time" @@ -231,6 +233,7 @@ var truncateRoundTests = []struct { {Date(-1, January, 1, 12, 15, 31, 5e8, UTC), 3}, {Date(2012, January, 1, 12, 15, 30, 5e8, UTC), Second}, {Date(2012, January, 1, 12, 15, 31, 5e8, UTC), Second}, + {Unix(-19012425939, 649146258), 7435029458905025217}, // 5.8*d rounds to 6*d, but .8*d+.8*d < 0 < d } func TestTruncateRound(t *testing.T) { @@ -1056,6 +1059,66 @@ func TestDurationHours(t *testing.T) { } } +var durationTruncateTests = []struct { + d Duration + m Duration + want Duration +}{ + {0, Second, 0}, + {Minute, -7 * Second, Minute}, + {Minute, 0, Minute}, + {Minute, 1, Minute}, + {Minute + 10*Second, 10 * Second, Minute + 10*Second}, + {2*Minute + 10*Second, Minute, 2 * Minute}, + {10*Minute + 10*Second, 3 * Minute, 9 * Minute}, + {Minute + 10*Second, Minute + 10*Second + 1, 0}, + {Minute + 10*Second, Hour, 0}, + {-Minute, Second, -Minute}, + {-10 * Minute, 3 * Minute, -9 * Minute}, + {-10 * Minute, Hour, 0}, +} + +func TestDurationTruncate(t *testing.T) { + for _, tt := range durationTruncateTests { + if got := tt.d.Truncate(tt.m); got != tt.want { + t.Errorf("Duration(%s).Truncate(%s) = %s; want: %s", tt.d, tt.m, got, tt.want) + } + } +} + +var durationRoundTests = []struct { + d Duration + m Duration + want Duration +}{ + {0, Second, 0}, + {Minute, -11 * Second, Minute}, + {Minute, 0, Minute}, + {Minute, 1, Minute}, + {2 * Minute, Minute, 2 * Minute}, + {2*Minute + 10*Second, Minute, 2 * Minute}, + {2*Minute + 30*Second, Minute, 3 * Minute}, + {2*Minute + 50*Second, Minute, 3 * Minute}, + {-Minute, 1, -Minute}, + {-2 * Minute, Minute, -2 * Minute}, + {-2*Minute - 10*Second, Minute, -2 * Minute}, + {-2*Minute - 30*Second, Minute, -3 * Minute}, + {-2*Minute - 50*Second, Minute, -3 * Minute}, + {8e18, 3e18, 9e18}, + {9e18, 5e18, 1<<63 - 1}, + {-8e18, 3e18, -9e18}, + {-9e18, 5e18, -1 << 63}, + {3<<61 - 1, 3 << 61, 3 << 61}, +} + +func TestDurationRound(t *testing.T) { + for _, tt := range durationRoundTests { + if got := tt.d.Round(tt.m); got != tt.want { + t.Errorf("Duration(%s).Round(%s) = %s; want: %s", tt.d, tt.m, got, tt.want) + } + } +} + var defaultLocTests = []struct { name string f func(t1, t2 Time) bool @@ -1254,3 +1317,14 @@ func TestZeroMonthString(t *testing.T) { t.Errorf("zero month = %q; want %q", got, want) } } + +func TestReadFileLimit(t *testing.T) { + const zero = "/dev/zero" + if _, err := os.Stat(zero); err != nil { + t.Skip("skipping test without a /dev/zero") + } + _, err := ReadFile(zero) + if err == nil || !strings.Contains(err.Error(), "is too large") { + t.Errorf("readFile(%q) error = %v; want error containing 'is too large'", zero, err) + } +} diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go index fb0aa39..f4d4df9 100644 --- a/libgo/go/time/zoneinfo.go +++ b/libgo/go/time/zoneinfo.go @@ -5,6 +5,7 @@ package time import ( + "errors" "sync" "syscall" ) @@ -81,7 +82,7 @@ func (l *Location) get() *Location { } // String returns a descriptive name for the time zone information, -// corresponding to the argument to LoadLocation. +// corresponding to the name argument to LoadLocation or FixedZone. func (l *Location) String() string { return l.get().name } @@ -256,7 +257,10 @@ func (l *Location) lookupName(name string, unix int64) (offset int, isDST bool, // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment // syntax too, but I don't feel like implementing it today. -var zoneinfo, _ = syscall.Getenv("ZONEINFO") +var errLocation = errors.New("time: invalid location name") + +var zoneinfo *string +var zoneinfoOnce sync.Once // LoadLocation returns the Location with the given name. // @@ -279,11 +283,33 @@ func LoadLocation(name string) (*Location, error) { if name == "Local" { return Local, nil } - if zoneinfo != "" { - if z, err := loadZoneFile(zoneinfo, name); err == nil { + if containsDotDot(name) || name[0] == '/' || name[0] == '\\' { + // No valid IANA Time Zone name contains a single dot, + // much less dot dot. Likewise, none begin with a slash. + return nil, errLocation + } + zoneinfoOnce.Do(func() { + env, _ := syscall.Getenv("ZONEINFO") + zoneinfo = &env + }) + if zoneinfo != nil && *zoneinfo != "" { + if z, err := loadZoneFile(*zoneinfo, name); err == nil { z.name = name return z, nil } } return loadLocation(name) } + +// containsDotDot reports whether s contains "..". +func containsDotDot(s string) bool { + if len(s) < 2 { + return false + } + for i := 0; i < len(s)-1; i++ { + if s[i] == '.' && s[i+1] == '.' { + return true + } + } + return false +} diff --git a/libgo/go/time/zoneinfo_abbrs_windows.go b/libgo/go/time/zoneinfo_abbrs_windows.go index 9425db8..db0bbfd 100644 --- a/libgo/go/time/zoneinfo_abbrs_windows.go +++ b/libgo/go/time/zoneinfo_abbrs_windows.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// generated by genzabbrs.go from -// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml +// Code generated by genzabbrs.go; DO NOT EDIT. +// Based on information from http://unicode.org/cldr/data/common/supplemental/windowsZones.xml package time @@ -22,124 +22,128 @@ var abbrs = map[string]abbr{ "Namibia Standard Time": {"WAT", "WAST"}, // Africa/Windhoek "Aleutian Standard Time": {"HST", "HDT"}, // America/Adak "Alaskan Standard Time": {"AKST", "AKDT"}, // America/Anchorage - "Tocantins Standard Time": {"BRT", "BRT"}, // America/Araguaina - "Paraguay Standard Time": {"PYT", "PYST"}, // America/Asuncion - "Bahia Standard Time": {"BRT", "BRT"}, // America/Bahia - "SA Pacific Standard Time": {"COT", "COT"}, // America/Bogota - "Argentina Standard Time": {"ART", "ART"}, // America/Buenos_Aires + "Tocantins Standard Time": {"-03", "-03"}, // America/Araguaina + "Paraguay Standard Time": {"-04", "-03"}, // America/Asuncion + "Bahia Standard Time": {"-03", "-03"}, // America/Bahia + "SA Pacific Standard Time": {"-05", "-05"}, // America/Bogota + "Argentina Standard Time": {"-03", "-03"}, // America/Buenos_Aires "Eastern Standard Time (Mexico)": {"EST", "EST"}, // America/Cancun - "Venezuela Standard Time": {"VET", "VET"}, // America/Caracas - "SA Eastern Standard Time": {"GFT", "GFT"}, // America/Cayenne + "Venezuela Standard Time": {"-04", "-04"}, // America/Caracas + "SA Eastern Standard Time": {"-03", "-03"}, // America/Cayenne "Central Standard Time": {"CST", "CDT"}, // America/Chicago "Mountain Standard Time (Mexico)": {"MST", "MDT"}, // America/Chihuahua - "Central Brazilian Standard Time": {"AMT", "AMST"}, // America/Cuiaba + "Central Brazilian Standard Time": {"-04", "-03"}, // America/Cuiaba "Mountain Standard Time": {"MST", "MDT"}, // America/Denver - "Greenland Standard Time": {"WGT", "WGST"}, // America/Godthab + "Greenland Standard Time": {"-03", "-02"}, // America/Godthab "Turks And Caicos Standard Time": {"AST", "AST"}, // America/Grand_Turk "Central America Standard Time": {"CST", "CST"}, // America/Guatemala "Atlantic Standard Time": {"AST", "ADT"}, // America/Halifax "Cuba Standard Time": {"CST", "CDT"}, // America/Havana "US Eastern Standard Time": {"EST", "EDT"}, // America/Indianapolis - "SA Western Standard Time": {"BOT", "BOT"}, // America/La_Paz + "SA Western Standard Time": {"-04", "-04"}, // America/La_Paz "Pacific Standard Time": {"PST", "PDT"}, // America/Los_Angeles "Central Standard Time (Mexico)": {"CST", "CDT"}, // America/Mexico_City - "Saint Pierre Standard Time": {"PMST", "PMDT"}, // America/Miquelon - "Montevideo Standard Time": {"UYT", "UYT"}, // America/Montevideo + "Saint Pierre Standard Time": {"-03", "-02"}, // America/Miquelon + "Montevideo Standard Time": {"-03", "-03"}, // America/Montevideo "Eastern Standard Time": {"EST", "EDT"}, // America/New_York "US Mountain Standard Time": {"MST", "MST"}, // America/Phoenix - "Haiti Standard Time": {"EST", "EST"}, // America/Port-au-Prince + "Haiti Standard Time": {"EST", "EDT"}, // America/Port-au-Prince + "Magallanes Standard Time": {"-03", "-03"}, // America/Punta_Arenas "Canada Central Standard Time": {"CST", "CST"}, // America/Regina - "Pacific SA Standard Time": {"CLT", "CLST"}, // America/Santiago - "E. South America Standard Time": {"BRT", "BRST"}, // America/Sao_Paulo + "Pacific SA Standard Time": {"-04", "-03"}, // America/Santiago + "E. South America Standard Time": {"-03", "-02"}, // America/Sao_Paulo "Newfoundland Standard Time": {"NST", "NDT"}, // America/St_Johns "Pacific Standard Time (Mexico)": {"PST", "PDT"}, // America/Tijuana "Central Asia Standard Time": {"+06", "+06"}, // Asia/Almaty "Jordan Standard Time": {"EET", "EEST"}, // Asia/Amman - "Arabic Standard Time": {"AST", "AST"}, // Asia/Baghdad - "Azerbaijan Standard Time": {"AZT", "AZT"}, // Asia/Baku - "SE Asia Standard Time": {"ICT", "ICT"}, // Asia/Bangkok - "Altai Standard Time": {"+06", "+07"}, // Asia/Barnaul + "Arabic Standard Time": {"+03", "+03"}, // Asia/Baghdad + "Azerbaijan Standard Time": {"+04", "+04"}, // Asia/Baku + "SE Asia Standard Time": {"+07", "+07"}, // Asia/Bangkok + "Altai Standard Time": {"+07", "+07"}, // Asia/Barnaul "Middle East Standard Time": {"EET", "EEST"}, // Asia/Beirut "India Standard Time": {"IST", "IST"}, // Asia/Calcutta - "Transbaikal Standard Time": {"IRKT", "YAKT"}, // Asia/Chita - "Sri Lanka Standard Time": {"IST", "IST"}, // Asia/Colombo + "Transbaikal Standard Time": {"+09", "+09"}, // Asia/Chita + "Sri Lanka Standard Time": {"+0530", "+0530"}, // Asia/Colombo "Syria Standard Time": {"EET", "EEST"}, // Asia/Damascus - "Bangladesh Standard Time": {"BDT", "BDT"}, // Asia/Dhaka - "Arabian Standard Time": {"GST", "GST"}, // Asia/Dubai + "Bangladesh Standard Time": {"+06", "+06"}, // Asia/Dhaka + "Arabian Standard Time": {"+04", "+04"}, // Asia/Dubai "West Bank Standard Time": {"EET", "EEST"}, // Asia/Hebron - "W. Mongolia Standard Time": {"HOVT", "HOVST"}, // Asia/Hovd - "North Asia East Standard Time": {"IRKT", "IRKT"}, // Asia/Irkutsk + "W. Mongolia Standard Time": {"+07", "+07"}, // Asia/Hovd + "North Asia East Standard Time": {"+08", "+08"}, // Asia/Irkutsk "Israel Standard Time": {"IST", "IDT"}, // Asia/Jerusalem - "Afghanistan Standard Time": {"AFT", "AFT"}, // Asia/Kabul - "Russia Time Zone 11": {"PETT", "PETT"}, // Asia/Kamchatka + "Afghanistan Standard Time": {"+0430", "+0430"}, // Asia/Kabul + "Russia Time Zone 11": {"+12", "+12"}, // Asia/Kamchatka "Pakistan Standard Time": {"PKT", "PKT"}, // Asia/Karachi - "Nepal Standard Time": {"NPT", "NPT"}, // Asia/Katmandu - "North Asia Standard Time": {"KRAT", "KRAT"}, // Asia/Krasnoyarsk - "Magadan Standard Time": {"MAGT", "MAGT"}, // Asia/Magadan - "N. Central Asia Standard Time": {"+06", "+07"}, // Asia/Novosibirsk + "Nepal Standard Time": {"+0545", "+0545"}, // Asia/Katmandu + "North Asia Standard Time": {"+07", "+07"}, // Asia/Krasnoyarsk + "Magadan Standard Time": {"+11", "+11"}, // Asia/Magadan + "N. Central Asia Standard Time": {"+07", "+07"}, // Asia/Novosibirsk + "Omsk Standard Time": {"+06", "+06"}, // Asia/Omsk "North Korea Standard Time": {"KST", "KST"}, // Asia/Pyongyang - "Myanmar Standard Time": {"MMT", "MMT"}, // Asia/Rangoon - "Arab Standard Time": {"AST", "AST"}, // Asia/Riyadh - "Sakhalin Standard Time": {"SAKT", "SAKT"}, // Asia/Sakhalin + "Myanmar Standard Time": {"+0630", "+0630"}, // Asia/Rangoon + "Arab Standard Time": {"+03", "+03"}, // Asia/Riyadh + "Sakhalin Standard Time": {"+11", "+11"}, // Asia/Sakhalin "Korea Standard Time": {"KST", "KST"}, // Asia/Seoul "China Standard Time": {"CST", "CST"}, // Asia/Shanghai - "Singapore Standard Time": {"SGT", "SGT"}, // Asia/Singapore - "Russia Time Zone 10": {"SRET", "SRET"}, // Asia/Srednekolymsk + "Singapore Standard Time": {"+08", "+08"}, // Asia/Singapore + "Russia Time Zone 10": {"+11", "+11"}, // Asia/Srednekolymsk "Taipei Standard Time": {"CST", "CST"}, // Asia/Taipei - "West Asia Standard Time": {"UZT", "UZT"}, // Asia/Tashkent - "Georgian Standard Time": {"GET", "GET"}, // Asia/Tbilisi - "Iran Standard Time": {"IRST", "IRDT"}, // Asia/Tehran + "West Asia Standard Time": {"+05", "+05"}, // Asia/Tashkent + "Georgian Standard Time": {"+04", "+04"}, // Asia/Tbilisi + "Iran Standard Time": {"+0330", "+0430"}, // Asia/Tehran "Tokyo Standard Time": {"JST", "JST"}, // Asia/Tokyo - "Tomsk Standard Time": {"+06", "+07"}, // Asia/Tomsk - "Ulaanbaatar Standard Time": {"ULAT", "ULAST"}, // Asia/Ulaanbaatar - "Vladivostok Standard Time": {"VLAT", "VLAT"}, // Asia/Vladivostok - "Yakutsk Standard Time": {"YAKT", "YAKT"}, // Asia/Yakutsk - "Ekaterinburg Standard Time": {"YEKT", "YEKT"}, // Asia/Yekaterinburg - "Caucasus Standard Time": {"AMT", "AMT"}, // Asia/Yerevan - "Azores Standard Time": {"AZOT", "AZOST"}, // Atlantic/Azores - "Cape Verde Standard Time": {"CVT", "CVT"}, // Atlantic/Cape_Verde + "Tomsk Standard Time": {"+07", "+07"}, // Asia/Tomsk + "Ulaanbaatar Standard Time": {"+08", "+08"}, // Asia/Ulaanbaatar + "Vladivostok Standard Time": {"+10", "+10"}, // Asia/Vladivostok + "Yakutsk Standard Time": {"+09", "+09"}, // Asia/Yakutsk + "Ekaterinburg Standard Time": {"+05", "+05"}, // Asia/Yekaterinburg + "Caucasus Standard Time": {"+04", "+04"}, // Asia/Yerevan + "Azores Standard Time": {"-01", "+00"}, // Atlantic/Azores + "Cape Verde Standard Time": {"-01", "-01"}, // Atlantic/Cape_Verde "Greenwich Standard Time": {"GMT", "GMT"}, // Atlantic/Reykjavik "Cen. Australia Standard Time": {"ACST", "ACDT"}, // Australia/Adelaide "E. Australia Standard Time": {"AEST", "AEST"}, // Australia/Brisbane "AUS Central Standard Time": {"ACST", "ACST"}, // Australia/Darwin - "Aus Central W. Standard Time": {"ACWST", "ACWST"}, // Australia/Eucla + "Aus Central W. Standard Time": {"+0845", "+0845"}, // Australia/Eucla "Tasmania Standard Time": {"AEST", "AEDT"}, // Australia/Hobart - "Lord Howe Standard Time": {"LHST", "LHDT"}, // Australia/Lord_Howe + "Lord Howe Standard Time": {"+1030", "+11"}, // Australia/Lord_Howe "W. Australia Standard Time": {"AWST", "AWST"}, // Australia/Perth "AUS Eastern Standard Time": {"AEST", "AEDT"}, // Australia/Sydney - "UTC": {"GMT", "GMT"}, // Etc/GMT - "UTC-11": {"GMT+11", "GMT+11"}, // Etc/GMT+11 - "Dateline Standard Time": {"GMT+12", "GMT+12"}, // Etc/GMT+12 - "UTC-02": {"GMT+2", "GMT+2"}, // Etc/GMT+2 - "UTC-08": {"GMT+8", "GMT+8"}, // Etc/GMT+8 - "UTC-09": {"GMT+9", "GMT+9"}, // Etc/GMT+9 - "UTC+12": {"GMT-12", "GMT-12"}, // Etc/GMT-12 - "Astrakhan Standard Time": {"+03", "+04"}, // Europe/Astrakhan - "W. Europe Standard Time": {"CET", "CEST"}, // Europe/Berlin - "GTB Standard Time": {"EET", "EEST"}, // Europe/Bucharest - "Central Europe Standard Time": {"CET", "CEST"}, // Europe/Budapest - "E. Europe Standard Time": {"EET", "EEST"}, // Europe/Chisinau - "Turkey Standard Time": {"EET", "EEST"}, // Europe/Istanbul - "Kaliningrad Standard Time": {"EET", "EET"}, // Europe/Kaliningrad - "FLE Standard Time": {"EET", "EEST"}, // Europe/Kiev - "GMT Standard Time": {"GMT", "BST"}, // Europe/London - "Belarus Standard Time": {"MSK", "MSK"}, // Europe/Minsk - "Russian Standard Time": {"MSK", "MSK"}, // Europe/Moscow - "Romance Standard Time": {"CET", "CEST"}, // Europe/Paris - "Russia Time Zone 3": {"SAMT", "SAMT"}, // Europe/Samara - "Central European Standard Time": {"CET", "CEST"}, // Europe/Warsaw - "Mauritius Standard Time": {"MUT", "MUT"}, // Indian/Mauritius - "Samoa Standard Time": {"WSST", "WSDT"}, // Pacific/Apia - "New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland - "Bougainville Standard Time": {"BST", "BST"}, // Pacific/Bougainville - "Chatham Islands Standard Time": {"CHAST", "CHADT"}, // Pacific/Chatham - "Easter Island Standard Time": {"EAST", "EASST"}, // Pacific/Easter - "Fiji Standard Time": {"FJT", "FJST"}, // Pacific/Fiji - "Central Pacific Standard Time": {"SBT", "SBT"}, // Pacific/Guadalcanal - "Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu - "Line Islands Standard Time": {"LINT", "LINT"}, // Pacific/Kiritimati - "Marquesas Standard Time": {"MART", "MART"}, // Pacific/Marquesas - "Norfolk Standard Time": {"NFT", "NFT"}, // Pacific/Norfolk - "West Pacific Standard Time": {"PGT", "PGT"}, // Pacific/Port_Moresby - "Tonga Standard Time": {"TOT", "TOT"}, // Pacific/Tongatapu + "UTC": {"GMT", "GMT"}, // Etc/GMT + "UTC-11": {"-11", "-11"}, // Etc/GMT+11 + "Dateline Standard Time": {"-12", "-12"}, // Etc/GMT+12 + "UTC-02": {"-02", "-02"}, // Etc/GMT+2 + "UTC-08": {"-08", "-08"}, // Etc/GMT+8 + "UTC-09": {"-09", "-09"}, // Etc/GMT+9 + "UTC+12": {"+12", "+12"}, // Etc/GMT-12 + "UTC+13": {"+13", "+13"}, // Etc/GMT-13 + "Astrakhan Standard Time": {"+04", "+04"}, // Europe/Astrakhan + "W. Europe Standard Time": {"CET", "CEST"}, // Europe/Berlin + "GTB Standard Time": {"EET", "EEST"}, // Europe/Bucharest + "Central Europe Standard Time": {"CET", "CEST"}, // Europe/Budapest + "E. Europe Standard Time": {"EET", "EEST"}, // Europe/Chisinau + "Turkey Standard Time": {"+03", "+03"}, // Europe/Istanbul + "Kaliningrad Standard Time": {"EET", "EET"}, // Europe/Kaliningrad + "FLE Standard Time": {"EET", "EEST"}, // Europe/Kiev + "GMT Standard Time": {"GMT", "BST"}, // Europe/London + "Belarus Standard Time": {"+03", "+03"}, // Europe/Minsk + "Russian Standard Time": {"MSK", "MSK"}, // Europe/Moscow + "Romance Standard Time": {"CET", "CEST"}, // Europe/Paris + "Russia Time Zone 3": {"+04", "+04"}, // Europe/Samara + "Saratov Standard Time": {"+03", "+04"}, // Europe/Saratov + "Central European Standard Time": {"CET", "CEST"}, // Europe/Warsaw + "Mauritius Standard Time": {"+04", "+04"}, // Indian/Mauritius + "Samoa Standard Time": {"+13", "+14"}, // Pacific/Apia + "New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland + "Bougainville Standard Time": {"+11", "+11"}, // Pacific/Bougainville + "Chatham Islands Standard Time": {"+1245", "+1345"}, // Pacific/Chatham + "Easter Island Standard Time": {"-06", "-05"}, // Pacific/Easter + "Fiji Standard Time": {"+12", "+13"}, // Pacific/Fiji + "Central Pacific Standard Time": {"+11", "+11"}, // Pacific/Guadalcanal + "Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu + "Line Islands Standard Time": {"+14", "+14"}, // Pacific/Kiritimati + "Marquesas Standard Time": {"-0930", "-0930"}, // Pacific/Marquesas + "Norfolk Standard Time": {"+11", "+11"}, // Pacific/Norfolk + "West Pacific Standard Time": {"+10", "+10"}, // Pacific/Port_Moresby + "Tonga Standard Time": {"+13", "+14"}, // Pacific/Tongatapu } diff --git a/libgo/go/time/zoneinfo_plan9.go b/libgo/go/time/zoneinfo_plan9.go index 0694f0a..26637a1 100644 --- a/libgo/go/time/zoneinfo_plan9.go +++ b/libgo/go/time/zoneinfo_plan9.go @@ -95,7 +95,7 @@ func loadZoneDataPlan9(s string) (l *Location, err error) { // Fill in the cache with information about right now, // since that will be the most common lookup. - sec, _ := now() + 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 diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go index 19cd40d..b0cd9da 100644 --- a/libgo/go/time/zoneinfo_read.go +++ b/libgo/go/time/zoneinfo_read.go @@ -11,6 +11,17 @@ package time import "errors" +// maxFileSize is the max permitted size of files read by readFile. +// As reference, the zoneinfo.zip distributed by Go is ~350 KB, +// so 10MB is overkill. +const maxFileSize = 10 << 20 + +type fileSizeError string + +func (f fileSizeError) Error() string { + return "time: file " + string(f) + " is too large" +} + // Copies of io.Seek* constants to avoid importing "io": const ( seekStart = 0 @@ -188,7 +199,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) { // Fill in the cache with information about right now, // since that will be the most common lookup. - sec, _ := now() + 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 diff --git a/libgo/go/time/zoneinfo_test.go b/libgo/go/time/zoneinfo_test.go index 5b6a4dc..8a4caa0 100644 --- a/libgo/go/time/zoneinfo_test.go +++ b/libgo/go/time/zoneinfo_test.go @@ -5,10 +5,56 @@ package time_test import ( + "fmt" + "os" "testing" "time" ) +func init() { + if time.ZoneinfoForTesting() != nil { + panic(fmt.Errorf("zoneinfo initialized before first LoadLocation")) + } +} + +func TestEnvVarUsage(t *testing.T) { + time.ResetZoneinfoForTesting() + + const testZoneinfo = "foo.zip" + const env = "ZONEINFO" + + defer os.Setenv(env, os.Getenv(env)) + os.Setenv(env, testZoneinfo) + + // Result isn't important, we're testing the side effect of this command + time.LoadLocation("Asia/Jerusalem") + defer time.ResetZoneinfoForTesting() + + if zoneinfo := time.ZoneinfoForTesting(); testZoneinfo != *zoneinfo { + t.Errorf("zoneinfo does not match env variable: got %q want %q", zoneinfo, testZoneinfo) + } +} + +func TestLoadLocationValidatesNames(t *testing.T) { + time.ResetZoneinfoForTesting() + const env = "ZONEINFO" + defer os.Setenv(env, os.Getenv(env)) + os.Setenv(env, "") + + bad := []string{ + "/usr/foo/Foo", + "\\UNC\foo", + "..", + "a..", + } + for _, v := range bad { + _, err := time.LoadLocation(v) + if err != time.ErrLocation { + t.Errorf("LoadLocation(%q) error = %v; want ErrLocation", v, err) + } + } +} + func TestVersion3(t *testing.T) { t.Skip("gccgo does not use the zip file") time.ForceZipFileForTesting(true) @@ -44,8 +90,8 @@ func TestFirstZone(t *testing.T) { { "Pacific/Fakaofo", 1325242799, - "Thu, 29 Dec 2011 23:59:59 -1100 (TKT)", - "Sat, 31 Dec 2011 00:00:00 +1300 (TKT)", + "Thu, 29 Dec 2011 23:59:59 -1100 (-11)", + "Sat, 31 Dec 2011 00:00:00 +1300 (+13)", }, } diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go index a6e227b..c201f4b 100644 --- a/libgo/go/time/zoneinfo_windows.go +++ b/libgo/go/time/zoneinfo_windows.go @@ -132,7 +132,7 @@ func pseudoUnix(year int, d *syscall.Systemtime) int64 { day -= 7 } } - return t.sec + int64(day-1)*secondsPerDay + internalToUnix + return t.sec() + int64(day-1)*secondsPerDay + internalToUnix } func initLocalFromTZI(i *syscall.Timezoneinformation) { |