Time handling is quite complex. Common incorrect assumptions about time include the following.
- A day consists of 24 hours.
- An hour contains 60 minutes.
- A week consists of seven days.
- A year comprises 365 days.
For example, 1 indicates that adding 24 hours to a specific point in time does not always result in a new calendar day.
Therefore, when dealing with time, always use the "time"
package as it helps to handle these incorrect assumptions in a safer and more accurate manner.
Using time.Time
to Represent Instantaneous Time
When dealing with instantaneous time, use time.Time
and its methods for comparing, adding, or subtracting time.
Not Recommended:
func isActive(now, start, stop int) bool {
return start <= now && now < stop
}
Recommended:
func isActive(now, start, stop time.Time) bool {
return (start.Before(now) || start.Equal(now)) && now.Before(stop)
}
Using time.Duration
to Represent Time Durations
When dealing with time durations, use time.Duration
.
Not Recommended:
func poll(delay int) {
for {
// ...
time.Sleep(time.Duration(delay) * time.Millisecond)
}
}
poll(10) // Is it in seconds or milliseconds?
Recommended:
func poll(delay time.Duration) {
for {
// ...
time.Sleep(delay)
}
}
poll(10*time.Second)
Returning to the first example, when adding 24 hours to an instantaneous time, the method used depends on the intent. If we want the same time point on the next calendar day (the day after the current day), we should use Time.AddDate
. However, if we want to ensure that a certain moment is 24 hours later than the previous one, we should use Time.Add
.
newDay := t.AddDate(0 /* years */, 0 /* months */, 1 /* days */)
maybeNewDay := t.Add(24 * time.Hour)
Using time.Time
and time.Duration
with External Systems
Whenever possible, use time.Duration
and time.Time
in interaction with external systems, for example:
- Command-line flags:
flag
supportstime.Duration
throughtime.ParseDuration
. - JSON:
encoding/json
supports encodingtime.Time
to an RFC 3339 string using itsUnmarshalJSON
method. - SQL:
database/sql
supports convertingDATETIME
orTIMESTAMP
columns totime.Time
, and returns if the underlying driver supports it. - YAML:
gopkg.in/yaml.v2
supports usingtime.Time
as an RFC 3339 string andtime.Duration
throughtime.ParseDuration
.
When time.Duration
cannot be used in these interactions, use int
or float64
and include the unit in the field name.
For example, since encoding/json
does not support time.Duration
, the unit is included in the field name.
Not recommended:
// {"interval": 2}
type Config struct {
Interval int `json:"interval"`
}
Recommended:
// {"intervalMillis": 2000}
type Config struct {
IntervalMillis int `json:"intervalMillis"`
}
When time.Time
cannot be used in these interactions, unless otherwise agreed upon, use string
and format timestamps according to RFC 3339. By default, Time.UnmarshalText
uses this format and it can be used in Time.Format
and time.Parse
through time.RFC3339
.
Although not a practical problem, please note that the "time"
package does not support parsing leap second timestamps (8728), and does not take leap seconds into account in calculations (15190). If you compare two moments in time, the difference will not include any leap seconds that may have occurred between those two moments.