diff options
Diffstat (limited to 'src/time')
-rw-r--r-- | src/time/example_test.go | 22 | ||||
-rw-r--r-- | src/time/sleep.go | 1 | ||||
-rw-r--r-- | src/time/time.go | 79 | ||||
-rw-r--r-- | src/time/time_test.go | 18 | ||||
-rw-r--r-- | src/time/zoneinfo.go | 6 | ||||
-rw-r--r-- | src/time/zoneinfo_read.go | 2 | ||||
-rw-r--r-- | src/time/zoneinfo_windows.go | 14 | ||||
-rw-r--r-- | src/time/zoneinfo_windows_test.go | 10 |
8 files changed, 94 insertions, 58 deletions
diff --git a/src/time/example_test.go b/src/time/example_test.go index 059c6310a6..cfdee8f4d7 100644 --- a/src/time/example_test.go +++ b/src/time/example_test.go @@ -591,19 +591,33 @@ func ExampleTime_Add() { } func ExampleTime_AddDate() { - start := time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC) + start := time.Date(2023, 03, 25, 12, 0, 0, 0, time.UTC) oneDayLater := start.AddDate(0, 0, 1) + dayDuration := oneDayLater.Sub(start) oneMonthLater := start.AddDate(0, 1, 0) oneYearLater := start.AddDate(1, 0, 0) + zurich, err := time.LoadLocation("Europe/Zurich") + if err != nil { + panic(err) + } + // This was the day before a daylight saving time transition in Zürich. + startZurich := time.Date(2023, 03, 25, 12, 0, 0, 0, zurich) + oneDayLaterZurich := startZurich.AddDate(0, 0, 1) + dayDurationZurich := oneDayLaterZurich.Sub(startZurich) + fmt.Printf("oneDayLater: start.AddDate(0, 0, 1) = %v\n", oneDayLater) fmt.Printf("oneMonthLater: start.AddDate(0, 1, 0) = %v\n", oneMonthLater) fmt.Printf("oneYearLater: start.AddDate(1, 0, 0) = %v\n", oneYearLater) + fmt.Printf("oneDayLaterZurich: startZurich.AddDate(0, 0, 1) = %v\n", oneDayLaterZurich) + fmt.Printf("Day duration in UTC: %v | Day duration in Zürich: %v\n", dayDuration, dayDurationZurich) // Output: - // oneDayLater: start.AddDate(0, 0, 1) = 2009-01-02 00:00:00 +0000 UTC - // oneMonthLater: start.AddDate(0, 1, 0) = 2009-02-01 00:00:00 +0000 UTC - // oneYearLater: start.AddDate(1, 0, 0) = 2010-01-01 00:00:00 +0000 UTC + // oneDayLater: start.AddDate(0, 0, 1) = 2023-03-26 12:00:00 +0000 UTC + // oneMonthLater: start.AddDate(0, 1, 0) = 2023-04-25 12:00:00 +0000 UTC + // oneYearLater: start.AddDate(1, 0, 0) = 2024-03-25 12:00:00 +0000 UTC + // oneDayLaterZurich: startZurich.AddDate(0, 0, 1) = 2023-03-26 12:00:00 +0200 CEST + // Day duration in UTC: 24h0m0s | Day duration in Zürich: 23h0m0s } func ExampleTime_After() { diff --git a/src/time/sleep.go b/src/time/sleep.go index cdab4782ad..0aec4cacc6 100644 --- a/src/time/sleep.go +++ b/src/time/sleep.go @@ -160,6 +160,7 @@ func After(d Duration) <-chan Time { // AfterFunc waits for the duration to elapse and then calls f // in its own goroutine. It returns a Timer that can // be used to cancel the call using its Stop method. +// The returned Timer's C field is not used and will be nil. func AfterFunc(d Duration, f func()) *Timer { t := &Timer{ r: runtimeTimer{ diff --git a/src/time/time.go b/src/time/time.go index e8aac5999a..9d4c6e919e 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -76,6 +76,14 @@ // 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(). +// +// # Timer Resolution +// +// Timer resolution varies depending on the Go runtime, the operating system +// and the underlying hardware. +// On Unix, the resolution is approximately 1ms. +// On Windows, the default resolution is approximately 16ms, but +// a higher resolution may be requested using [golang.org/x/sys/windows.TimeBeginPeriod]. package time import ( @@ -101,12 +109,10 @@ import ( // As this time is unlikely to come up in practice, the IsZero method gives // a simple way of detecting a time that has not been initialized explicitly. // -// Each Time has associated with it a Location, consulted when computing the -// presentation form of the time, such as in the Format, Hour, and Year methods. -// The methods Local, UTC, and In return a Time with a specific location. -// Changing the location in this way changes only the presentation; it does not -// change the instant in time being denoted and therefore does not affect the -// computations described in earlier paragraphs. +// Each time has an associated Location. The methods Local, UTC, and In return a +// Time with a specific Location. Changing the Location of a Time value with +// these methods does not change the actual instant it represents, only the time +// zone in which to interpret it. // // Representations of a Time value saved by the GobEncode, MarshalBinary, // MarshalJSON, and MarshalText methods store the Time.Location's offset, but not @@ -642,8 +648,17 @@ const ( // second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure // that the leading digit is non-zero. The zero duration formats as 0s. func (d Duration) String() string { + // This is inlinable to take advantage of "function outlining". + // Thus, the caller can decide whether a string must be heap allocated. + var arr [32]byte + n := d.format(&arr) + return string(arr[n:]) +} + +// format formats the representation of d into the end of buf and +// returns the offset of the first character. +func (d Duration) format(buf *[32]byte) int { // Largest time is 2540400h10m10.000000000s - var buf [32]byte w := len(buf) u := uint64(d) @@ -661,7 +676,8 @@ func (d Duration) String() string { w-- switch { case u == 0: - return "0s" + buf[w] = '0' + return w case u < uint64(Microsecond): // print nanoseconds prec = 0 @@ -711,7 +727,7 @@ func (d Duration) String() string { buf[w] = '-' } - return string(buf[w:]) + return w } // fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the @@ -883,16 +899,7 @@ func (t Time) Add(d Duration) Time { // To compute t-d for a duration d, use t.Add(-d). func (t Time) Sub(u Time) Duration { if t.wall&u.wall&hasMonotonic != 0 { - te := t.ext - ue := 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 + return subMono(t.ext, u.ext) } d := Duration(t.sec()-u.sec())*Second + Duration(t.nsec()-u.nsec()) // Check for overflow or underflow. @@ -906,30 +913,35 @@ func (t Time) Sub(u Time) Duration { } } +func subMono(t, u int64) Duration { + d := Duration(t - u) + if d < 0 && t > u { + return maxDuration // t - u is positive out of range + } + if d > 0 && t < u { + return minDuration // t - u is negative out of range + } + return d +} + // Since returns the time elapsed since t. // It is shorthand for time.Now().Sub(t). func Since(t Time) Duration { - var now Time if t.wall&hasMonotonic != 0 { // Common case optimization: if t has monotonic time, then Sub will use only it. - now = Time{hasMonotonic, runtimeNano() - startNano, nil} - } else { - now = Now() + return subMono(runtimeNano()-startNano, t.ext) } - return now.Sub(t) + return Now().Sub(t) } // Until returns the duration until t. // It is shorthand for t.Sub(time.Now()). func Until(t Time) Duration { - var now Time if t.wall&hasMonotonic != 0 { // Common case optimization: if t has monotonic time, then Sub will use only it. - now = Time{hasMonotonic, runtimeNano() - startNano, nil} - } else { - now = Now() + return subMono(t.ext, runtimeNano()-startNano) } - return t.Sub(now) + return t.Sub(Now()) } // AddDate returns the time corresponding to adding the @@ -937,6 +949,15 @@ func Until(t Time) Duration { // For example, AddDate(-1, 2, 3) applied to January 1, 2011 // returns March 4, 2010. // +// Note that dates are fundamentally coupled to timezones, and calendrical +// periods like days don't have fixed durations. AddDate uses the Location of +// the Time value to determine these durations. That means that the same +// AddDate arguments can produce a different shift in absolute time depending on +// the base Time value and its Location. For example, AddDate(0, 0, 1) applied +// to 12:00 on March 27 always returns 12:00 on March 28. At some locations and +// in some years this is a 24 hour shift. In others it's a 23 hour shift due to +// daylight savings time transitions. +// // AddDate normalizes its result in the same way that Date does, // so, for example, adding one month to October 31 yields // December 1, the normalized form for November 31. diff --git a/src/time/time_test.go b/src/time/time_test.go index 3b30f802ef..86335e3796 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -283,7 +283,7 @@ func TestTruncateRound(t *testing.T) { testOne := func(ti, tns, di int64) bool { t.Helper() - t0 := Unix(ti, int64(tns)).UTC() + t0 := Unix(ti, tns).UTC() d := Duration(di) if d < 0 { d = -d @@ -321,7 +321,7 @@ func TestTruncateRound(t *testing.T) { // The commented out code would round half to even instead of up, // but that makes it time-zone dependent, which is a bit strange. if r > int64(d)/2 || r+r == int64(d) /*&& bq.Bit(0) == 1*/ { - t1 = t1.Add(Duration(d)) + t1 = t1.Add(d) } // Check that time.Round works. @@ -1106,14 +1106,14 @@ var subTests = []struct { {Date(2009, 11, 23, 0, 0, 0, 0, UTC), Date(2009, 11, 24, 0, 0, 0, 0, UTC), -24 * Hour}, {Date(2009, 11, 24, 0, 0, 0, 0, UTC), Date(2009, 11, 23, 0, 0, 0, 0, UTC), 24 * Hour}, {Date(-2009, 11, 24, 0, 0, 0, 0, UTC), Date(-2009, 11, 23, 0, 0, 0, 0, UTC), 24 * Hour}, - {Time{}, Date(2109, 11, 23, 0, 0, 0, 0, UTC), Duration(minDuration)}, - {Date(2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, Duration(maxDuration)}, - {Time{}, Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Duration(maxDuration)}, - {Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, Duration(minDuration)}, + {Time{}, Date(2109, 11, 23, 0, 0, 0, 0, UTC), minDuration}, + {Date(2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, maxDuration}, + {Time{}, Date(-2109, 11, 23, 0, 0, 0, 0, UTC), maxDuration}, + {Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, minDuration}, {Date(2290, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), 290*365*24*Hour + 71*24*Hour}, - {Date(2300, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), Duration(maxDuration)}, + {Date(2300, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), maxDuration}, {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2290, 1, 1, 0, 0, 0, 0, UTC), -290*365*24*Hour - 71*24*Hour}, - {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2300, 1, 1, 0, 0, 0, 0, UTC), Duration(minDuration)}, + {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2300, 1, 1, 0, 0, 0, 0, UTC), minDuration}, {Date(2311, 11, 26, 02, 16, 47, 63535996, UTC), Date(2019, 8, 16, 2, 29, 30, 268436582, UTC), 9223372036795099414}, {MinMonoTime, MaxMonoTime, minDuration}, {MaxMonoTime, MinMonoTime, maxDuration}, @@ -1640,7 +1640,7 @@ func TestZeroMonthString(t *testing.T) { // Issue 24692: Out of range weekday panics func TestWeekdayString(t *testing.T) { - if got, want := Weekday(Tuesday).String(), "Tuesday"; got != want { + if got, want := Tuesday.String(), "Tuesday"; got != want { t.Errorf("Tuesday weekday = %q; want %q", got, want) } if got, want := Weekday(14).String(), "%!Weekday(14)"; got != want { diff --git a/src/time/zoneinfo.go b/src/time/zoneinfo.go index 4edcf3d98f..c8d1762302 100644 --- a/src/time/zoneinfo.go +++ b/src/time/zoneinfo.go @@ -16,6 +16,10 @@ import ( // Typically, the Location represents the collection of time offsets // in use in a geographical area. For many Locations the time offset varies // depending on whether daylight savings time is in use at the time instant. +// +// Location is used to provide a time zone in a printed Time value and for +// calculations involving intervals that may cross daylight savings time +// boundaries. type Location struct { name string zone []zone @@ -184,7 +188,7 @@ func (l *Location) lookup(sec int64) (name string, offset int, start, end int64, lo := 0 hi := len(tx) for hi-lo > 1 { - m := lo + (hi-lo)/2 + m := int(uint(lo+hi) >> 1) lim := tx[m].when if sec < lim { end = lim diff --git a/src/time/zoneinfo_read.go b/src/time/zoneinfo_read.go index 4d0e47d890..707dd1189d 100644 --- a/src/time/zoneinfo_read.go +++ b/src/time/zoneinfo_read.go @@ -90,7 +90,7 @@ func (d *dataIO) byte() (n byte, ok bool) { return p[0], true } -// read returns the read of the data in the buffer. +// rest returns the rest of the data in the buffer. func (d *dataIO) rest() []byte { r := d.p d.p = nil diff --git a/src/time/zoneinfo_windows.go b/src/time/zoneinfo_windows.go index 76d79759f7..c9f38ea3e0 100644 --- a/src/time/zoneinfo_windows.go +++ b/src/time/zoneinfo_windows.go @@ -20,8 +20,8 @@ var platformZoneSources []string // none: Windows uses system calls instead // time apply to all previous and future years as well. // matchZoneKey checks if stdname and dstname match the corresponding key -// values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level -// from Vista) in the kname key stored under the open registry key zones. +// values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" in the kname key stored +// under the open registry key zones. func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (matched bool, err2 error) { k, err := registry.OpenKey(zones, kname, registry.READ) if err != nil { @@ -30,12 +30,10 @@ func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (ma defer k.Close() var std, dlt string - if err = registry.LoadRegLoadMUIString(); err == nil { - // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs - std, err = k.GetMUIStringValue("MUI_Std") - if err == nil { - dlt, err = k.GetMUIStringValue("MUI_Dlt") - } + // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs + std, err = k.GetMUIStringValue("MUI_Std") + if err == nil { + dlt, err = k.GetMUIStringValue("MUI_Dlt") } if err != nil { // Fallback to Std and Dlt if std, _, err = k.GetStringValue("Std"); err != nil { diff --git a/src/time/zoneinfo_windows_test.go b/src/time/zoneinfo_windows_test.go index f23d9dcecb..5196b8e1de 100644 --- a/src/time/zoneinfo_windows_test.go +++ b/src/time/zoneinfo_windows_test.go @@ -45,12 +45,10 @@ func TestToEnglishName(t *testing.T) { defer k.Close() var std, dlt string - if err = registry.LoadRegLoadMUIString(); err == nil { - // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs - std, err = k.GetMUIStringValue("MUI_Std") - if err == nil { - dlt, err = k.GetMUIStringValue("MUI_Dlt") - } + // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs + std, err = k.GetMUIStringValue("MUI_Std") + if err == nil { + dlt, err = k.GetMUIStringValue("MUI_Dlt") } if err != nil { // Fallback to Std and Dlt if std, _, err = k.GetStringValue("Std"); err != nil { |