aboutsummaryrefslogtreecommitdiff
path: root/src/time
diff options
context:
space:
mode:
Diffstat (limited to 'src/time')
-rw-r--r--src/time/example_test.go22
-rw-r--r--src/time/sleep.go1
-rw-r--r--src/time/time.go79
-rw-r--r--src/time/time_test.go18
-rw-r--r--src/time/zoneinfo.go6
-rw-r--r--src/time/zoneinfo_read.go2
-rw-r--r--src/time/zoneinfo_windows.go14
-rw-r--r--src/time/zoneinfo_windows_test.go10
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 {