diff options
| author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-05-02 14:43:35 +0000 |
|---|---|---|
| committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-05-02 14:43:35 +0000 |
| commit | 34efdaf078b01a7387007c4e6bde6db86384c4b7 (patch) | |
| tree | d503eaf41d085669d1481bb46ec038bc866fece6 /libgo/go/time | |
| parent | f733cf303bcdc952c92b81dd62199a40a1f555ec (diff) | |
| download | gcc-tarball-master.tar.gz | |
gcc-7.1.0gcc-7.1.0
Diffstat (limited to 'libgo/go/time')
| -rw-r--r-- | libgo/go/time/example_test.go | 20 | ||||
| -rw-r--r-- | libgo/go/time/export_android_test.go | 12 | ||||
| -rw-r--r-- | libgo/go/time/format.go | 71 | ||||
| -rw-r--r-- | libgo/go/time/format_test.go | 47 | ||||
| -rw-r--r-- | libgo/go/time/genzabbrs.go | 2 | ||||
| -rw-r--r-- | libgo/go/time/sleep.go | 45 | ||||
| -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/tick.go | 3 | ||||
| -rw-r--r-- | libgo/go/time/tick_test.go | 2 | ||||
| -rw-r--r-- | libgo/go/time/time.go | 135 | ||||
| -rw-r--r-- | libgo/go/time/time_test.go | 150 | ||||
| -rw-r--r-- | libgo/go/time/zoneinfo.go | 2 | ||||
| -rw-r--r-- | libgo/go/time/zoneinfo_abbrs_windows.go | 187 | ||||
| -rw-r--r-- | libgo/go/time/zoneinfo_android.go | 119 | ||||
| -rw-r--r-- | libgo/go/time/zoneinfo_android_test.go | 18 | ||||
| -rw-r--r-- | libgo/go/time/zoneinfo_read.go | 11 | ||||
| -rw-r--r-- | libgo/go/time/zoneinfo_test.go | 11 | ||||
| -rw-r--r-- | libgo/go/time/zoneinfo_unix.go | 10 | ||||
| -rw-r--r-- | libgo/go/time/zoneinfo_windows.go | 6 |
21 files changed, 670 insertions, 193 deletions
diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go index f76fdcd4d0..7dc2bb5e7e 100644 --- a/libgo/go/time/example_test.go +++ b/libgo/go/time/example_test.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// 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. @@ -251,20 +251,18 @@ func ExampleTime_Truncate() { 2 * time.Second, time.Minute, 10 * time.Minute, - time.Hour, } for _, d := range trunc { - fmt.Printf("t.Truncate(%6s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999")) + fmt.Printf("t.Truncate(%5s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999")) } // Output: - // t.Truncate( 1ns) = 12:15:30.918273645 - // t.Truncate( 1µs) = 12:15:30.918273 - // t.Truncate( 1ms) = 12:15:30.918 - // t.Truncate( 1s) = 12:15:30 - // t.Truncate( 2s) = 12:15:30 - // t.Truncate( 1m0s) = 12:15:00 - // t.Truncate( 10m0s) = 12:10:00 - // t.Truncate(1h0m0s) = 12:00:00 + // t.Truncate( 1ns) = 12:15:30.918273645 + // t.Truncate( 1µs) = 12:15:30.918273 + // t.Truncate( 1ms) = 12:15:30.918 + // t.Truncate( 1s) = 12:15:30 + // t.Truncate( 2s) = 12:15:30 + // t.Truncate( 1m0s) = 12:15:00 + // t.Truncate(10m0s) = 12:10:00 } diff --git a/libgo/go/time/export_android_test.go b/libgo/go/time/export_android_test.go new file mode 100644 index 0000000000..fa6a058a73 --- /dev/null +++ b/libgo/go/time/export_android_test.go @@ -0,0 +1,12 @@ +// Copyright 2016 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 + +func ForceAndroidTzdataForTest(tzdata bool) { + tzdataPaths = origTzdataPaths + if tzdata { + tzdataPaths = tzdataPaths[:1] + } +} diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index e616feb048..b903e1485c 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -23,7 +23,7 @@ import "errors" // compatibility with fixed-width Unix time formats. // // A decimal point followed by one or more zeros represents a fractional -// second, printed to the given number of decimal places. A decimal point +// second, printed to the given number of decimal places. A decimal point // followed by one or more nines represents a fractional second, printed to // the given number of decimal places, with trailing zeros removed. // When parsing (only), the input may contain a fractional second @@ -37,11 +37,18 @@ import "errors" // -07 ±hh // Replacing the sign in the format with a Z triggers // the ISO 8601 behavior of printing Z instead of an -// offset for the UTC zone. Thus: +// offset for the UTC zone. Thus: // Z0700 Z or ±hhmm // Z07:00 Z or ±hh:mm // Z07 Z or ±hh // +// The recognized day of week formats are "Mon" and "Monday". +// The recognized month formats are "Jan" and "January". +// +// Text in the format string that is not recognized as part of the reference +// time is echoed verbatim during Format and expected to appear verbatim +// in the input to Parse. +// // The executable example for time.Format demonstrates the working // of the layout string in detail and is a good reference. // @@ -51,6 +58,9 @@ import "errors" // use of "GMT" in that case. // In general RFC1123Z should be used instead of RFC1123 for servers // that insist on that format, and RFC3339 should be preferred for new protocols. +// 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. const ( ANSIC = "Mon Jan _2 15:04:05 2006" UnixDate = "Mon Jan _2 15:04:05 MST 2006" @@ -551,7 +561,7 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { b = append(b, "am"...) } case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumShortTZ, stdNumColonSecondsTZ: - // Ugly special case. We cheat and take the "Z" variants + // Ugly special case. We cheat and take the "Z" variants // to mean "the time zone as formatted for ISO 8601". if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ShortTZ || std == stdISO8601ColonSecondsTZ) { b = append(b, 'Z') @@ -841,6 +851,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) sec, value, err = getnum(value, std == stdZeroSecond) if sec < 0 || 60 <= sec { rangeErrString = "second" + break } // Special case: do we have a fractional second but no // fractional second in the format? @@ -1001,7 +1012,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) } // Validate the day of the month. - if day > daysIn(Month(month), year) { + if day < 1 || day > daysIn(Month(month), year) { return Time{}, &ParseError{alayout, avalue, "", value, ": day out of range"} } @@ -1017,12 +1028,12 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) // If that zone was in effect at the given time, use it. name, offset, _, _, _ := local.lookup(t.sec + internalToUnix) if offset == zoneOffset && (zoneName == "" || name == zoneName) { - t.loc = local + t.setLoc(local) return t, nil } // Otherwise create fake zone to record offset. - t.loc = FixedZone(zoneName, zoneOffset) + t.setLoc(FixedZone(zoneName, zoneOffset)) return t, nil } @@ -1033,7 +1044,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) offset, _, ok := local.lookupName(zoneName, t.sec+internalToUnix) if ok { t.sec -= int64(offset) - t.loc = local + t.setLoc(local) return t, nil } @@ -1042,7 +1053,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT. offset *= 3600 } - t.loc = FixedZone(zoneName, offset) + t.setLoc(FixedZone(zoneName, offset)) return t, nil } @@ -1090,8 +1101,9 @@ func parseTimeZone(value string) (length int, ok bool) { if value[4] == 'T' { return 5, true } - case 4: // Must end in T to match. - if value[3] == 'T' { + case 4: + // Must end in T, except one special case. + if value[3] == 'T' || value[:4] == "WITA" { return 4, true } case 3: @@ -1170,6 +1182,37 @@ func leadingInt(s string) (x int64, rem string, err error) { return x, s[i:], nil } +// leadingFraction consumes the leading [0-9]* from s. +// It is used only for fractions, so does not return an error on overflow, +// it just stops accumulating precision. +func leadingFraction(s string) (x int64, scale float64, rem string) { + i := 0 + scale = 1 + overflow := false + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + if overflow { + continue + } + if x > (1<<63-1)/10 { + // It's possible for overflow to give a positive number, so take care. + overflow = true + continue + } + y := x*10 + int64(c) - '0' + if y < 0 { + overflow = true + continue + } + x = y + scale *= 10 + } + return x, scale, s[i:] +} + var unitMap = map[string]int64{ "ns": int64(Nanosecond), "us": int64(Microsecond), @@ -1232,13 +1275,7 @@ func ParseDuration(s string) (Duration, error) { if s != "" && s[0] == '.' { s = s[1:] pl := len(s) - f, s, err = leadingInt(s) - if err != nil { - return 0, errors.New("time: invalid duration " + orig) - } - for n := pl - len(s); n > 0; n-- { - scale *= 10 - } + f, scale, s = leadingFraction(s) post = pl != len(s) } if !pre && !post { diff --git a/libgo/go/time/format_test.go b/libgo/go/time/format_test.go index a2592e2ceb..0e4a417430 100644 --- a/libgo/go/time/format_test.go +++ b/libgo/go/time/format_test.go @@ -224,6 +224,7 @@ var dayOutOfRangeTests = []struct { {"Thu Nov 31 21:00:57 2010", false}, {"Thu Dec 31 21:00:57 2010", true}, {"Thu Dec 32 21:00:57 2010", false}, + {"Thu Dec 00 21:00:57 2010", false}, } func TestParseDayOutOfRange(t *testing.T) { @@ -244,27 +245,45 @@ func TestParseDayOutOfRange(t *testing.T) { } } +// TestParseInLocation checks that the Parse and ParseInLocation +// functions do not get confused by the fact that AST (Arabia Standard +// Time) and AST (Atlantic Standard Time) are different time zones, +// even though they have the same abbreviation. +// +// ICANN has been slowly phasing out invented abbreviation in favor of +// numeric time zones (for example, the Asia/Baghdad time zone +// abbreviation got changed from AST to +03 in the 2017a tzdata +// release); but we still want to make sure that the time package does +// not get confused on systems with slightly older tzdata packages. func TestParseInLocation(t *testing.T) { - // Check that Parse (and ParseInLocation) understand that - // Feb 01 AST (Arabia Standard Time) and Feb 01 AST (Atlantic Standard Time) - // are in different time zones even though both are called AST baghdad, err := LoadLocation("Asia/Baghdad") if err != nil { t.Fatal(err) } - t1, err := ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", baghdad) + var t1, t2 Time + + t1, err = ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", baghdad) if err != nil { t.Fatal(err) } - t2 := Date(2013, February, 1, 00, 00, 00, 0, baghdad) - if t1 != t2 { - t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad) = %v, want %v", t1, t2) - } + _, offset := t1.Zone() - if offset != 3*60*60 { - t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad).Zone = _, %d, want _, %d", offset, 3*60*60) + + // A zero offset means that ParseInLocation did not recognize the + // 'AST' abbreviation as matching the current location (Baghdad, + // where we'd expect a +03 hrs offset); likely because we're using + // a recent tzdata release (2017a or newer). + // If it happens, skip the Baghdad test. + if offset != 0 { + t2 = Date(2013, February, 1, 00, 00, 00, 0, baghdad) + if t1 != t2 { + t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad) = %v, want %v", t1, t2) + } + if offset != 3*60*60 { + t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad).Zone = _, %d, want _, %d", offset, 3*60*60) + } } blancSablon, err := LoadLocation("America/Blanc-Sablon") @@ -272,6 +291,9 @@ func TestParseInLocation(t *testing.T) { t.Fatal(err) } + // In this case 'AST' means 'Atlantic Standard Time', and we + // expect the abbreviation to correctly match the american + // location. t1, err = ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", blancSablon) if err != nil { t.Fatal(err) @@ -406,6 +428,7 @@ var parseTimeZoneTests = []ParseTimeZoneTest{ {"ESAST hi", 5, true}, {"ESASTT hi", 0, false}, // run of upper-case letters too long. {"ESATY hi", 0, false}, // five letters must end in T. + {"WITA hi", 4, true}, // Issue #18251 } func TestParseTimeZone(t *testing.T) { @@ -442,6 +465,8 @@ var parseErrorTests = []ParseErrorTest{ {RFC3339, "2006-01-02T15:04_abc", `parsing time "2006-01-02T15:04_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as ":"`}, {RFC3339, "2006-01-02T15:04:05_abc", `parsing time "2006-01-02T15:04:05_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as "Z07:00"`}, {RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: _abc`}, + // invalid second followed by optional fractional seconds + {RFC3339, "2010-02-04T21:00:67.012345678-08:00", "second out of range"}, } func TestParseErrors(t *testing.T) { @@ -449,7 +474,7 @@ func TestParseErrors(t *testing.T) { _, err := Parse(test.format, test.value) if err == nil { t.Errorf("expected error for %q %q", test.format, test.value) - } else if strings.Index(err.Error(), test.expect) < 0 { + } else if !strings.Contains(err.Error(), test.expect) { t.Errorf("expected error with %q for %q %q; got %s", test.expect, test.format, test.value, err) } } diff --git a/libgo/go/time/genzabbrs.go b/libgo/go/time/genzabbrs.go index 9eb0728a42..6281f73ce4 100644 --- a/libgo/go/time/genzabbrs.go +++ b/libgo/go/time/genzabbrs.go @@ -30,7 +30,7 @@ var filename = flag.String("output", "zoneinfo_abbrs_windows.go", "output file n // getAbbrs finds timezone abbreviations (standard and daylight saving time) // for location l. func getAbbrs(l *time.Location) (st, dt string) { - t := time.Date(time.Now().Year(), 0, 0, 0, 0, 0, 0, l) + t := time.Date(time.Now().Year(), 0, 1, 0, 0, 0, 0, l) abbr1, off1 := t.Zone() for i := 0; i < 12; i++ { t = t.AddDate(0, 1, 0) diff --git a/libgo/go/time/sleep.go b/libgo/go/time/sleep.go index e7a2ee2059..4b01404896 100644 --- a/libgo/go/time/sleep.go +++ b/libgo/go/time/sleep.go @@ -12,7 +12,7 @@ func Sleep(d Duration) func runtimeNano() int64 // Interface to timers implemented in package runtime. -// Must be in sync with ../runtime/runtime.h:/^struct.Timer$ +// Must be in sync with ../runtime/time.go:/^type timer type runtimeTimer struct { i int when int64 @@ -24,7 +24,7 @@ type runtimeTimer struct { // when is a helper function for setting the 'when' field of a runtimeTimer. // It returns what the time will be, in nanoseconds, Duration d in the future. -// If d is negative, it is ignored. If the returned value would be less than +// If d is negative, it is ignored. If the returned value would be less than // zero because of an overflow, MaxInt64 is returned. func when(d Duration) int64 { if d <= 0 { @@ -54,6 +54,23 @@ type Timer struct { // expired or been stopped. // Stop does not close the channel, to prevent a read from the channel succeeding // incorrectly. +// +// To prevent a timer created with NewTimer from firing after a call to Stop, +// check the return value and drain the channel. +// For example, assuming the program has not received from t.C already: +// +// if !t.Stop() { +// <-t.C +// } +// +// This cannot be done concurrent to other receives from the Timer's +// channel. +// +// For a timer created with AfterFunc(d, f), if t.Stop returns false, then the timer +// has already expired and the function f has been started in its own goroutine; +// Stop does not wait for f to complete before returning. +// If the caller needs to know whether f is completed, it must coordinate +// with f explicitly. func (t *Timer) Stop() bool { if t.r.f == nil { panic("time: Stop called on uninitialized Timer") @@ -80,6 +97,27 @@ func NewTimer(d Duration) *Timer { // Reset changes the timer to expire after duration d. // It returns true if the timer had been active, false if the timer had // expired or been stopped. +// +// Resetting a timer must take care not to race with the send into t.C +// that happens when the current timer expires. +// If a program has already received a value from t.C, the timer is known +// to have expired, and t.Reset can be used directly. +// If a program has not yet received a value from t.C, however, +// the timer must be stopped and—if Stop reports that the timer expired +// before being stopped—the channel explicitly drained: +// +// if !t.Stop() { +// <-t.C +// } +// t.Reset(d) +// +// This should not be done concurrent to other receives from the Timer's +// channel. +// +// Note that it is not possible to use Reset's return value correctly, as there +// is a race condition between draining the channel and the new timer expiring. +// Reset should always be invoked on stopped or expired channels, as described above. +// The return value exists to preserve compatibility with existing programs. func (t *Timer) Reset(d Duration) bool { if t.r.f == nil { panic("time: Reset called on uninitialized Timer") @@ -106,6 +144,9 @@ func sendTime(c interface{}, seq uintptr) { // After waits for the duration to elapse and then sends the current time // on the returned channel. // It is equivalent to NewTimer(d).C. +// The underlying Timer is not recovered by the garbage collector +// until the timer fires. If efficiency is a concern, use NewTimer +// instead and call Timer.Stop if the timer is no longer needed. func After(d Duration) <-chan Time { return NewTimer(d).C } diff --git a/libgo/go/time/sys_plan9.go b/libgo/go/time/sys_plan9.go index 8484729448..11365a791f 100644 --- a/libgo/go/time/sys_plan9.go +++ b/libgo/go/time/sys_plan9.go @@ -55,9 +55,9 @@ func closefd(fd uintptr) { } func preadn(fd uintptr, buf []byte, off int) error { - whence := 0 + whence := seekStart if off < 0 { - whence = 2 + whence = seekEnd } if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil { return err diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go index e592415daa..91d54c9ffd 100644 --- a/libgo/go/time/sys_unix.go +++ b/libgo/go/time/sys_unix.go @@ -55,9 +55,9 @@ func closefd(fd uintptr) { } func preadn(fd uintptr, buf []byte, off int) error { - whence := 0 + whence := seekStart if off < 0 { - whence = 2 + whence = seekEnd } if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil { return err diff --git a/libgo/go/time/sys_windows.go b/libgo/go/time/sys_windows.go index de63b4bf4b..a4a068f784 100644 --- a/libgo/go/time/sys_windows.go +++ b/libgo/go/time/sys_windows.go @@ -52,9 +52,9 @@ func closefd(fd uintptr) { } func preadn(fd uintptr, buf []byte, off int) error { - whence := 0 + whence := seekStart if off < 0 { - whence = 2 + whence = seekEnd } if _, err := syscall.Seek(syscall.Handle(fd), int64(off), whence); err != nil { return err diff --git a/libgo/go/time/tick.go b/libgo/go/time/tick.go index 196e8ac61a..3d693206a5 100644 --- a/libgo/go/time/tick.go +++ b/libgo/go/time/tick.go @@ -39,7 +39,7 @@ func NewTicker(d Duration) *Ticker { return t } -// Stop turns off a ticker. After Stop, no more ticks will be sent. +// Stop turns off a ticker. After Stop, no more ticks will be sent. // Stop does not close the channel, to prevent a read from the channel succeeding // incorrectly. func (t *Ticker) Stop() { @@ -50,6 +50,7 @@ func (t *Ticker) Stop() { // channel only. While Tick is useful for clients that have no need to shut down // the Ticker, be aware that without a way to shut it down the underlying // Ticker cannot be recovered by the garbage collector; it "leaks". +// Unlike NewTicker, Tick will return nil if d <= 0. func Tick(d Duration) <-chan Time { if d <= 0 { return nil diff --git a/libgo/go/time/tick_test.go b/libgo/go/time/tick_test.go index 32f4740ad9..2ab77f6025 100644 --- a/libgo/go/time/tick_test.go +++ b/libgo/go/time/tick_test.go @@ -35,7 +35,7 @@ func TestTicker(t *testing.T) { } } -// Test that a bug tearing down a ticker has been fixed. This routine should not deadlock. +// Test that a bug tearing down a ticker has been fixed. This routine should not deadlock. func TestTeardown(t *testing.T) { Delta := 100 * Millisecond if testing.Short() { diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index ef4ba5842d..10b32461e1 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -4,7 +4,8 @@ // Package time provides functionality for measuring and displaying time. // -// The calendrical calculations always assume a Gregorian calendar. +// The calendrical calculations always assume a Gregorian calendar, with +// no leap seconds. package time import "errors" @@ -12,8 +13,8 @@ import "errors" // A Time represents an instant in time with nanosecond precision. // // 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 +// 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. // // Time instants can be compared using the Before, After, and Equal methods. @@ -49,11 +50,18 @@ type Time struct { // loc specifies the Location that should be used to // determine the minute, hour, month, day, and year // that correspond to this Time. - // Only the zero Time has a nil Location. - // In that case it is interpreted to mean UTC. + // The nil location means UTC. + // All UTC times are represented with loc==nil, never loc==&utcLoc. loc *Location } +func (t *Time) setLoc(loc *Location) { + if loc == &utcLoc { + loc = nil + } + t.loc = loc +} + // 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 @@ -67,8 +75,7 @@ func (t Time) Before(u Time) bool { // 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. -// This comparison is different from using t == u, which also compares -// the locations. +// Do not use == with Time values. func (t Time) Equal(u Time) bool { return t.sec == u.sec && t.nsec == u.nsec } @@ -107,7 +114,14 @@ var months = [...]string{ } // String returns the English name of the month ("January", "February", ...). -func (m Month) String() string { return months[m-1] } +func (m Month) String() string { + if January <= m && m <= December { + return months[m-1] + } + buf := make([]byte, 20) + n := fmtInt(buf, uint64(m)) + return "%!Month(" + string(buf[n:]) + ")" +} // A Weekday specifies a day of the week (Sunday = 0, ...). type Weekday int @@ -146,7 +160,7 @@ func (d Weekday) String() string { return days[d] } // 00:00:00 UTC, which would be 12-31-(-1) 19:00:00 in New York. // // The zero Time value does not force a specific epoch for the time -// representation. For example, to use the Unix epoch internally, we +// 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 // suggest a representation, namely using 1-1-1 00:00:00 UTC as the @@ -155,17 +169,17 @@ func (d Weekday) String() string { return days[d] } // The Add and Sub computations are oblivious to the choice of epoch. // // The presentation computations - year, month, minute, and so on - all -// rely heavily on division and modulus by positive constants. For +// rely heavily on division and modulus by positive constants. For // calendrical calculations we want these divisions to round down, even // for negative values, so that the remainder is always positive, but // Go's division (like most hardware division instructions) rounds to -// zero. We can still do those computations and then adjust the result +// zero. We can still do those computations and then adjust the result // for a negative numerator, but it's annoying to write the adjustment -// over and over. Instead, we can change to a different epoch so long +// over and over. Instead, we can change to a different epoch so long // ago that all the times we care about will be positive, and then round -// to zero and round down coincide. These presentation routines already +// to zero and round down coincide. These presentation routines already // have to add the zone offset, so adding the translation to the -// alternate epoch is cheap. For example, having a non-negative time t +// alternate epoch is cheap. For example, having a non-negative time t // means that we can write // // sec = t % 60 @@ -181,9 +195,9 @@ func (d Weekday) String() string { return days[d] } // // 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 -// of the week match up. It simplifies the computations to choose the +// 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 +// long as possible. That means choosing a year equal to 1 mod 400, so // that the first leap year is the 4th year, the first missed leap year // is the 100th year, and the missed missed leap year is the 400th year. // So we'd prefer instead to print a calendar for 2001-2400 and reuse it @@ -209,7 +223,7 @@ func (d Weekday) String() string { return days[d] } // 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 // printing the zero time correctly isn't supported in half the time -// zones. By comparison, it's reasonable to mishandle some times in +// zones. By comparison, it's reasonable to mishandle some times in // the year -292277022399. // // All this is opaque to clients of the API and can be changed if a @@ -225,9 +239,6 @@ const ( // Assumed by the unixToInternal computation below. internalYear = 1 - // The year of the zero Unix time. - unixYear = 1970 - // Offsets to convert between internal and absolute or Unix times. absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay internalToAbsolute = -absoluteToInternal @@ -425,7 +436,7 @@ func (t Time) YearDay() int { } // A Duration represents the elapsed time between two instants -// as an int64 nanosecond count. The representation limits the +// as an int64 nanosecond count. The representation limits the // largest representable duration to approximately 290 years. type Duration int64 @@ -434,7 +445,7 @@ const ( maxDuration Duration = 1<<63 - 1 ) -// Common durations. There is no definition for units of Day or larger +// Common durations. There is no definition for units of Day or larger // to avoid confusion across daylight savings time zone transitions. // // To count the number of units in a Duration, divide: @@ -455,10 +466,9 @@ const ( ) // String returns a string representing the duration in the form "72h3m0.5s". -// Leading zero units are omitted. As a special case, durations less than one +// Leading zero units are omitted. As a special case, durations less than one // second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure -// that the leading digit is non-zero. The zero duration formats as 0, -// with no unit. +// that the leading digit is non-zero. The zero duration formats as 0s. func (d Duration) String() string { // Largest time is 2540400h10m10.000000000s var buf [32]byte @@ -479,7 +489,7 @@ func (d Duration) String() string { w-- switch { case u == 0: - return "0" + return "0s" case u < uint64(Microsecond): // print nanoseconds prec = 0 @@ -589,27 +599,27 @@ func (d Duration) Nanoseconds() int64 { return int64(d) } func (d Duration) Seconds() float64 { sec := d / Second nsec := d % Second - return float64(sec) + float64(nsec)*1e-9 + return float64(sec) + float64(nsec)/1e9 } // Minutes returns the duration as a floating point number of minutes. func (d Duration) Minutes() float64 { min := d / Minute nsec := d % Minute - return float64(min) + float64(nsec)*(1e-9/60) + return float64(min) + float64(nsec)/(60*1e9) } // Hours returns the duration as a floating point number of hours. func (d Duration) Hours() float64 { hour := d / Hour nsec := d % Hour - return float64(hour) + float64(nsec)*(1e-9/60/60) + return float64(hour) + float64(nsec)/(60*60*1e9) } // Add returns the time t+d. func (t Time) Add(d Duration) Time { t.sec += int64(d / 1e9) - nsec := int32(t.nsec) + int32(d%1e9) + nsec := t.nsec + int32(d%1e9) if nsec >= 1e9 { t.sec++ nsec -= 1e9 @@ -626,7 +636,7 @@ 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(int32(t.nsec)-int32(u.nsec)) + d := Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec) // Check for overflow or underflow. switch { case u.Add(d).Equal(t): @@ -644,6 +654,12 @@ func Since(t Time) Duration { return Now().Sub(t) } +// Until returns the duration until t. +// It is shorthand for t.Sub(time.Now()). +func Until(t Time) Duration { + return t.Sub(Now()) +} + // AddDate returns the time corresponding to adding the // given number of years, months, and days to t. // For example, AddDate(-1, 2, 3) applied to January 1, 2011 @@ -655,7 +671,7 @@ func Since(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.loc) + return Date(year+years, month+Month(months), day+days, hour, min, sec, int(t.nsec), t.Location()) } const ( @@ -749,7 +765,7 @@ func absDate(abs uint64, full bool) (year int, month Month, day int, yday int) { } // daysBefore[m] counts the number of days in a non-leap year -// before month m begins. There is an entry for m=12, counting +// before month m begins. There is an entry for m=12, counting // the number of days before January of next year (365). var daysBefore = [...]int32{ 0, @@ -785,13 +801,13 @@ func Now() Time { // UTC returns t with the location set to UTC. func (t Time) UTC() Time { - t.loc = UTC + t.setLoc(&utcLoc) return t } // Local returns t with the location set to local time. func (t Time) Local() Time { - t.loc = Local + t.setLoc(Local) return t } @@ -802,7 +818,7 @@ func (t Time) In(loc *Location) Time { if loc == nil { panic("time: missing Location in call to Time.In") } - t.loc = loc + t.setLoc(loc) return t } @@ -830,8 +846,9 @@ func (t Time) Unix() int64 { // UnixNano returns t as a Unix time, the number of nanoseconds elapsed // since January 1, 1970 UTC. The result is undefined if the Unix time -// in nanoseconds cannot be represented by an int64. Note that this -// means the result of calling UnixNano on the zero Time is undefined. +// in nanoseconds cannot be represented by an int64 (a date before the year +// 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) } @@ -842,7 +859,7 @@ const timeBinaryVersion byte = 1 func (t Time) MarshalBinary() ([]byte, error) { var offsetMin int16 // minutes east of UTC. -1 is UTC. - if t.Location() == &utcLoc { + if t.Location() == UTC { offsetMin = -1 } else { _, offset := t.Zone() @@ -903,11 +920,11 @@ func (t *Time) UnmarshalBinary(data []byte) error { offset := int(int16(buf[1])|int16(buf[0])<<8) * 60 if offset == -1*60 { - t.loc = &utcLoc + t.setLoc(&utcLoc) } else if _, localoff, _, _, _ := Local.lookup(t.sec + internalToUnix); offset == localoff { - t.loc = Local + t.setLoc(Local) } else { - t.loc = FixedZone("", offset) + t.setLoc(FixedZone("", offset)) } return nil @@ -945,10 +962,15 @@ func (t Time) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the json.Unmarshaler interface. // The time is expected to be a quoted string in RFC 3339 format. -func (t *Time) UnmarshalJSON(data []byte) (err error) { +func (t *Time) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } // Fractional seconds are handled implicitly by Parse. + var err error *t, err = Parse(`"`+RFC3339+`"`, string(data)) - return + return err } // MarshalText implements the encoding.TextMarshaler interface. @@ -964,10 +986,11 @@ func (t Time) MarshalText() ([]byte, error) { // UnmarshalText implements the encoding.TextUnmarshaler interface. // The time is expected to be in RFC 3339 format. -func (t *Time) UnmarshalText(data []byte) (err error) { +func (t *Time) UnmarshalText(data []byte) error { // Fractional seconds are handled implicitly by Parse. + var err error *t, err = Parse(RFC3339, string(data)) - return + return err } // Unix returns the local Time corresponding to the given Unix time, @@ -1019,7 +1042,7 @@ func norm(hi, lo, base int) (nhi, nlo int) { // // A daylight savings time transition skips or repeats times. // For example, in the United States, March 13, 2011 2:15am never occurred, -// while November 6, 2011 1:15am occurred twice. In such cases, the +// while November 6, 2011 1:15am occurred twice. In such cases, the // choice of time zone, and therefore the time, is not well-defined. // Date returns a time that is correct in one of the two zones involved // in the transition, but it does not guarantee which. @@ -1094,11 +1117,18 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T unix -= int64(offset) } - return Time{unix + unixToInternal, int32(nsec), loc} + t := Time{unix + unixToInternal, int32(nsec), nil} + t.setLoc(loc) + return t } // Truncate returns the result of rounding t down to a multiple of d (since the zero time). // If d <= 0, Truncate returns t unchanged. +// +// Truncate operates on the time as an absolute duration since the +// zero time; it does not operate on the presentation form of the +// 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 { if d <= 0 { return t @@ -1110,6 +1140,11 @@ func (t Time) Truncate(d Duration) Time { // Round returns the result of rounding t to the nearest multiple of d (since the zero time). // The rounding behavior for halfway values is to round up. // If d <= 0, Round returns t unchanged. +// +// Round operates on the time as an absolute duration since the +// zero time; it does not operate on the presentation form of the +// 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 { if d <= 0 { return t @@ -1126,7 +1161,7 @@ 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 := int32(t.nsec) + nsec := t.nsec if t.sec < 0 { // Operate on absolute value. neg = true @@ -1160,7 +1195,7 @@ func div(t Time, d Duration) (qmod2 int, r Duration) { tmp := (sec >> 32) * 1e9 u1 := tmp >> 32 u0 := tmp << 32 - tmp = uint64(sec&0xFFFFFFFF) * 1e9 + tmp = (sec & 0xFFFFFFFF) * 1e9 u0x, u0 := u0, u0+tmp if u0 < u0x { u1++ diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index a925e98a83..2922560f09 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -22,7 +22,7 @@ import ( // the subsequent tests fail. func TestZoneData(t *testing.T) { lt := Now() - // PST is 8 hours west, PDT is 7 hours west. We could use the name but it's not unique. + // PST is 8 hours west, PDT is 7 hours west. We could use the name but it's not unique. if name, off := lt.Zone(); off != -8*60*60 && off != -7*60*60 { t.Errorf("Unable to find US Pacific time zone data for testing; time zone is %q offset %d", name, off) t.Error("Likely problem: the time zone files have not been installed.") @@ -533,7 +533,7 @@ var durationTests = []struct { str string d Duration }{ - {"0", 0}, + {"0s", 0}, {"1ns", 1 * Nanosecond}, {"1.1µs", 1100 * Nanosecond}, {"2.2ms", 2200 * Microsecond}, @@ -840,6 +840,10 @@ var parseDurationTests = []struct { {"9223372036s854ms775us807ns", true, (1<<63 - 1) * Nanosecond}, // large negative value {"-9223372036854775807ns", true, -1<<63 + 1*Nanosecond}, + // huge string; issue 15011. + {"0.100000000000000000000h", true, 6 * Minute}, + // This value tests the first overflow check in leadingFraction. + {"0.830103483285477580700h", true, 49*Minute + 48*Second + 372539827*Nanosecond}, // errors {"", false, 0}, @@ -891,7 +895,7 @@ func TestLocationRace(t *testing.T) { go func() { c <- Now().String() }() - Now().String() + _ = Now().String() <-c Sleep(100 * Millisecond) @@ -939,8 +943,11 @@ func TestLoadFixed(t *testing.T) { // but Go and most other systems use "east is positive". // So GMT+1 corresponds to -3600 in the Go zone, not +3600. name, offset := Now().In(loc).Zone() - if name != "GMT+1" || offset != -1*60*60 { - t.Errorf("Now().In(loc).Zone() = %q, %d, want %q, %d", name, offset, "GMT+1", -1*60*60) + // The zone abbreviation is "-01" since tzdata-2016g, and "GMT+1" + // on earlier versions; we accept both. (Issue #17276). + if !(name == "GMT+1" || name == "-01") || offset != -1*60*60 { + t.Errorf("Now().In(loc).Zone() = %q, %d, want %q or %q, %d", + name, offset, "GMT+1", "-01", -1*60*60) } } @@ -996,6 +1003,21 @@ func TestDurationNanoseconds(t *testing.T) { } } +var secDurationTests = []struct { + d Duration + want float64 +}{ + {Duration(300000000), 0.3}, +} + +func TestDurationSeconds(t *testing.T) { + for _, tt := range secDurationTests { + if got := tt.d.Seconds(); got != tt.want { + t.Errorf("d.Seconds() = %g; want: %g", got, tt.want) + } + } +} + var minDurationTests = []struct { d Duration want float64 @@ -1004,6 +1026,7 @@ var minDurationTests = []struct { {Duration(-1), -1 / 60e9}, {Duration(1), 1 / 60e9}, {Duration(60000000000), 1}, + {Duration(3000), 5e-8}, } func TestDurationMinutes(t *testing.T) { @@ -1022,6 +1045,7 @@ var hourDurationTests = []struct { {Duration(-1), -1 / 3600e9}, {Duration(1), 1 / 3600e9}, {Duration(3600000000000), 1}, + {Duration(36), 1e-11}, } func TestDurationHours(t *testing.T) { @@ -1032,6 +1056,100 @@ func TestDurationHours(t *testing.T) { } } +var defaultLocTests = []struct { + name string + f func(t1, t2 Time) bool +}{ + {"After", func(t1, t2 Time) bool { return t1.After(t2) == t2.After(t1) }}, + {"Before", func(t1, t2 Time) bool { return t1.Before(t2) == t2.Before(t1) }}, + {"Equal", func(t1, t2 Time) bool { return t1.Equal(t2) == t2.Equal(t1) }}, + + {"IsZero", func(t1, t2 Time) bool { return t1.IsZero() == t2.IsZero() }}, + {"Date", func(t1, t2 Time) bool { + a1, b1, c1 := t1.Date() + a2, b2, c2 := t2.Date() + return a1 == a2 && b1 == b2 && c1 == c2 + }}, + {"Year", func(t1, t2 Time) bool { return t1.Year() == t2.Year() }}, + {"Month", func(t1, t2 Time) bool { return t1.Month() == t2.Month() }}, + {"Day", func(t1, t2 Time) bool { return t1.Day() == t2.Day() }}, + {"Weekday", func(t1, t2 Time) bool { return t1.Weekday() == t2.Weekday() }}, + {"ISOWeek", func(t1, t2 Time) bool { + a1, b1 := t1.ISOWeek() + a2, b2 := t2.ISOWeek() + return a1 == a2 && b1 == b2 + }}, + {"Clock", func(t1, t2 Time) bool { + a1, b1, c1 := t1.Clock() + a2, b2, c2 := t2.Clock() + return a1 == a2 && b1 == b2 && c1 == c2 + }}, + {"Hour", func(t1, t2 Time) bool { return t1.Hour() == t2.Hour() }}, + {"Minute", func(t1, t2 Time) bool { return t1.Minute() == t2.Minute() }}, + {"Second", func(t1, t2 Time) bool { return t1.Second() == t2.Second() }}, + {"Nanosecond", func(t1, t2 Time) bool { return t1.Hour() == t2.Hour() }}, + {"YearDay", func(t1, t2 Time) bool { return t1.YearDay() == t2.YearDay() }}, + + // Using Equal since Add don't modify loc using "==" will cause a fail + {"Add", func(t1, t2 Time) bool { return t1.Add(Hour).Equal(t2.Add(Hour)) }}, + {"Sub", func(t1, t2 Time) bool { return t1.Sub(t2) == t2.Sub(t1) }}, + + //Original caus for this test case bug 15852 + {"AddDate", func(t1, t2 Time) bool { return t1.AddDate(1991, 9, 3) == t2.AddDate(1991, 9, 3) }}, + + {"UTC", func(t1, t2 Time) bool { return t1.UTC() == t2.UTC() }}, + {"Local", func(t1, t2 Time) bool { return t1.Local() == t2.Local() }}, + {"In", func(t1, t2 Time) bool { return t1.In(UTC) == t2.In(UTC) }}, + + {"Local", func(t1, t2 Time) bool { return t1.Local() == t2.Local() }}, + {"Zone", func(t1, t2 Time) bool { + a1, b1 := t1.Zone() + a2, b2 := t2.Zone() + return a1 == a2 && b1 == b2 + }}, + + {"Unix", func(t1, t2 Time) bool { return t1.Unix() == t2.Unix() }}, + {"UnixNano", func(t1, t2 Time) bool { return t1.UnixNano() == t2.UnixNano() }}, + + {"MarshalBinary", func(t1, t2 Time) bool { + a1, b1 := t1.MarshalBinary() + a2, b2 := t2.MarshalBinary() + return bytes.Equal(a1, a2) && b1 == b2 + }}, + {"GobEncode", func(t1, t2 Time) bool { + a1, b1 := t1.GobEncode() + a2, b2 := t2.GobEncode() + return bytes.Equal(a1, a2) && b1 == b2 + }}, + {"MarshalJSON", func(t1, t2 Time) bool { + a1, b1 := t1.MarshalJSON() + a2, b2 := t2.MarshalJSON() + return bytes.Equal(a1, a2) && b1 == b2 + }}, + {"MarshalText", func(t1, t2 Time) bool { + a1, b1 := t1.MarshalText() + a2, b2 := t2.MarshalText() + return bytes.Equal(a1, a2) && b1 == b2 + }}, + + {"Truncate", func(t1, t2 Time) bool { return t1.Truncate(Hour).Equal(t2.Truncate(Hour)) }}, + {"Round", func(t1, t2 Time) bool { return t1.Round(Hour).Equal(t2.Round(Hour)) }}, + + {"== Time{}", func(t1, t2 Time) bool { return (t1 == Time{}) == (t2 == Time{}) }}, +} + +func TestDefaultLoc(t *testing.T) { + //This test verifyes that all Time's methods behaves identical if loc is set + //as nil or UTC + for _, tt := range defaultLocTests { + t1 := Time{} + t2 := Time{}.UTC() + if !tt.f(t1, t2) { + t.Errorf("Time{} and Time{}.UTC() behave differently for %s", tt.name) + } + } +} + func BenchmarkNow(b *testing.B) { for i := 0; i < b.N; i++ { t = Now() @@ -1114,3 +1232,25 @@ func BenchmarkDay(b *testing.B) { _ = t.Day() } } + +func TestMarshalBinaryZeroTime(t *testing.T) { + t0 := Time{} + enc, err := t0.MarshalBinary() + if err != nil { + t.Fatal(err) + } + t1 := Now() // not zero + if err := t1.UnmarshalBinary(enc); err != nil { + t.Fatal(err) + } + if t1 != t0 { + t.Errorf("t0=%#v\nt1=%#v\nwant identical structures", t0, t1) + } +} + +// Issue 17720: Zero value of time.Month fails to print +func TestZeroMonthString(t *testing.T) { + if got, want := Month(0).String(), "%!Month(0)"; got != want { + t.Errorf("zero month = %q; want %q", got, want) + } +} diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go index c56743933f..fb0aa39240 100644 --- a/libgo/go/time/zoneinfo.go +++ b/libgo/go/time/zoneinfo.go @@ -9,6 +9,8 @@ import ( "syscall" ) +//go:generate env ZONEINFO=$GOROOT/lib/time/zoneinfo.zip go run genzabbrs.go -output zoneinfo_abbrs_windows.go + // A Location maps time instants to the zone in use at that time. // Typically, the Location represents the collection of time offsets // in use in a geographical area, such as CEST and CET for central Europe. diff --git a/libgo/go/time/zoneinfo_abbrs_windows.go b/libgo/go/time/zoneinfo_abbrs_windows.go index 51a1a2f66d..9425db844c 100644 --- a/libgo/go/time/zoneinfo_abbrs_windows.go +++ b/libgo/go/time/zoneinfo_abbrs_windows.go @@ -13,104 +13,133 @@ type abbr struct { } var abbrs = map[string]abbr{ - "Egypt Standard Time": {"EET", "EET"}, // Africa/Cairo - "Morocco Standard Time": {"WET", "WEST"}, // Africa/Casablanca - "South Africa Standard Time": {"SAST", "SAST"}, // Africa/Johannesburg - "W. Central Africa Standard Time": {"WAT", "WAT"}, // Africa/Lagos - "E. Africa Standard Time": {"EAT", "EAT"}, // Africa/Nairobi - "Libya Standard Time": {"EET", "EET"}, // Africa/Tripoli - "Namibia Standard Time": {"WAT", "WAST"}, // Africa/Windhoek - "Alaskan Standard Time": {"AKST", "AKDT"}, // America/Anchorage - "Paraguay Standard Time": {"PYT", "PYST"}, // America/Asuncion - "Bahia Standard Time": {"BRT", "BRST"}, // America/Bahia - "SA Pacific Standard Time": {"COT", "COT"}, // America/Bogota - "Argentina Standard Time": {"ART", "ART"}, // America/Buenos_Aires - "Venezuela Standard Time": {"VET", "VET"}, // America/Caracas - "SA Eastern Standard Time": {"GFT", "GFT"}, // 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 - "Mountain Standard Time": {"MST", "MDT"}, // America/Denver - "Greenland Standard Time": {"WGT", "WGST"}, // America/Godthab - "Central America Standard Time": {"CST", "CST"}, // America/Guatemala - "Atlantic Standard Time": {"AST", "ADT"}, // America/Halifax - "US Eastern Standard Time": {"EST", "EDT"}, // America/Indianapolis - "SA Western Standard Time": {"BOT", "BOT"}, // America/La_Paz - "Pacific Standard Time": {"PST", "PDT"}, // America/Los_Angeles - "Central Standard Time (Mexico)": {"CST", "CDT"}, // America/Mexico_City - "Montevideo Standard Time": {"UYT", "UYST"}, // America/Montevideo - "Eastern Standard Time": {"EST", "EDT"}, // America/New_York - "US Mountain Standard Time": {"MST", "MST"}, // America/Phoenix - "Canada Central Standard Time": {"CST", "CST"}, // America/Regina - "Pacific Standard Time (Mexico)": {"PST", "PDT"}, // America/Santa_Isabel - "Pacific SA Standard Time": {"CLT", "CLST"}, // America/Santiago - "E. South America Standard Time": {"BRT", "BRST"}, // America/Sao_Paulo - "Newfoundland Standard Time": {"NST", "NDT"}, // America/St_Johns - "Central Asia Standard Time": {"ALMT", "ALMT"}, // Asia/Almaty - "Jordan Standard Time": {"EET", "EEST"}, // Asia/Amman - "Arabic Standard Time": {"AST", "AST"}, // Asia/Baghdad - "Azerbaijan Standard Time": {"AZT", "AZST"}, // Asia/Baku - "SE Asia Standard Time": {"ICT", "ICT"}, // Asia/Bangkok - "Middle East Standard Time": {"EET", "EEST"}, // Asia/Beirut - "India Standard Time": {"IST", "IST"}, // Asia/Calcutta - "Sri Lanka Standard Time": {"IST", "IST"}, // Asia/Colombo - "Syria Standard Time": {"EET", "EEST"}, // Asia/Damascus - "Bangladesh Standard Time": {"BDT", "BDT"}, // Asia/Dhaka - "Arabian Standard Time": {"GST", "GST"}, // Asia/Dubai - "North Asia East Standard Time": {"IRKT", "IRKT"}, // Asia/Irkutsk - "Israel Standard Time": {"IST", "IDT"}, // Asia/Jerusalem - "Afghanistan Standard Time": {"AFT", "AFT"}, // Asia/Kabul - "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": {"NOVT", "NOVT"}, // Asia/Novosibirsk - "Myanmar Standard Time": {"MMT", "MMT"}, // Asia/Rangoon - "Arab Standard Time": {"AST", "AST"}, // Asia/Riyadh - "Korea Standard Time": {"KST", "KST"}, // Asia/Seoul - "China Standard Time": {"CST", "CST"}, // Asia/Shanghai - "Singapore Standard Time": {"SGT", "SGT"}, // Asia/Singapore - "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 - "Tokyo Standard Time": {"JST", "JST"}, // Asia/Tokyo - "Ulaanbaatar Standard Time": {"ULAT", "ULAT"}, // 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 - "Greenwich Standard Time": {"GMT", "GMT"}, // Atlantic/Reykjavik - "Cen. Australia Standard Time": {"CST", "CST"}, // Australia/Adelaide - "E. Australia Standard Time": {"EST", "EST"}, // Australia/Brisbane - "AUS Central Standard Time": {"CST", "CST"}, // Australia/Darwin - "Tasmania Standard Time": {"EST", "EST"}, // Australia/Hobart - "W. Australia Standard Time": {"WST", "WST"}, // Australia/Perth - "AUS Eastern Standard Time": {"EST", "EST"}, // Australia/Sydney + "Egypt Standard Time": {"EET", "EET"}, // Africa/Cairo + "Morocco Standard Time": {"WET", "WEST"}, // Africa/Casablanca + "South Africa Standard Time": {"SAST", "SAST"}, // Africa/Johannesburg + "W. Central Africa Standard Time": {"WAT", "WAT"}, // Africa/Lagos + "E. Africa Standard Time": {"EAT", "EAT"}, // Africa/Nairobi + "Libya Standard Time": {"EET", "EET"}, // Africa/Tripoli + "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 + "Eastern Standard Time (Mexico)": {"EST", "EST"}, // America/Cancun + "Venezuela Standard Time": {"VET", "VET"}, // America/Caracas + "SA Eastern Standard Time": {"GFT", "GFT"}, // 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 + "Mountain Standard Time": {"MST", "MDT"}, // America/Denver + "Greenland Standard Time": {"WGT", "WGST"}, // 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 + "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 + "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 + "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 + "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 + "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 + "Syria Standard Time": {"EET", "EEST"}, // Asia/Damascus + "Bangladesh Standard Time": {"BDT", "BDT"}, // Asia/Dhaka + "Arabian Standard Time": {"GST", "GST"}, // 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 + "Israel Standard Time": {"IST", "IDT"}, // Asia/Jerusalem + "Afghanistan Standard Time": {"AFT", "AFT"}, // Asia/Kabul + "Russia Time Zone 11": {"PETT", "PETT"}, // 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 + "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 + "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 + "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 + "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 + "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 + "Tasmania Standard Time": {"AEST", "AEDT"}, // Australia/Hobart + "Lord Howe Standard Time": {"LHST", "LHDT"}, // 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": {"FET", "FET"}, // Europe/Kaliningrad + "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": {"WST", "WST"}, // Pacific/Apia + "Samoa Standard Time": {"WSST", "WSDT"}, // Pacific/Apia "New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland - "Fiji Standard Time": {"FJT", "FJT"}, // Pacific/Fiji + "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 } diff --git a/libgo/go/time/zoneinfo_android.go b/libgo/go/time/zoneinfo_android.go new file mode 100644 index 0000000000..695a8adfaa --- /dev/null +++ b/libgo/go/time/zoneinfo_android.go @@ -0,0 +1,119 @@ +// Copyright 2016 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 the "tzdata" packed timezone file used on Android. +// The format is lifted from ZoneInfoDB.java and ZoneInfo.java in +// java/libcore/util in the AOSP. + +package time + +import ( + "errors" + "runtime" +) + +var tzdataPaths = []string{ + "/system/usr/share/zoneinfo/tzdata", + "/data/misc/zoneinfo/current/tzdata", + runtime.GOROOT() + "/lib/time/zoneinfo.zip", +} + +var origTzdataPaths = tzdataPaths + +func forceZipFileForTesting(zipOnly bool) { + tzdataPaths = make([]string, len(origTzdataPaths)) + copy(tzdataPaths, origTzdataPaths) + if zipOnly { + for i := 0; i < len(tzdataPaths)-1; i++ { + tzdataPaths[i] = "/XXXNOEXIST" + } + } +} + +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() { + // TODO(elias.naur): getprop persist.sys.timezone + localLoc = *UTC +} + +func loadLocation(name string) (*Location, error) { + var firstErr error + for _, path := range tzdataPaths { + var z *Location + var err error + if len(path) > 4 && path[len(path)-4:] == ".zip" { + z, err = loadZoneZip(path, name) + } else { + z, err = loadTzdataFile(path, name) + } + if err == nil { + z.name = name + return z, nil + } else if firstErr == nil && !isNotExist(err) { + firstErr = err + } + } + if firstErr != nil { + return nil, firstErr + } + return nil, errors.New("unknown time zone " + name) +} + +func loadTzdataFile(file, name string) (*Location, error) { + const ( + headersize = 12 + 3*4 + namesize = 40 + entrysize = namesize + 3*4 + ) + if len(name) > namesize { + return nil, errors.New(name + " is longer than the maximum zone name length (40 bytes)") + } + fd, err := open(file) + if err != nil { + return nil, err + } + defer closefd(fd) + + buf := make([]byte, headersize) + if err := preadn(fd, buf, 0); err != nil { + return nil, errors.New("corrupt tzdata file " + file) + } + d := data{buf, false} + if magic := d.read(6); string(magic) != "tzdata" { + return nil, errors.New("corrupt tzdata file " + file) + } + d = data{buf[12:], false} + indexOff, _ := d.big4() + dataOff, _ := d.big4() + indexSize := dataOff - indexOff + entrycount := indexSize / entrysize + buf = make([]byte, indexSize) + if err := preadn(fd, buf, int(indexOff)); err != nil { + return nil, errors.New("corrupt tzdata file " + file) + } + for i := 0; i < int(entrycount); i++ { + entry := buf[i*entrysize : (i+1)*entrysize] + // len(name) <= namesize is checked at function entry + if string(entry[:len(name)]) != name { + continue + } + d := data{entry[namesize:], false} + off, _ := d.big4() + size, _ := d.big4() + buf := make([]byte, size) + if err := preadn(fd, buf, int(off+dataOff)); err != nil { + return nil, errors.New("corrupt tzdata file " + file) + } + return loadZoneData(buf) + } + return nil, errors.New("cannot find " + name + " in tzdata file " + file) +} diff --git a/libgo/go/time/zoneinfo_android_test.go b/libgo/go/time/zoneinfo_android_test.go new file mode 100644 index 0000000000..ba065d10a6 --- /dev/null +++ b/libgo/go/time/zoneinfo_android_test.go @@ -0,0 +1,18 @@ +// Copyright 2016 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 ( + "testing" + . "time" +) + +func TestAndroidTzdata(t *testing.T) { + ForceAndroidTzdataForTest(true) + defer ForceAndroidTzdataForTest(false) + if _, err := LoadLocation("America/Los_Angeles"); err != nil { + t.Error(err) + } +} diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go index de9ebb41c8..19cd40d847 100644 --- a/libgo/go/time/zoneinfo_read.go +++ b/libgo/go/time/zoneinfo_read.go @@ -11,6 +11,13 @@ package time import "errors" +// Copies of io.Seek* constants to avoid importing "io": +const ( + seekStart = 0 + seekCurrent = 1 + seekEnd = 2 +) + // Simple I/O interface to binary blob of data. type data struct { p []byte @@ -210,10 +217,10 @@ func loadZoneFile(dir, name string) (l *Location, err error) { return loadZoneData(buf) } -// There are 500+ zoneinfo files. Rather than distribute them all +// There are 500+ zoneinfo files. Rather than distribute them all // individually, we ship them in an uncompressed zip file. // Used this way, the zip file format serves as a commonly readable -// container for the individual small files. We choose zip over tar +// container for the individual small files. We choose zip over tar // because zip files have a contiguous table of contents, making // individual file lookups faster, and because the per-file overhead // in a zip file is considerably less than tar's 512 bytes. diff --git a/libgo/go/time/zoneinfo_test.go b/libgo/go/time/zoneinfo_test.go index ede5330f5c..5b6a4dc4e4 100644 --- a/libgo/go/time/zoneinfo_test.go +++ b/libgo/go/time/zoneinfo_test.go @@ -20,7 +20,7 @@ func TestVersion3(t *testing.T) { } // Test that we get the correct results for times before the first -// transition time. To do this we explicitly check early dates in a +// transition time. To do this we explicitly check early dates in a // couple of specific timezones. func TestFirstZone(t *testing.T) { t.Skip("gccgo does not use the zip file") @@ -64,3 +64,12 @@ func TestFirstZone(t *testing.T) { } } } + +func TestLocationNames(t *testing.T) { + if time.Local.String() != "Local" { + t.Errorf(`invalid Local location name: got %q want "Local"`, time.Local) + } + if time.UTC.String() != "UTC" { + t.Errorf(`invalid UTC location name: got %q want "UTC"`, time.UTC) + } +} diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go index 5fc669e478..772748818a 100644 --- a/libgo/go/time/zoneinfo_unix.go +++ b/libgo/go/time/zoneinfo_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin,386 darwin,amd64 dragonfly freebsd linux nacl netbsd openbsd solaris +// +build darwin,386 darwin,amd64 dragonfly freebsd linux,!android nacl netbsd openbsd solaris // Parse "zoneinfo" time zone file. // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. @@ -18,8 +18,12 @@ import ( ) func initTestingZone() { - syscall.Setenv("TZ", "America/Los_Angeles") - initLocal() + z, err := loadLocation("America/Los_Angeles") + if err != nil { + panic("cannot load America/Los_Angeles for testing: " + err.Error()) + } + z.name = "Local" + localLoc = *z } // Many systems use /usr/share/zoneinfo, Solaris 2 has diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go index bcb8ccd563..a6e227b5b0 100644 --- a/libgo/go/time/zoneinfo_windows.go +++ b/libgo/go/time/zoneinfo_windows.go @@ -11,8 +11,6 @@ import ( "syscall" ) -//go:generate go run genzabbrs.go -output zoneinfo_abbrs_windows.go - // TODO(rsc): Fall back to copy of zoneinfo files. // BUG(brainman,rsc): On Windows, the operating system does not provide complete @@ -83,7 +81,7 @@ func extractCAPS(desc string) string { var short []rune for _, c := range desc { if 'A' <= c && c <= 'Z' { - short = append(short, rune(c)) + short = append(short, c) } } return string(short) @@ -140,6 +138,8 @@ func pseudoUnix(year int, d *syscall.Systemtime) int64 { func initLocalFromTZI(i *syscall.Timezoneinformation) { l := &localLoc + l.name = "Local" + nzone := 1 if i.StandardDate.Month > 0 { nzone++ |
