🕰️

楽に Go で ISO 8601 の日付と時刻を扱ってみる

2023/09/11に公開

この記事では github.com/Code-Hex/synchro/iso8601 というライブラリを使って ISO 8601 形式の文字列を Go で扱ってみます。

ISO 8601 の形式を採用した日付(2023-09-11)や時間(12:00:00)をよくコードの中で扱うのではないのでしょうか。

ISO 8601 について:

ISO 8601 は "Data elements and interchange formats--Information interchange--Representation of dates and times" (和訳: 情報交換のためのデータ要素及び交換方式ー日付及び時刻の表記) というタイトルが表すように、コンピュータ間で日付と時刻の送受信を行うためのフォーマットを規定する規格です。ISO 8601 以前にも日付と時刻の表現を定めた規格は複数存在していましたが、ISO 8601 はそれらを統合した規格となっています。現行版は第 3 版にあたる ISO 8601:2004 です。

https://zenn.dev/khasunuma/articles/introduction-to-iso8601

日付

ISO 8601 で扱える日付は次のようなものがあります:

  • 暦日付 (calendar date)
    • 標準形式: YYYYMMDD (拡張形式: YYYY-MM-DD)
    • 例: 20140321, 2014-03-21
  • 年間通算日 (ordinal date)
    • 標準形式: YYYYDDD (拡張形式: YYYY-DDD)
    • 例: 2014080, 2014-080
  • 暦週日付 (week date)
    • 標準形式: YYYYWwwD (拡張形式: YYYY-Www-D)
    • 例: 2014W125, 2014-W12-5

また各言語のライブラリによって、クオーター形式の日付にも対応されているケースがあります。

  • クオーター日付 (quarter date)
    • 標準形式: YYYYQqDD (拡張形式: YYYY-Qq-DD)
    • 例:2014Q180, 2014-Q4-80

iso8601.ParseDateTime 関数もしくは iso8601.ParseDate 関数を利用します。 iso8601.ParseDateTimetime.Time を返しますが、iso8601.ParseDateDateLike interface を返します。

func main() {
	// calendar date
	t1, _ := iso8601.ParseDateTime("20140321")

	// ordinal date
	t2, _ := iso8601.ParseDateTime("2014080")

	// week date
	t3, _ := iso8601.ParseDateTime("2014W125")

	// quarter date
	t4, _ := iso8601.ParseDateTime("2014Q180")
}

Playground

https://go.dev/play/p/2EVQwS_nD0r

時刻

  • 時刻
    • 基本形式: hhmmss (拡張形式: hh:mm:ss)
  • 小数を含む時刻
    • .(ピリオド)もしくは ,(カンマ)で区切ります。
    • 基本形式: hhmmss.sssssssss (拡張形式: hh:mm:ss.sssssssss)
    • 基本形式: hhmm.mmmmmmmmm (拡張形式: hh:mm.mmmmmmmmm)
    • 基本形式: hh.hhhhhhhhh (拡張形式: hh.hhhhhhhhh)

時刻のみをパースする場合は iso8601.ParseTime を利用します。この関数はタイムゾーンを含めない、ISO 8601 の時刻を表現する構造体 Time を返します。

func main() {
	t1, _ := iso8601.ParseTime("24:00:00")

	t2, _ := iso8601.ParseTime("23:59:59.999999999")

	t3, _ := iso8601.ParseTime("2359.5") // 23:59:30
	
	t4, _ := iso8601.ParseTime("23.5") // 23:30:00
}

Playground

https://go.dev/play/p/OXBZlfuTOzb

日付と時刻の組み合わせ

前述した、日付と時刻の形式の組み合わせの文字列を iso8601.ParseDateTime を利用することで time.Time へ変換できます。またタイムゾーンを含めることもできます。

標準ライブラリのようにレイアウトを指定しなくてもいいので、とても便利ですね!

func main() {
	s := "2017-04-24T09:41:34.89312523"
	t1, _ := iso8601.ParseDateTime(s) // local time
	fmt.Println(t1)

	t2, _ := iso8601.ParseDateTime(s + "Z") // UTC
	fmt.Println(t2)

	t3, _ := iso8601.ParseDateTime(s + "+09:00") // +09:00
	fmt.Println(t3)

	s2 := "2017-04-24 09:41:34.89312523" // 'T' ではなく ' ' を区切りにする
	t4, _ := iso8601.ParseDateTime(s2, iso8601.WithTimeDesignators(' '))
	fmt.Println(t4)
}

Playground

https://go.dev/play/p/wN4fxDfYAyA

タイムゾーンファーストで ISO 8601 を使う

github.com/Code-Hex/synchro/iso8601synchro のサブパッケージです。

synchrotime.Time をタイムゾーンを含めた型として扱うことができるライブラリです。

https://zenn.dev/codehex/articles/f527d66b949b85

synchro.ParseISO 関数を利用することで、型で指定したタイムゾーンの出力を保証しながら、ISO 8601 の日付、時刻の文字列を扱うことが可能です。

func main() {
	s := "2017-04-24T09:41:34.89312523"
	t1, _ := synchro.ParseISO[tz.AsiaTokyo](s) // local time
	fmt.Println(t1)

	t2, _ := synchro.ParseISO[tz.AsiaTokyo](s + "Z") // UTC
	fmt.Println(t2)

	t3, _ := synchro.ParseISO[tz.AsiaTokyo](s + "+09:00") // +09:00
	fmt.Println(t3)
}

// 2017-04-24 18:41:34.89312523 +0900 JST
// 2017-04-24 18:41:34.89312523 +0900 JST
// 2017-04-24 09:41:34.89312523 +0900 JST

Playground

https://go.dev/play/p/Alt7OSxRJBX

最後に

今回は、synchro とそのサブパッケージである github.com/Code-Hex/synchro/iso8601 を紹介しました。このライブラリを使用することで、ISO 8601 の日付と時刻の扱いが Go で非常にシンプルになり、さらにタイムゾーンを型として保証することが可能になります。

また、このライブラリはまだ開発途中の段階にあり、新しい機能の追加や改善を続けています。

使用してみて感じたこと、改善したい点などがあれば、ぜひ GitHub のリポジトリにフィードバックとして共有してください!

NOT A HOTEL

Discussion