LoginSignup
0
0

More than 3 years have passed since last update.

Go言語でTimestamp文字列のパースに手間取った話

Last updated at Posted at 2020-07-25

今までほぼPython一筋でしたが,最近Goを始めてみました。
新しい言語に手を出すと,他の言語ならすぐできるような簡単なことでも非常に引っかかりますね。
ということで,今日起きたそんな一幕をメモがてら記事にしてみました。

やろうとしたこと

Go言語で2020-01-01T09:00:00Zのような文字列から,日本時間における年・月だけ取り出して,つなげて202001のようなint型を返す。

最終的に行き着いたやり方


import (
    "strconv"
    "strings"
    "time"
)

func createYearMonth(stringTimestamp string) int {
    // 1. 文字列を日時オブジェクトへとパース
    // "2020-01-01T09:00:00Z" -> 2020-01-01 09:00:00 +0000 UTC
    t, err := time.Parse(time.RFC3339, stringTimestamp)
    if err != nil {
        fmt.Println("Parse Error: ", err.Error())
    }

    // 2. timezoneの変更
    // 2020-01-01 09:00:00 +0000 UTC -> 2020-01-01 18:00:00 +0900 JST
    loc, _ := time.LoadLocation("Asia/Tokyo")
    t_jst := t.In(loc)

    // 3. Year, Month部分を文字列のまま取得し結合
    // "2020-01-01 18:00:00 +0900 JST" -> ["2020", "01", "01 18:00:00 +0900 JST"]
    splitTimestamp := strings.Split(t_jst.String(), "-")
    // "2020" + "01" -> "202001" -> 202001
    intYearMonth, _ := strconv.Atoi(splitTimestamp[0] + splitTimestamp[1])

    return intYearMonth
}

詰まったところ

1. 文字列を日時オブジェクトへとパース

最初は以下のようにtimestampのフォーマットとなるlayoutを自分で適当に定義してパースしようとしました。

layout := "1970-01-01T00:00:00Z"
t, err := time.Parse(layout, "2020-01-01T09:00:00Z")

ただし,これではうまくパースしてくれず。

timeパッケージの公式ドキュメントを見たところ,パッケージ側でconstantとしてフォーマットがいくつか用意されていることを知り,RFC3339という今回の入力に最も近いもの"2006-01-02T15:04:05Z07:00"をlayoutとして使いました。

2. timezoneの変更

最初は以下のようにパース部分でtimezoneの指定も同時に行おうとしていました。

loc, _ := time.LoadLocation("Asia/Tokyo")
t, err := time.ParseInLocation(time.RFC3339, stringTimestamp, loc)

しかしこれをやっても,得られたtUTCのままです。

どうやら上記のParseInLocationにおけるtimezoneの指定は,入力文字列からはtimezoneが判断できない時のためのもののようです。

今回はそもそも入力が"2020-01-01T09:00:00Z"のようにUTCであることが明記されていたので,意味をなさず。

timezoneそのものを変更するには,

loc, _ := time.LoadLocation("Asia/Tokyo")
t_jst := t.In(loc)

のように後から変換を行うのが正解でした。

3. Year, Month部分を文字列のまま取得し結合

こちらはやや力技です。

.Year().Month()で簡単に年・月を取得できるので,単純に考えれば,

stringYearMonth := fmt.Sprintf("%d%d", t.Year(), t.Month())

で澄むはずの話です。

が,これで得られるのは,"20201"であって"202001"ではないのです。

t.Month()がint型なのが問題でした。

ということで,少し遠回りですが,日時オブジェクトを文字列に再変換した後,ハイフン-で文字列を分けて年・月だけ繋げる,という廻りくどいことを行っています。

ここもう少しスマートにやる方法あるかも,,,

2020.07.26 追記

なんてことはない,%dで文字数を指定すれば簡単にできました。

stringYearMonth := fmt.Sprintf("%04d%02d", t.Year(), t.Month())

まとめ(?)

とりあえず動いた,よかった!

一応無理やりポイントとなる話を拾い出すと,

  • 文字列timestampをパースする際のlayoutは,なるべくtimeパッケージ側で用意されているフォーマットを使う。
  • timezoneの変更は,.In()を使う。

あたりかなと。

もっとこうやると簡単にできるよ,というのがあれば是非お寄せください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0