diff options
Diffstat (limited to 'libgo/go/time')
-rw-r--r-- | libgo/go/time/example_test.go | 15 | ||||
-rw-r--r-- | libgo/go/time/format.go | 13 | ||||
-rw-r--r-- | libgo/go/time/format_test.go | 21 | ||||
-rw-r--r-- | libgo/go/time/sleep.go | 3 | ||||
-rw-r--r-- | libgo/go/time/sleep_test.go | 4 | ||||
-rw-r--r-- | libgo/go/time/time.go | 44 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo.go | 13 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_android.go | 3 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_js.go | 67 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_read.go | 15 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_test.go | 11 |
11 files changed, 181 insertions, 28 deletions
diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go index 494a416..0fd325f 100644 --- a/libgo/go/time/example_test.go +++ b/libgo/go/time/example_test.go @@ -132,7 +132,7 @@ func ExampleAfter() { select { case m := <-c: handle(m) - case <-time.After(5 * time.Minute): + case <-time.After(10 * time.Second): fmt.Println("timed out") } } @@ -144,7 +144,7 @@ func ExampleSleep() { func statusUpdate() string { return "" } func ExampleTick() { - c := time.Tick(1 * time.Minute) + c := time.Tick(5 * time.Second) for now := range c { fmt.Printf("%v %s\n", now, statusUpdate()) } @@ -429,6 +429,17 @@ func ExampleTime_Truncate() { // t.Truncate(10m0s) = 12:10:00 } +func ExampleLoadLocation() { + location, err := time.LoadLocation("America/Los_Angeles") + if err != nil { + panic(err) + } + + timeInUTC := time.Date(2018, 8, 30, 12, 0, 0, 0, time.UTC) + fmt.Println(timeInUTC.In(location)) + // Output: 2018-08-30 05:00:00 -0700 PDT +} + func ExampleLocation() { // China doesn't have daylight saving. It uses a fixed 8 hour offset from UTC. secondsEastOfUTC := int((8 * time.Hour).Seconds()) diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index 237f287..2adbbe0 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -1120,7 +1120,8 @@ func parseTimeZone(value string) (length int, ok bool) { // Special Case 3: Some time zones are not named, but have +/-00 format if value[0] == '+' || value[0] == '-' { length = parseSignedOffset(value) - return length, true + ok := length > 0 // parseSignedOffset returns 0 in case of bad input + return length, ok } // How many upper-case letters are there? Need at least three, at most five. var nUpper int @@ -1152,7 +1153,7 @@ func parseTimeZone(value string) (length int, ok bool) { // parseGMT parses a GMT time zone. The input string is known to start "GMT". // The function checks whether that is followed by a sign and a number in the -// range -14 through 12 excluding zero. +// range -23 through +23 excluding zero. func parseGMT(value string) int { value = value[3:] if len(value) == 0 { @@ -1163,7 +1164,7 @@ func parseGMT(value string) int { } // parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04"). -// The function checks for a signed number in the range -14 through +12 excluding zero. +// The function checks for a signed number in the range -23 through +23 excluding zero. // Returns length of the found offset string or 0 otherwise func parseSignedOffset(value string) int { sign := value[0] @@ -1171,13 +1172,15 @@ func parseSignedOffset(value string) int { return 0 } x, rem, err := leadingInt(value[1:]) - if err != nil { + + // fail if nothing consumed by leadingInt + if err != nil || value[1:] == rem { return 0 } if sign == '-' { x = -x } - if x == 0 || x < -14 || 12 < x { + if x < -23 || 23 < x { return 0 } return len(value) - len(rem) diff --git a/libgo/go/time/format_test.go b/libgo/go/time/format_test.go index 72dd592..0b9f3f4 100644 --- a/libgo/go/time/format_test.go +++ b/libgo/go/time/format_test.go @@ -418,7 +418,11 @@ var parseTimeZoneTests = []ParseTimeZoneTest{ {"gmt hi there", 0, false}, {"GMT hi there", 3, true}, {"GMT+12 hi there", 6, true}, - {"GMT+00 hi there", 3, true}, // 0 or 00 is not a legal offset. + {"GMT+00 hi there", 6, true}, + {"GMT+", 3, true}, + {"GMT+3", 5, true}, + {"GMT+a", 3, true}, + {"GMT+3a", 5, true}, {"GMT-5 hi there", 5, true}, {"GMT-51 hi there", 3, true}, {"ChST hi there", 4, true}, @@ -429,8 +433,19 @@ var parseTimeZoneTests = []ParseTimeZoneTest{ {"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 - {"+03 hi", 3, true}, // Issue #24071 - {"-04 hi", 3, true}, // Issue #24071 + // Issue #24071 + {"+03 hi", 3, true}, + {"-04 hi", 3, true}, + // Issue #26032 + {"+00", 3, true}, + {"-11", 3, true}, + {"-12", 3, true}, + {"-23", 3, true}, + {"-24", 0, false}, + {"+13", 3, true}, + {"+14", 3, true}, + {"+23", 3, true}, + {"+24", 0, false}, } func TestParseTimeZone(t *testing.T) { diff --git a/libgo/go/time/sleep.go b/libgo/go/time/sleep.go index b8c81b4..10edf6f 100644 --- a/libgo/go/time/sleep.go +++ b/libgo/go/time/sleep.go @@ -8,9 +8,6 @@ package time // A negative or zero duration causes Sleep to return immediately. func Sleep(d Duration) -// runtimeNano returns the current value of the runtime clock in nanoseconds. -func runtimeNano() int64 - // Interface to timers implemented in package runtime. // Must be in sync with ../runtime/time.go:/^type timer type runtimeTimer struct { diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go index e6a08fc..42df6d1 100644 --- a/libgo/go/time/sleep_test.go +++ b/libgo/go/time/sleep_test.go @@ -426,10 +426,6 @@ func TestOverflowSleep(t *testing.T) { // Test that a panic while deleting a timer does not leave // the timers mutex held, deadlocking a ticker.Stop in a defer. func TestIssue5745(t *testing.T) { - if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" { - t.Skipf("skipping on %s/%s, see issue 10043", runtime.GOOS, runtime.GOARCH) - } - ticker := NewTicker(Hour) defer func() { // would deadlock here before the fix due to diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index 2374043..d0d780f 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -75,7 +75,10 @@ // package time -import "errors" +import ( + "errors" + _ "unsafe" // for go:linkname +) // A Time represents an instant in time with nanosecond precision. // @@ -102,6 +105,10 @@ import "errors" // change the instant in time being denoted and therefore does not affect the // computations described in earlier paragraphs. // +// Representations of a Time value saved by the GobEncode, MarshalBinary, +// MarshalJSON, and MarshalText methods store the Time.Location's offset, but not +// the location name. They therefore lose information about Daylight Saving Time. +// // 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. @@ -908,13 +915,27 @@ func (t Time) Sub(u Time) Duration { // Since returns the time elapsed since t. // It is shorthand for time.Now().Sub(t). func Since(t Time) Duration { - return Now().Sub(t) + var now Time + if t.wall&hasMonotonic != 0 { + // Common case optimization: if t has monotomic time, then Sub will use only it. + now = Time{hasMonotonic, runtimeNano() - startNano, nil} + } else { + now = Now() + } + 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()) + var now Time + if t.wall&hasMonotonic != 0 { + // Common case optimization: if t has monotomic time, then Sub will use only it. + now = Time{hasMonotonic, runtimeNano() - startNano, nil} + } else { + now = Now() + } + return t.Sub(now) } // AddDate returns the time corresponding to adding the @@ -933,7 +954,7 @@ func (t Time) AddDate(years int, months int, days int) Time { const ( secondsPerMinute = 60 - secondsPerHour = 60 * 60 + secondsPerHour = 60 * secondsPerMinute secondsPerDay = 24 * secondsPerHour secondsPerWeek = 7 * secondsPerDay daysPer400Years = 365*400 + 97 @@ -1050,9 +1071,22 @@ func daysIn(m Month, year int) int { // Provided by package runtime. func now() (sec int64, nsec int32, mono int64) +// runtimeNano returns the current value of the runtime clock in nanoseconds. +//go:linkname runtimeNano runtime.nanotime +func runtimeNano() int64 + +// Monotonic times are reported as offsets from startNano. +// We initialize startNano to runtimeNano() - 1 so that on systems where +// monotonic time resolution is fairly low (e.g. Windows 2008 +// which appears to have a default resolution of 15ms), +// we avoid ever reporting a monotonic time of 0. +// (Callers may want to use 0 as "time not set".) +var startNano int64 = runtimeNano() - 1 + // Now returns the current local time. func Now() Time { sec, nsec, mono := now() + mono -= startNano sec += unixToInternal - minWall if uint64(sec)>>33 != 0 { return Time{uint64(nsec), sec + minWall, Local} @@ -1076,7 +1110,7 @@ func (t Time) Local() Time { return t } -// In returns a copy of t representating the same time instant, but +// In returns a copy of t representing the same time instant, but // with the copy's location information set to loc for display // purposes. // diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go index d2bc642..7dffbfa 100644 --- a/libgo/go/time/zoneinfo.go +++ b/libgo/go/time/zoneinfo.go @@ -205,7 +205,7 @@ func (l *Location) lookupFirstZone() int { return 0 } -// firstZoneUsed returns whether the first zone is used by some +// firstZoneUsed reports whether the first zone is used by some // transition. func (l *Location) firstZoneUsed() bool { for _, tx := range l.tx { @@ -288,14 +288,23 @@ func LoadLocation(name string) (*Location, error) { env, _ := syscall.Getenv("ZONEINFO") zoneinfo = &env }) + var firstErr error if *zoneinfo != "" { if zoneData, err := loadTzinfoFromDirOrZip(*zoneinfo, name); err == nil { if z, err := LoadLocationFromTZData(name, zoneData); err == nil { return z, nil } + firstErr = err + } else if err != syscall.ENOENT { + firstErr = err } } - return loadLocation(name, zoneSources) + if z, err := loadLocation(name, zoneSources); err == nil { + return z, nil + } else if firstErr == nil { + firstErr = err + } + return nil, firstErr } // containsDotDot reports whether s contains "..". diff --git a/libgo/go/time/zoneinfo_android.go b/libgo/go/time/zoneinfo_android.go index 65e0975..237ff20 100644 --- a/libgo/go/time/zoneinfo_android.go +++ b/libgo/go/time/zoneinfo_android.go @@ -11,6 +11,7 @@ package time import ( "errors" "runtime" + "syscall" ) var zoneSources = []string{ @@ -75,5 +76,5 @@ func androidLoadTzinfoFromTzdata(file, name string) ([]byte, error) { } return buf, nil } - return nil, errors.New("cannot find " + name + " in tzdata file " + file) + return nil, syscall.ENOENT } diff --git a/libgo/go/time/zoneinfo_js.go b/libgo/go/time/zoneinfo_js.go new file mode 100644 index 0000000..2d76a57 --- /dev/null +++ b/libgo/go/time/zoneinfo_js.go @@ -0,0 +1,67 @@ +// Copyright 2018 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. + +// +build js,wasm + +package time + +import ( + "runtime" + "syscall/js" +) + +var zoneSources = []string{ + "/usr/share/zoneinfo/", + "/usr/share/lib/zoneinfo/", + "/usr/lib/locale/TZ/", + runtime.GOROOT() + "/lib/time/zoneinfo.zip", +} + +func initLocal() { + localLoc.name = "Local" + + z := zone{} + d := js.Global().Get("Date").New() + offset := d.Call("getTimezoneOffset").Int() * -1 + z.offset = offset * 60 + // According to https://tc39.github.io/ecma262/#sec-timezoneestring, + // the timezone name from (new Date()).toTimeString() is an implementation-dependent + // result, and in Google Chrome, it gives the fully expanded name rather than + // the abbreviation. + // Hence, we construct the name from the offset. + z.name = "UTC" + if offset < 0 { + z.name += "-" + offset *= -1 + } else { + z.name += "+" + } + z.name += itoa(offset / 60) + min := offset % 60 + if min != 0 { + z.name += ":" + itoa(min) + } + localLoc.zone = []zone{z} +} + +// itoa is like strconv.Itoa but only works for values of i in range [0,99]. +// It panics if i is out of range. +func itoa(i int) string { + if i < 10 { + return digits[i : i+1] + } + return smallsString[i*2 : i*2+2] +} + +const smallsString = "00010203040506070809" + + "10111213141516171819" + + "20212223242526272829" + + "30313233343536373839" + + "40414243444546474849" + + "50515253545556575859" + + "60616263646566676869" + + "70717273747576777879" + + "80818283848586878889" + + "90919293949596979899" +const digits = "0123456789" diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go index 20f84f0..d8d4070 100644 --- a/libgo/go/time/zoneinfo_read.go +++ b/libgo/go/time/zoneinfo_read.go @@ -11,6 +11,7 @@ package time import ( "errors" + "runtime" "syscall" ) @@ -55,7 +56,7 @@ func (d *dataIO) big4() (n uint32, ok bool) { d.error = true return 0, false } - return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true + return uint32(p[3]) | uint32(p[2])<<8 | uint32(p[1])<<16 | uint32(p[0])<<24, true } func (d *dataIO) byte() (n byte, ok bool) { @@ -172,6 +173,14 @@ func LoadLocationFromTZData(name string, data []byte) (*Location, error) { return nil, badData } zone[i].name = byteString(abbrev[b:]) + if runtime.GOOS == "aix" && len(name) > 8 && (name[:8] == "Etc/GMT+" || name[:8] == "Etc/GMT-") { + // There is a bug with AIX 7.2 TL 0 with files in Etc, + // GMT+1 will return GMT-1 instead of GMT+1 or -01. + if name != "Etc/GMT+0" { + // GMT+0 is OK + zone[i].name = name[4:] + } + } } // Now the transition time info. @@ -262,7 +271,7 @@ func get2(b []byte) int { func loadTzinfoFromZip(zipfile, name string) ([]byte, error) { fd, err := open(zipfile) if err != nil { - return nil, errors.New("open " + zipfile + ": " + err.Error()) + return nil, err } defer closefd(fd) @@ -364,7 +373,7 @@ func loadTzinfoFromZip(zipfile, name string) ([]byte, error) { return buf, nil } - return nil, errors.New("cannot find " + name + " in zip file " + zipfile) + return nil, syscall.ENOENT } // loadTzinfoFromTzdata returns the time zone information of the time zone diff --git a/libgo/go/time/zoneinfo_test.go b/libgo/go/time/zoneinfo_test.go index 9bf8c4f..2f7612d 100644 --- a/libgo/go/time/zoneinfo_test.go +++ b/libgo/go/time/zoneinfo_test.go @@ -5,6 +5,7 @@ package time_test import ( + "errors" "fmt" "os" "reflect" @@ -36,6 +37,16 @@ func TestEnvVarUsage(t *testing.T) { } } +func TestBadLocationErrMsg(t *testing.T) { + time.ResetZoneinfoForTesting() + loc := "Asia/SomethingNotExist" + want := errors.New("unknown time zone " + loc) + _, err := time.LoadLocation(loc) + if err.Error() != want.Error() { + t.Errorf("LoadLocation(%q) error = %v; want %v", loc, err, want) + } +} + func TestLoadLocationValidatesNames(t *testing.T) { time.ResetZoneinfoForTesting() const env = "ZONEINFO" |