[date] Parsing date string in Go

I tried parsing the date string "2014-09-12T11:45:26.371Z" in Go.

Code

layout := "2014-09-12T11:45:26.371Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout , str)

I got this error:

parsing time "2014-11-12T11:47:39.489Z": month out of range

How can I parse this date string?

This question is related to date go

The answer is


I will suggest using time.RFC3339 constant from time package. You can check other constants from time package. https://golang.org/pkg/time/#pkg-constants

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("Time parsing");
    dateString := "2014-11-12T11:45:26.371Z"
    time1, err := time.Parse(time.RFC3339,dateString);
    if err!=nil {
    fmt.Println("Error while parsing date :", err);
    }
    fmt.Println(time1); 
}

The layout to use is indeed "2006-01-02T15:04:05.000Z" described in RickyA's answer.
It isn't "the time of the first commit of go", but rather a mnemonic way to remember said layout.
See pkg/time:

The reference time used in the layouts is:

Mon Jan 2 15:04:05 MST 2006

which is Unix time 1136239445.
Since MST is GMT-0700, the reference time can be thought of as

 01/02 03:04:05PM '06 -0700

(1,2,3,4,5,6,7, provided you remember that 1 is for the month, and 2 for the day, which is not easy for an European like myself, used to the day-month date format)

As illustrated in "time.parse : why does golang parses the time incorrectly?", that layout (using 1,2,3,4,5,6,7) must be respected exactly.


As answered but to save typing out "2006-01-02T15:04:05.000Z" for the layout, you could use the package's constant RFC3339.

str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(time.RFC3339, str)

if err != nil {
    fmt.Println(err)
}
fmt.Println(t)

https://play.golang.org/p/Dgu2ZvHwTh


If you have worked with time/date formatting/parsing in other languages you might have noticed that the other languages use special placeholders for time/date formatting. For eg ruby language uses

%d for day
%Y for year

etc. Golang, instead of using codes such as above, uses date and time format placeholders that look like date and time only. Go uses standard time, which is:

Mon Jan 2 15:04:05 MST 2006  (MST is GMT-0700)
or 
01/02 03:04:05PM '06 -0700

So if you notice Go uses

01 for the day of the month,
02 for the month
03 for hours,
04 for minutes
05 for second
and so on

Therefore for example for parsing 2020-01-29, layout string should be 06-01-02 or 2006-01-02.

You can refer to the full placeholder layout table at this link - https://golangbyexample.com/parse-time-in-golang/


For those of you out there that are encountering this, use the time.RFC3339 versus the string constant of "2006-01-02T15:04:05.000Z". And here is the reason why:

regDate := "2007-10-09T22:50:01.23Z"

layout1 := "2006-01-02T15:04:05.000Z"
t1, err := time.Parse(layout1, regDate)

if err != nil {
    fmt.Println("Static format doesn't work")
} else {
    fmt.Println(t1)
}

layout2 := time.RFC3339
t2, err := time.Parse(layout2, regDate)

if err != nil {
    fmt.Println("RFC format doesn't work") // You shouldn't see this at all
} else {
    fmt.Println(t2)
}

This will produce the following result:

Static format doesn't work
2007-10-09 22:50:01.23 +0000 UTC

Here is the Playground Link


This is rather late to the party, and not really saying anything that hasn't been already said in one form or another, mostly through links above, but I wanted to give a TL;DR recap to those with less attention span:

The date and time of the go format string is very important. It's how Go knows which field is which. They are generally 1-9 left to right as follows:

  • January / Jan / january / jan / 01 / _1 (etc) are for month
  • 02 / _2 are for day of month
  • 15 / 03 / _3 / PM / P / pm /p are for hour & meridian (3pm)
  • 04 / _4 are for minutes
  • 05 / _5 are for seconds
  • 2006 / 06 are for year
  • -0700 / 07:00 / MST are for timezone
  • .999999999 / .000000000 etc are for partial seconds (I think the distinction is if trailing zeros are removed)
  • Mon / Monday are day of the week (which 01-02-2006 actually was),

So, Don't write "01-05-15" as your date format, unless you want "Month-Second-Hour"

(... again, this was basically a summary of above.)


This might be super late, but this is for people that might stumble on this problem and might want to use external package for parsing date string.

I've tried looking for a libraries and I found this one:

https://github.com/araddon/dateparse

Example from the README:

package main

import (
    "flag"
    "fmt"
    "time"

    "github.com/apcera/termtables"
    "github.com/araddon/dateparse"
)

