Menu

Golang Time: Now, Sleep, Format, Parse and Durations

How to work with dates and times in Go: time.Now and time.Sleep, Duration arithmetic, the 2006-01-02 15:04:05 reference layout for Format and Parse, time zones, Unix timestamps, and timers and tickers.

This page includes runnable editors - edit, run, and see output instantly.

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:

ConstantValue
time.Nanosecond1
time.Microsecond1000 ns
time.Millisecond1000 µs
time.Second1000 ms
time.Minute60 s
time.Hour60 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 when n is an int variable. Convert it: time.Duration(n) * time.Second. A constant like 5 works 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:

TokenMeaningExample
2006 / 06year, 4 or 2 digits2026 / 26
01 / 1 / Jan / Januarymonth03 / 3 / Mar / March
02 / 2 / _2day of month (zero-padded, plain, space-padded)05 / 5 / " 5"
Mon / MondayweekdayThu / Thursday
15hour, 24-hour clock09
03 / 3hour, 12-hour clock09 / 9
04 / 4minute07 / 7
05 / 5second03 / 3
PM / pmAM or PM markerAM
.000 / .999fractional seconds (fixed / trailing zeros trimmed).250 / .25
MSTzone abbreviationUTC
-0700 / -07:00numeric zone offset+0000 / +00:00
Z07:00like -07:00, but prints Z for UTCZ

Predefined layouts:

ConstantLayout
time.RFC33392006-01-02T15:04:05Z07:00 (use this for APIs and JSON)
time.RFC3339Nano2006-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.Kitchen3:04PM
time.RFC1123Mon, 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.UTC is always available. Store and transmit times in UTC (or RFC 3339 with an offset) and convert only for display.
  • time.Local is 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.
  • Sub returns a Duration, 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) or time.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 are 01. "15:01" prints the hour and then the month, not the minute.
  • Comparing with ==. Use Equal.
  • Relying on time.Local. It differs between machines. Be explicit with time.UTC or a loaded location.
  • Ignoring the error from LoadLocation or Parse. Both fail on real input. A failed Parse returns the zero time, which prints as year 1, and a failed LoadLocation returns a nil location that makes t.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.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED