Use "time" to handle time
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 for instants of time
Use time.Time when dealing with instants of time, and the methods on
time.Time when comparing, adding, or subtracting time.
| Bad | Good |
|---|---|
| ```go func isActive(now, start, stop int) bool { return start <= now="" &&="" <="" stop="" }="" ```="" td=""> | ```go func isActive(now, start, stop time.Time) bool { return (start.Before(now) || start.Equal(now)) && now.Before(stop) } ``` | =>
Use time.Duration for periods of time
Use time.Duration when dealing with periods of time.
| Bad | Good |
|---|---|
| ```go func poll(delay int) { for { // ... time.Sleep(time.Duration(delay) * time.Millisecond) } } poll(10) // was it seconds or milliseconds? ``` | ```go 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.Time and time.Duration with external systems
Use time.Duration and time.Time in interactions with external systems when
possible. For example:
- Command-line flags:
flagsupportstime.Durationviatime.ParseDuration - JSON:
encoding/jsonsupports encodingtime.Timeas an RFC 3339 string via itsUnmarshalJSONmethod - SQL:
database/sqlsupports convertingDATETIMEorTIMESTAMPcolumns intotime.Timeand back if the underlying driver supports it - YAML:
gopkg.in/yaml.v2supportstime.Timeas an RFC 3339 string, andtime.Durationviatime.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 |
|---|---|
| ```go // {"interval": 2} type Config struct { Interval int `json:"interval"` } ``` | ```go // {"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.