The basics in one program
time.Now() returns the current local time, time.Sleep pauses the current goroutine, time.Since measures elapsed time, and time.Date builds a specific moment. The rest of this page takes each piece in turn. The examples use fixed dates and UTC so their output is the same wherever you run them.
Sleep and Duration
A time.Duration is an int64 count of nanoseconds. The package defines constants to build readable values:
| Constant | Value |
|---|---|
time.Nanosecond | 1 |
time.Microsecond | 1000 ns |
time.Millisecond | 1000 µs |
time.Second | 1000 ms |
time.Minute | 60 s |
time.Hour | 60 min |
There is no time.Day, because a day is not always 24 hours when a time zone changes for daylight saving. Use AddDate for calendar days (see below).
Two common bugs come from Duration being an integer:
time.Sleep(5)sleeps 5 nanoseconds. Always multiply by a unit:time.Sleep(5 * time.Second).time.Sleep(n * time.Second)does not compile whennis anintvariable. Convert it:time.Duration(n) * time.Second. A constant like5works without conversion because untyped constants adapt to the Duration type.
ParseDuration accepts ns, us (or µs), ms, s, m and h, combined like "2h45m" or "1.5s". It has no unit for days.
Formatting: the 2006-01-02 reference layout
Go does not use %Y-%m-%d or yyyy-MM-dd. A layout is the reference time
Mon Jan 2 15:04:05 MST 2006
written the way you want your output to look. The values are chosen so each one is unique: month 1, day 2, hour 3 (or 15), minute 4, second 5, year 6 (2006), zone offset -7 (-0700). Read it as 01/02 03:04:05PM '06 -0700.
The layout tokens you will need most:
| Token | Meaning | Example |
|---|---|---|
2006 / 06 | year, 4 or 2 digits | 2026 / 26 |
01 / 1 / Jan / January | month | 03 / 3 / Mar / March |
02 / 2 / _2 | day of month (zero-padded, plain, space-padded) | 05 / 5 / " 5" |
Mon / Monday | weekday | Thu / Thursday |
15 | hour, 24-hour clock | 09 |
03 / 3 | hour, 12-hour clock | 09 / 9 |
04 / 4 | minute | 07 / 7 |
05 / 5 | second | 03 / 3 |
PM / pm | AM or PM marker | AM |
.000 / .999 | fractional seconds (fixed / trailing zeros trimmed) | .250 / .25 |
MST | zone abbreviation | UTC |
-0700 / -07:00 | numeric zone offset | +0000 / +00:00 |
Z07:00 | like -07:00, but prints Z for UTC | Z |
Predefined layouts:
| Constant | Layout |
|---|---|
time.RFC3339 | 2006-01-02T15:04:05Z07:00 (use this for APIs and JSON) |
time.RFC3339Nano | 2006-01-02T15:04:05.999999999Z07:00 |
time.DateTime (Go 1.20) | 2006-01-02 15:04:05 |
time.DateOnly (Go 1.20) | 2006-01-02 |
time.TimeOnly (Go 1.20) | 15:04:05 |
time.Kitchen | 3:04PM |
time.RFC1123 | Mon, 02 Jan 2006 15:04:05 MST (HTTP dates use http.TimeFormat instead) |
The classic mistake is writing a layout with the wrong numbers, for example "2023-01-01". Go does not reject it. It copies characters it does not recognize and substitutes the ones it does: each 2 is the day, 3 is the 12-hour clock hour and both 01s are the month, so March 5 at 9:07 formats as 5059-03-03. A layout like "YYYY-MM-DD" contains no tokens at all and prints itself unchanged. If formatted dates look strange, check that the layout uses exactly the reference values.
Parsing strings into times
time.Parse(layout, value) uses the same layouts, and returns an error you must check:
Without a zone in the input, Parse returns UTC. The same wall-clock string can mean different instants depending on the zone, which is why ParseInLocation exists. The error message names the part that failed to match, which helps when you are debugging a layout.
Time zones
A time.Time is an instant plus a location used for display. Changing the location with In changes how it prints, not which moment it is.
time.UTCis always available. Store and transmit times in UTC (or RFC 3339 with an offset) and convert only for display.time.Localis the machine's zone. On servers and in containers it is often UTC, and on a laptop it is not, so code that depends on it behaves differently in each place.time.LoadLocation("Europe/Berlin")reads the IANA database from the operating system. Minimal container images often lack it, and the call then returns an error. The blank import_ "time/tzdata"embeds the database in your binary (about 450 KB) so it always works. Always handle the error.time.FixedZone(name, offsetSeconds)creates a zone with a constant offset. It has no daylight saving rules, so use it for offsets you received, not for named regions.
Arithmetic and comparison
Things to notice in the output:
AddDate(0, 1, 0)on January 31 gives March 3, not February 28. Go adds one month to get "February 31" and then normalizes the overflow. If you need "same day next month, clamped", write that logic yourself.Subreturns aDuration, which tops out at about 292 years. For differences in calendar days, compare dates at midnight UTC and divide by 24 hours.- To get the start of the day, rebuild the time from
Date().t.Truncate(24 * time.Hour)rounds relative to the zero time in UTC, so it gives the wrong answer for any zone other than UTC.
Compare with Equal, Before, After, never with ==. time.Now() includes a monotonic clock reading, used so that time.Since stays correct if the wall clock is adjusted. == compares that reading and the location too, so two Time values for the same instant can be unequal. For the same reason, do not use time.Time as a map key without normalizing it first (t.UTC().Round(0) strips the monotonic reading).
The zero time.Time is January 1, year 1, 00:00 UTC. Check for it with t.IsZero().
Measuring elapsed time
start := time.Now()
doWork()
log.Printf("doWork took %v", time.Since(start))
time.Since(start) is time.Now().Sub(start), and time.Until(deadline) is deadline.Sub(time.Now()). Both use the monotonic clock when available, so they are safe against system clock changes. For benchmarking code, use Go's benchmark support in the testing package instead of timing by hand.
Timers and tickers
time.After(d) returns a channel that receives once after d. A time.Timer is the same thing with a Stop method. A time.Ticker delivers a value every period until you stop it.
The three ticks at 20, 40 and 60 ms arrive well before the 110 ms timer, so the program stops after the third tick. time.AfterFunc(d, f) runs f in its own goroutine after d, which is handy for one-off delayed work.
Since Go 1.23, timers and tickers that are no longer referenced are garbage collected even if you never call Stop, and a stopped or reset timer's channel no longer delivers a stale value. Calling Stop with defer is still the clear way to say the ticker's life is over. When a time limit applies to a whole operation rather than one wait, a context.WithTimeout usually reads better than a timer.
Time in JSON
time.Time marshals to and from RFC 3339 strings in JSON automatically, with nanosecond precision:
type Event struct {
Name string `json:"name"`
At time.Time `json:"at"`
}
// {"name":"deploy","at":"2026-09-23T14:30:00Z"}
A JSON string in a different format fails to unmarshal. For Unix timestamps or custom formats, store an int64 or a string and convert, or define a type with its own UnmarshalJSON.
Common mistakes
time.Sleep(1)ortime.Sleep(n)with a bare number. That is nanoseconds.- Layouts with the wrong digits.
"2023-01-01"or"YYYY-MM-DD"are not layouts. Use"2006-01-02". - Mixing up minutes and months. Minutes are
04, months are01."15:01"prints the hour and then the month, not the minute. - Comparing with
==. UseEqual. - Relying on
time.Local. It differs between machines. Be explicit withtime.UTCor a loaded location. - Ignoring the error from
LoadLocationorParse. Both fail on real input. A failedParsereturns the zero time, which prints as year 1, and a failedLoadLocationreturns anillocation that makest.In(loc)panic.
Frequently Asked Questions
How do I sleep in Go?
Call time.Sleep with a time.Duration: time.Sleep(2 * time.Second) or time.Sleep(500 * time.Millisecond). A bare number like time.Sleep(2) compiles but sleeps 2 nanoseconds, because a Duration counts nanoseconds. Sleep blocks only the current goroutine.
Why does Go use 2006-01-02 15:04:05 for date formats?
Go formats dates by example. The layout is the reference time Mon Jan 2 15:04:05 MST 2006 written the way you want your output to look. Its parts count up in American order: month 1, day 2, hour 3 (15 on a 24-hour clock), minute 4, second 5, year 6 (2006), zone offset 7 (-0700). So "2006-01-02" means year-month-day and "02/01/2006" means day/month/year.
How do I parse a date string in Go?
Use time.Parse(layout, value) with a layout written in the reference time: t, err := time.Parse("2006-01-02", "2026-09-23"). Always check err. Without zone information in the string the result is in UTC; use time.ParseInLocation to interpret it in another zone.
How do I get a Unix timestamp in Go?
time.Now().Unix() returns seconds since January 1, 1970 UTC as an int64. UnixMilli(), UnixMicro() and UnixNano() give finer units. To go the other way, use time.Unix(sec, 0) or time.UnixMilli(ms).
How do I compare two times in Go?
Use t1.Before(t2), t1.After(t2) and t1.Equal(t2). Do not use ==: it also compares the location and the monotonic clock reading, so two values for the same instant can be unequal. t2.Sub(t1) gives the difference as a Duration.