Time is complicated. Incorrect assumptions often made about time include the following.
- A day has 24 hours
- An hour has 60 minutes
- A week has 7 days
- A year has 365 days
- And a lot more
For example, 1 means that adding 24 hours to a time instant will not always yield a new calendar day.
Therefore, always use the "time"
package when dealing with time because it
helps deal with these incorrect assumptions in a safer, more accurate manner.
Use time.Time
when dealing with instants of time, and the methods on
time.Time
when comparing, adding, or subtracting time.
Bad | Good |
---|---|
func isActive(now, start, stop int) bool {
return start <= now && now < stop
} |
func isActive(now, start, stop time.Time) bool {
return (start.Before(now) || start.Equal(now)) && now.Before(stop)
} |
Use time.Duration
when dealing with periods of time.
Bad | Good |
---|---|
func poll(delay int) {
for {
// ...
time.Sleep(time.Duration(delay) * time.Millisecond)
}
}
poll(10) // was it seconds or milliseconds? |
func poll(delay time.Duration) {
for {
// ...
time.Sleep(delay)
}
}
poll(10*time.Second) |
Going back to the example of adding 24 hours to a time instant, the method we
use to add time depends on intent. If we want the same time of the day, but on
the next calendar day, we should use Time.AddDate
. However, if we want an
instant of time guaranteed to be 24 hours after the previous time, we should
use Time.Add
.
newDay := t.AddDate(0 /* years */, 0 /* months */, 1 /* days */)
maybeNewDay := t.Add(24 * time.Hour)
Use time.Duration
and time.Time
in interactions with external systems when
possible. For example:
- Command-line flags:
flag
supportstime.Duration
viatime.ParseDuration
- JSON:
encoding/json
supports encodingtime.Time
as an RFC 3339 string via itsUnmarshalJSON
method - SQL:
database/sql
supports convertingDATETIME
orTIMESTAMP
columns intotime.Time
and back if the underlying driver supports it - YAML:
gopkg.in/yaml.v2
supportstime.Time
as an RFC 3339 string, andtime.Duration
viatime.ParseDuration
.
When it is not possible to use time.Duration
in these interactions, use
int
or float64
and include the unit in the name of the field.
For example, since encoding/json
does not support time.Duration
, the unit
is included in the name of the field.
Bad | Good |
---|---|
// {"interval": 2}
type Config struct {
Interval int `json:"interval"`
} |
// {"intervalMillis": 2000}
type Config struct {
IntervalMillis int `json:"intervalMillis"`
} |
When it is not possible to use time.Time
in these interactions, unless an
alternative is agreed upon, use string
and format timestamps as defined in
RFC 3339. This format is used by default by Time.UnmarshalText
and is
available for use in Time.Format
and time.Parse
via time.RFC3339
.
Although this tends to not be a problem in practice, keep in mind that the
"time"
package does not support parsing timestamps with leap seconds
(8728), nor does it account for leap seconds in calculations (15190). If
you compare two instants of time, the difference will not include the leap
seconds that may have occurred between those two instants.