var examples = []string{
    "May 8, 2009 5:57:51 PM",
    "Mon Jan  2 15:04:05 2006",
    "Mon Jan  2 15:04:05 MST 2006",
    "Mon Jan 02 15:04:05 -0700 2006",
    "Monday, 02-Jan-06 15:04:05 MST",
    "Mon, 02 Jan 2006 15:04:05 MST",
    "Tue, 11 Jul 2017 16:28:13 +0200 (CEST)",
    "Mon, 02 Jan 2006 15:04:05 -0700",
    "Thu, 4 Jan 2018 17:53:36 +0000",
    "Mon Aug 10 15:44:11 UTC+0100 2015",
    "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
    "12 Feb 2006, 19:17",
    "12 Feb 2006 19:17",
    "03 February 2013",
    "2013-Feb-03",
    //   mm/dd/yy
    "3/31/2014",
    "03/31/2014",
    "08/21/71",
    "8/1/71",
    "4/8/2014 22:05",
    "04/08/2014 22:05",
    "4/8/14 22:05",
    "04/2/2014 03:00:51",
    "8/8/1965 12:00:00 AM",
    "8/8/1965 01:00:01 PM",
    "8/8/1965 01:00 PM",
    "8/8/1965 1:00 PM",
    "8/8/1965 12:00 AM",
    "4/02/2014 03:00:51",
    "03/19/2012 10:11:59",
    "03/19/2012 10:11:59.3186369",
    // yyyy/mm/dd
    "2014/3/31",
    "2014/03/31",
    "2014/4/8 22:05",
    "2014/04/08 22:05",
    "2014/04/2 03:00:51",
    "2014/4/02 03:00:51",
    "2012/03/19 10:11:59",
    "2012/03/19 10:11:59.3186369",
    // Chinese
    "2014?04?08?",
    //   yyyy-mm-ddThh
    "2006-01-02T15:04:05+0000",
    "2009-08-12T22:15:09-07:00",
    "2009-08-12T22:15:09",
    "2009-08-12T22:15:09Z",
    //   yyyy-mm-dd hh:mm:ss
    "2014-04-26 17:24:37.3186369",
    "2012-08-03 18:31:59.257000000",
    "2014-04-26 17:24:37.123",
    "2013-04-01 22:43",
    "2013-04-01 22:43:22",
    "2014-12-16 06:20:00 UTC",
    "2014-12-16 06:20:00 GMT",
    "2014-04-26 05:24:37 PM",
    "2014-04-26 13:13:43 +0800",
    "2014-04-26 13:13:44 +09:00",
    "2012-08-03 18:31:59.257000000 +0000 UTC",
    "2015-09-30 18:48:56.35272715 +0000 UTC",
    "2015-02-18 00:12:00 +0000 GMT",
    "2015-02-18 00:12:00 +0000 UTC",
    "2017-07-19 03:21:51+00:00",
    "2014-04-26",
    "2014-04",
    "2014",
    "2014-05-11 08:20:13,787",
    // mm.dd.yy
    "3.31.2014",
    "03.31.2014",
    "08.21.71",
    //  yyyymmdd and similar
    "20140601",
    // unix seconds, ms
    "1332151919",
    "1384216367189",
}

var (
    timezone = ""
)

func main() {
    flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone")
    flag.Parse()

    if timezone != "" {
        // NOTE:  This is very, very important to understand
        // time-parsing in go
        loc, err := time.LoadLocation(timezone)
        if err != nil {
            panic(err.Error())
        }
        time.Local = loc
    }

    table := termtables.CreateTable()

    table.AddHeaders("Input", "Parsed, and Output as %v")
    for _, dateExample := range examples {
        t, err := dateparse.ParseLocal(dateExample)
        if err != nil {
            panic(err.Error())
        }
        table.AddRow(dateExample, fmt.Sprintf("%v", t))
    }
    fmt.Println(table.Render())
}

Use the exact layout numbers described here and a nice blogpost here.

so:

layout := "2006-01-02T15:04:05.000Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout, str)

if err != nil {
    fmt.Println(err)
}
fmt.Println(t)

gives:

>> 2014-11-12 11:45:26.371 +0000 UTC

I know. Mind boggling. Also caught me first time. Go just doesn't use an abstract syntax for datetime components (YYYY-MM-DD), but these exact numbers (I think the time of the first commit of go Nope, according to this. Does anyone know?).