今までほぼ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)
しかしこれをやっても,得られたt
はUTCのままです。
どうやら上記の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()
を使う。
あたりかなと。
もっとこうやると簡単にできるよ,というのがあれば是非お寄せください!