Go言語(Golang)の日付フォーマットとゼロパディングについて

開発Divの藤原です。

サーバー側のアプリを書くときは主に(というか私の場合はほぼ)Go言語を使っています。
元々C言語をやっていた私にとってGo言語は非常にとっつきやすく、今ではかなりのお気に入りです。
今回はそんなGo言語の日付フォーマットについて書いていこうと思います。

Go言語の日付フォーマット

Go言語の日付フォーマットは少し特殊で特定の数字自体が年や月や日などを表します。
例えば、Java の java.text.SimpleDateFormat では「年/月/日」を以下のような形で表現できます。

yyyy/MM/dd

分かり易いですね。

しかし、Go言語の場合に同じ「年/月/日」を以下のような形で表現する必要があります。

2006/01/02

ぱっと見、フォーマットだとは思えませんが、これがGo言語の日付フォーマットです。

フォーマットをまとめると以下のとおりです。

年(4桁)                    = 2006
年(下2桁、0埋め)           = 06

月(0埋め)                  = 01
月(0埋め不要)              = 1
月(英語表記)               = January
月(英語省略表記)           = Jan

日(0埋め)                  = 02
日(0埋め不要)              = 2

曜日                       = Monday
曜日(省略表記)             = Mon

時(24時間 0埋め不要)       = 15
時(12時間 0埋め)           = 03
時(12時間 0埋め不要)       = 3
午前/午後(大文字)          = PM
午前/午後(小文字)          = pm

分(0埋め)                  = 04
分(0埋め不要)              = 4

秒(0埋め)                  = 05
秒(0埋め不要)              = 5

ミリ秒(桁数固定)           = .000 (例 .100)
ミリ秒(桁数可変)           = .999 (例 .1)

マイクロ秒(桁数固定)       = .000000 (例 .100000)
マイクロ秒(桁数可変)       = .999999 (例 .1)

ナノ秒(桁数固定)           = .000000000 (例 .100000000)
ナノ秒(桁数可変)           = .999999999 (例 .1)

タイムゾーン               = Z0700
タイムゾーン               = Z07:00
タイムゾーン               = Z07
タイムゾーン               = -0700
タイムゾーン               = -07:00
タイムゾーン               = -07
タイムゾーン(英語表記)     = MST

日付フォーマットのゼロパディング(ゼロ埋め)

そして、この日付フォーマットを使うときに気を付けないといけないのがゼロパディングです。
特に文字列をパースして日付型(time.Time)に変換するときに注意が必要です。

例えば、以下のような2つの日付の文字列があります。

2018-06-01
2018-6-1

この文字列を以下の日付フォーマットでパースするとどうなるでしょうか?

2006-01-02

パースのソースコードはこんな感じです。

t, err := time.Parse("2006-01-02", "2018-06-01")
t, err = time.Parse("2006-01-02", "2018-6-1")

これを実行すると、上は成功しますが、下は失敗します。
なぜなら、月と日のフォーマットが0パディング指定になっているからです。

実際にソースコード書いて実行してみました。
https://play.golang.org/p/5MdH96ybRYm
このような結果になります。

Sccess!! 2018-06-01 00:00:00 +0000 UTC
Failed!! parsing time "2018-6-1": month out of range

 

では、上記を踏まえ、修正してみましょう。

t, err := time.Parse("2006-1-2", "2018-06-01")
t, err = time.Parse("2006-1-2", "2018-6-1")

こんな感じにして、月と日のフォーマットからゼロパディングを取り除きました。
しかし、これだと今度は下は成功するけど上は失敗するような気もします。

実際にやってみましょう。
https://play.golang.org/p/LWoHB5RCGg3

Sccess!! 2018-06-01 00:00:00 +0000 UTC
Sccess!! 2018-06-01 00:00:00 +0000 UTC

なんと、両方とも成功です。
ゼロパディングを付けないフォーマットの場合は、ゼロパディングがあってもなくてもパースしてくれるようです。

さらに色んなパターンを試してみました。
https://play.golang.org/p/14vMPEwZX7W

すべて期待通りの結果になりました。

*** Layout = A (2006-01-02 15:04:05.000000000) ***
  values[0]: Success : (2018-06-01 01:04:05.100000000) : time = 2018-06-01 01:04:05.1 +0000 UTC
  values[1]: Failed  : (2018-06-01 01:04:05.1        ) : error = parsing time "2018-06-01 01:04:05.1" as "2006-01-02 15:04:05.000000000": cannot parse ".1" as ".000000000"
  values[2]: Failed  : (2018-06-01 01:04:5.1         ) : error = parsing time "2018-06-01 01:04:5.1" as "2006-01-02 15:04:05.000000000": cannot parse "5.1" as "05"
  values[3]: Failed  : (2018-06-01 01:4:5.1          ) : error = parsing time "2018-06-01 01:4:5.1" as "2006-01-02 15:04:05.000000000": cannot parse "4:5.1" as "04"
  values[4]: Failed  : (2018-06-01 1:4:5.1           ) : error = parsing time "2018-06-01 1:4:5.1" as "2006-01-02 15:04:05.000000000": cannot parse "4:5.1" as "04"
  values[5]: Failed  : (2018-06-1 1:4:5.1            ) : error = parsing time "2018-06-1 1:4:5.1" as "2006-01-02 15:04:05.000000000": cannot parse "1 1:4:5.1" as "02"
  values[6]: Failed  : (2018-6-1 1:4:5.1             ) : error = parsing time "2018-6-1 1:4:5.1": month out of range

*** Layout = B (2006-1-2 15:4:5.999999999) ***
  values[0]: Success : (2018-06-01 01:04:05.100000000) : time = 2018-06-01 01:04:05.1 +0000 UTC
  values[1]: Success : (2018-06-01 01:04:05.1        ) : time = 2018-06-01 01:04:05.1 +0000 UTC
  values[2]: Success : (2018-06-01 01:04:5.1         ) : time = 2018-06-01 01:04:05.1 +0000 UTC
  values[3]: Success : (2018-06-01 01:4:5.1          ) : time = 2018-06-01 01:04:05.1 +0000 UTC
  values[4]: Success : (2018-06-01 1:4:5.1           ) : time = 2018-06-01 01:04:05.1 +0000 UTC
  values[5]: Success : (2018-06-1 1:4:5.1            ) : time = 2018-06-01 01:04:05.1 +0000 UTC
  values[6]: Success : (2018-6-1 1:4:5.1             ) : time = 2018-06-01 01:04:05.1 +0000 UTC

   

ソースコードの中の layouts と values の配列をいじれば、他にも色々試せますので良かったらお試し下さい。