The RFC3339 standard is a popular way to format timestamps on the internet. RFC3339 timestamps:
- Are human readable.
- Represent instants in time. No intervals.
- Are in UTC time or include an UTC offset. No local time.
- Allow for different precision levels, from years to fractions of a second.
- Use the
YYYY-MM-DDformat. No confusion betweenMM-DD-YYYYorDD-MM-YYYYformats. - Are string sortable when every timestamps use consistent time zone information and precision.
This makes it a very pragmatic format.
RFC3339 is a tailored version of the broader ISO8601 standard. It’s not entirely a subset, but it’s pretty close.
RFC3339 in Go’s time package
The Go time package includes two constants that that define appropriate reference layouts for RFC3339 timestamps:
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
If we pass these to the time.Parse function we can parse RFC3339 formatted timestamps into time.Time values.
However, not every valid RFC3339 timestamp can be parsed this way:
- Go expects RFC3339 timestamps to always contain the capital
Tcharacter, the standard allows for lower case or a space. - The standard allows for leap seconds, the
timedoes not deal with them.
package main
import (
"fmt"
"log"
"time"
)
func main() {
timestamps := []string{
"2023-12-22T16:25:33Z",
"2023-12-22T16:25:33.8Z",
"2023-12-22T16:25:33.85Z",
"2023-12-22T16:25:33.855Z",
"2023-12-22T16:25:33.855873Z",
"2023-12-22T17:25:33+01:00",
"2023-12-22T17:25:33.855+01:00",
"2023-12-22T17:25:33.855873+01:00",
}
for i, ts := range timestamps {
t, err := time.Parse(time.RFC3339, ts)
if err != nil {
log.Fatalf("%d: %v", i, err)
}
fmt.Println(t)
}
}
Get my free newsletter periodically*
Used by 500+ developers to boost their Go skills.
*working on a big project as of 2025, will get back to posting on the regular schedule once time allows.