この記事は、Java Advent Calendar 2019 22日目の記事になりました。
Java
とか言いつつJavaScript
やKotlin
の話が出てきますが、分量的にはDateTime API
の話が主なので大体Java
です。
解決したい問題
以下の記事の通り、JavaScript
のDate
型には多くの罠があります。
日本人であれば「日時を送信したら1日巻き戻って届いた」的な状況は経験された方も多いのではないでしょうか?
そんな罠だらけのDate
型を安全に送信/解釈します。
解決方法
ものすごくざっくり言うと、規格に則って文字列として送信し、規格に則って文字列から解釈すれば安全に取り扱えます。
今回は例として、ISO 8601
規格に則って処理を行います。
具体的には以下を用います。
-
Date.prototype.toISOString()
メソッド -
DateTime API
のZoned
系クラス
補足
Date.prototype.toISOString()
メソッドは大体の環境で動きますが、DateTime API
はJava8
以降のサポートです。
やり方
送信側
以下で生成したdateStr
を送信すればOKです
const date = Date.now();
// ISO 8601で、タイムゾーンがUTCの文字列
const dateStr = date.toISOString();
受信側
まずZonedDateTime
として解釈して、そこからLocal
系の内容に変換するのがオススメです。
ZonedDateTime.parse
メソッドはDate.prototype.toISOString()
で生成した文字列を直に解釈できます。
val dateStr = /* toISOString()した文字列 */
// タイムゾーン付き日時型
val zonedDateTime = ZonedDateTime.parse(dateStr)
タイムゾーンを設定する
型としてはZonedDateTime
に解釈した時点で安全に取り扱えますが、この方法では受け取った時点でのタイムゾーンがUTC扱いになるため、そこから更に何か扱う前にローカルのタイムゾーンに変換した方が良いでしょう。
タイムゾーンのセットは、ZonedDateTime::withZoneSameInstant
メソッドで行えます。
このメソッドの引数はZoneId
型で、ZoneId.systemDefault()
を呼び出すことでシステムのタイムゾーンのZoneId
を得ることができます。
val original: ZonedDateTime
val zoned = original.withZoneSameInstant(ZoneId.systemDefault())
この処理を終えれば、後は自由に扱うことができます。
補足として、日時を厳密に扱いたいのであれば、JVM
内では日時全てをZonedDateTime
として扱い、ゲッター等で必要に応じて変換するというのがオススメです。
終わりに
今回は日時(Date)を安全に送信する/解釈する方法についてまとめました。
日時に限らず相対的な値は扱いがとても難しいですが、規格に則ることで安全に扱えるようになります。
この記事が相対的な値の扱いのお役に立てば幸いです。