ZetCode

Go datetime format

last modified October 30, 2026

In this article we show how to format date and time values in Go. Formatting turns a time.Time value into text with the Format method, which takes a layout string.

Go does not use format specifiers such as yyyy-MM-dd. Instead, a layout is a sample date, and the reference instant is

Mon Jan 2 15:04:05 MST 2006

Every component of that sample has a fixed value, and the same component in a layout is replaced by the corresponding part of the formatted value. The year is 2006, the month is 01 or January, the day is 02 or 2, and so on.

The signature of the method is:

func (t Time) Format(layout string) string

All examples in this article were compiled and run with Go 1.27.0. The examples that use time.Now print different values on every run.

The reference layout

The reference instant is Monday, January 2, 15:04:05 (that is, 3:04:05 PM) in the year 2006, in the MST time zone. The components of this value are the only ones the formatter recognizes.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 30, 9, 5, 7, 0, time.UTC)

    fmt.Println(t.Format("2006-01-02"))
    fmt.Println(t.Format("02 Jan 2006"))
    fmt.Println(t.Format("15:04:05"))
    fmt.Println(t.Format("3:04 PM"))
}

The first layout produces an ISO-like date, the second a human readable date, and the last two a 24-hour and a 12-hour clock. The value t is built with time.Date so that the output does not depend on the current clock.

$ go run main.go
2026-10-30
30 Oct 2026
09:05:07
9:05 AM

Formatting the current time

A typical program formats time.Now instead of a fixed value.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    now := time.Now()

    fmt.Println("Time:      ", now.Format("15:04:05"))
    fmt.Println("Short date:", now.Format("Jan 2, 2006"))
    fmt.Println("Long date: ", now.Format("Mon Jan 2 15:04:05 2006"))
    fmt.Println("ISO 8601:  ", now.Format(time.RFC3339))
}

The output depends on the moment of execution. The layout Jan 2, 2006 is not padded, so a single-digit day appears without a leading zero.

$ go run main.go
Time:       19:57:59
Short date: Oct 30, 2026
Long date:  Fri Oct 30 19:57:59 2026
ISO 8601:   2026-10-30T19:57:59+01:00

Predefined layouts

The time package defines a set of layout constants for the common internet date formats and for its own timestamp formats.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 30, 9, 5, 7, 123456789,
        time.FixedZone("CET", 3600))

    fmt.Println(t.Format(time.Layout))
    fmt.Println(t.Format(time.ANSIC))
    fmt.Println(t.Format(time.UnixDate))
    fmt.Println(t.Format(time.RubyDate))
    fmt.Println(t.Format(time.RFC822))
    fmt.Println(t.Format(time.RFC822Z))
    fmt.Println(t.Format(time.RFC850))
    fmt.Println(t.Format(time.RFC1123))
    fmt.Println(t.Format(time.RFC1123Z))
    fmt.Println(t.Format(time.RFC3339))
    fmt.Println(t.Format(time.RFC3339Nano))
    fmt.Println(t.Format(time.Kitchen))
    fmt.Println(t.Format(time.Stamp))
    fmt.Println(t.Format(time.StampMilli))
    fmt.Println(t.Format(time.StampMicro))
    fmt.Println(t.Format(time.StampNano))
    fmt.Println(t.Format(time.DateTime))
    fmt.Println(t.Format(time.DateOnly))
    fmt.Println(t.Format(time.TimeOnly))
}

The value uses a fixed zone named CET with an offset of one hour, so the zone abbreviation, the numeric offset and the milliseconds are visible in the output. The Z variants of the RFC layouts print +0100 instead of the zone name, while RFC 3339 uses a colon in the offset.

$ go run main.go
10/30 09:05:07AM '26 +0100
Fri Oct 30 09:05:07 2026
Fri Oct 30 09:05:07 CET 2026
Fri Oct 30 09:05:07 +0100 2026
30 Oct 26 09:05 CET
30 Oct 26 09:05 +0100
Friday, 30-Oct-26 09:05:07 CET
Fri, 30 Oct 2026 09:05:07 CET
Fri, 30 Oct 2026 09:05:07 +0100
2026-10-30T09:05:07+01:00
2026-10-30T09:05:07.123456789+01:00
9:05AM
Oct 30 09:05:07
Oct 30 09:05:07.123
Oct 30 09:05:07.123456
Oct 30 09:05:07.123456789
2026-10-30 09:05:07
2026-10-30
09:05:07

Date components

The year has two forms: 2006 prints all four digits and 06 the last two. The month can be written as a number (01, 1) or as a name (January, Jan).

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 5, 9, 5, 7, 0, time.UTC)

    fmt.Println(t.Format("2006"))
    fmt.Println(t.Format("06"))
    fmt.Println(t.Format("January"))
    fmt.Println(t.Format("Jan"))
    fmt.Println(t.Format("01"))
    fmt.Println(t.Format("1"))
}

October is the tenth month, so 01 and 1 both print 10 here. The difference appears only for months with a single digit.

$ go run main.go
2026
26
October
Oct
10
10

Day components

The day of the month is 02 for the zero-padded form, 2 for the plain form and _2 for the space-padded form. The weekday is Monday or Mon, and the day of the year is 002.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 5, 9, 5, 7, 0, time.UTC)

    fmt.Println(t.Format("02"))
    fmt.Println(t.Format("2"))
    fmt.Println(t.Format("_2"))
    fmt.Println(t.Format("Monday"))
    fmt.Println(t.Format("Mon"))
    fmt.Println(t.Format("002"))
}

October 5, 2026 is a Monday and the 278th day of the year. In a non-leap year the last three months start at days 274, 305 and 335, so the count is easy to check.

$ go run main.go
05
5
 5
Monday
Mon
278

Padding

The difference between the padded and the plain forms is visible only when the value is smaller than ten. The _2 form pads with a space, which keeps the width of a column constant.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    june := time.Date(2026, time.June, 5, 9, 5, 7, 0, time.UTC)
    jan := time.Date(2026, time.January, 15, 9, 5, 7, 0, time.UTC)

    fmt.Printf("%q %q %q\n", june.Format("1"), june.Format("01"), june.Format("Jan"))
    fmt.Printf("%q %q %q\n", june.Format("2"), june.Format("02"), june.Format("_2"))
    fmt.Printf("%q %q %q\n", jan.Format("2"), jan.Format("02"), jan.Format("_2"))
}

The %q verb quotes the strings, so the leading space of _2 is visible. The first line is the month, the next two are the day: June 5 gives " 5", while January 15 already has two digits and gives "15".

$ go run main.go
"6" "06" "Jun"
"5" "05" " 5"
"15" "15" "15"

Time components

The hour is 15 in the 24-hour clock and 3 or 03 in the 12-hour clock. The minute is 04 or 4, the second is 05 or 5, and the meridiem is PM or pm.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 5, 9, 5, 7, 0, time.UTC)

    fmt.Println(t.Format("15:04:05"))
    fmt.Println(t.Format("3:04:05 PM"))
    fmt.Println(t.Format("03:04:05 pm"))
    fmt.Println(t.Format("15"), t.Format("3"))
    fmt.Println(t.Format("04"), t.Format("4"))
    fmt.Println(t.Format("05"), t.Format("5"))
}

Note that 3 is an hour, not a minute; minutes and seconds are 4 and 5. The same value appears as 09:05:07 in the 24-hour clock and 9:05:07 AM in the 12-hour clock: 03 adds a leading zero, and the meridiem token adds AM or PM.

$ go run main.go
09:05:07
9:05:07 AM
09:05:07 am
09 9
05 5
07 7

Fractional seconds

A trailing .000 prints exactly the given number of digits and truncates the rest. A trailing .999 removes trailing zeros, so the number of printed digits varies. Both forms follow the decimal point and can be used after the seconds.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    a := time.Date(2026, time.January, 2, 3, 4, 5, 120000000, time.UTC)
    b := time.Date(2026, time.January, 2, 3, 4, 5, 0, time.UTC)

    fmt.Println(a.Format("15:04:05.000"))
    fmt.Println(a.Format("15:04:05.999"))
    fmt.Println(b.Format("15:04:05.000"))
    fmt.Println(b.Format("15:04:05.999"))
    fmt.Println(a.Format(time.RFC3339Nano))
    fmt.Println(b.Format(time.RFC3339Nano))
}

The value a has 120 milliseconds, so .000 prints .120 and .999 prints .12. The value b has no fraction: .000 still prints .000, while .999 prints nothing, and RFC3339Nano ends directly with Z.

$ go run main.go
03:04:05.120
03:04:05.12
03:04:05.000
03:04:05
2026-01-02T03:04:05.12Z
2026-01-02T03:04:05Z

Time zones

MST prints the zone abbreviation, -0700 and -07:00 the numeric offset, and the Z forms print Z for the UTC zone instead of +0000.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    utc := time.Date(2026, time.October, 30, 9, 5, 7, 0, time.UTC)
    cet := utc.In(time.FixedZone("CET", 3600))

    fmt.Println(utc.Format("MST -0700 -07:00 Z07:00 Z0700"))
    fmt.Println(cet.Format("MST -0700 -07:00 Z07:00 Z0700"))
}

Both values describe the same instant. The UTC value prints UTC and Z; the CET value prints the name and +0100.

$ go run main.go
UTC +0000 +00:00 Z Z
CET +0100 +01:00 +01:00 +0100

Week numbers and day of year

The layout token 002 is the zero-padded day of the year, while ISOWeek returns the ISO 8601 year and week numbers.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.June, 5, 9, 5, 7, 0, time.UTC)

    fmt.Println(t.Format("2006-002"))

    year, week := t.ISOWeek()
    fmt.Println(year, week)
}

June 5, 2026 is day 156 of the year and falls in ISO week 23. The ISO year can differ from the calendar year near New Year, which is why ISOWeek returns both values.

$ go run main.go
2026-156
2026 23

Combining layouts

Layouts are ordinary strings, so they can contain any text, including words and punctuation. Only the recognized components are replaced.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 5, 9, 5, 7, 0, time.UTC)

    fmt.Println(t.Format("Monday, 2 January 2006"))
    fmt.Println(t.Format("02 Jan 2006 15:04 MST"))
    fmt.Println(t.Format("2006/01/02"))
    fmt.Println(t.Format("01/02/06"))
    fmt.Println(t.Format(time.DateOnly + " " + time.TimeOnly))
    fmt.Println(t.Format("15:04"))
    fmt.Println(t.Format("3:04 PM"))
    fmt.Println(t.Format("It is 15:04 on Monday"))
}

The layout constants can be concatenated because they are strings. The last line shows that literal words are copied unchanged.

$ go run main.go
Monday, 5 October 2026
05 Oct 2026 09:05 UTC
2026/10/05
10/05/26
2026-10-05 09:05:07
09:05
9:05 AM
It is 09:05 on Monday

Wrong layouts

Only the components of the reference time are replaced. Letters that do not form a component are copied literally, and stray digits can be read as a different component.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 30, 9, 5, 7, 0, time.UTC)

    fmt.Println(t.Format("YYYY-MM-DD"))
    fmt.Println(t.Format("2026-10-30"))
    fmt.Println(t.Format("2026-13-02"))
}

The first layout prints its own letters, because YYYY, MM and DD are not Go components. The second line looks like a date but is a literal, so it always prints the same text. In the third line 13 is read as the month token 1 followed by the hour token 3, which produces the meaningless 2026-109-30.

$ go run main.go
YYYY-MM-DD
2026-10-30
2026-109-30

Building strings with AppendFormat

AppendFormat writes the formatted value to an existing byte slice and returns the extended slice. It avoids an intermediate string allocation and is convenient when several values are joined.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 30, 9, 5, 7, 0, time.UTC)

    b := []byte("date: ")
    b = t.AppendFormat(b, time.DateOnly)
    b = append(b, ", time: "...)
    b = t.AppendFormat(b, time.TimeOnly)

    fmt.Println(string(b))
}

The buffer starts with the prefix date: , receives the date, the literal separator and the time. The result is the same as concatenating three Format calls.

$ go run main.go
date: 2026-10-30, time: 09:05:07

Default string forms

Printing a time.Time without a layout uses String, which combines the date, the time, the offset and the zone name. GoString produces the source expression that would recreate the value.

main.go
package main

import (
    "fmt"
    "time"
)

func main() {

    t := time.Date(2026, time.October, 30, 9, 5, 7, 0, time.UTC)

    fmt.Println(t.String())
    fmt.Println(t.GoString())
}

String is the form used by fmt.Println. GoString is what the %#v verb prints, and it is handy when a time has to be pasted back into source code.

$ go run main.go
2026-10-30 09:05:07 +0000 UTC
time.Date(2026, time.October, 30, 9, 5, 7, 0, time.UTC)

Source

Go time package - reference

In this article we have formatted date and time values in Go. We covered the reference layout, the predefined layout constants, the individual date, day and time components, padding, fractional seconds, time zones, week numbers, combined and incorrect layouts, and the AppendFormat, String and GoString methods.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Go tutorials.