JSON を経由して、Java8 LocalDateTime
と、Java6 Date
で、シリアライズ・デシリアライズをしようとしたときに表題の件でハマったのでメモ。
イメージとしては、プロジェクトをまたいで、以下の様なことをしようとしました。
Java8 DateTime -> (Serialize) -> JSON -> (Deserialize) -> Java6 Date
すると、 LocalDateTime
と、Date
での値の取りうる範囲の違いがあらわれてきます。具体的に何が起きるかというと、LocalDateTime.MIN
などの Date
で表現できない範囲の値をもったものをデシリアライズしたとき、もとの値が失われてしまいます。
より具体的には、以下の実行結果のようになります。
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.Date
import java.text.SimpleDateFormat
fun main(args: Array<String>) {
val formatter = SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss")
val minLocalDateTime = formatter.format(formatter.parse(
DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.MIN)))
val minDateText = formatter.format(Date(Long.MIN_VALUE))
val zeroDateText = formatter.format(Date(0L))
val zeroEpocLocalDateTime = formatter.format(formatter.parse(
DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(
LocalDateTime.ofEpochSecond(0L, 0, ZoneOffset.UTC))))
println (
"""Java8 DateTime | LocalDateTime.MIN | ${LocalDateTime.MIN}
Java6 Date | LocalDateTime.MIN | $minLocalDateTime
Java6 Date | Date(Long.MIN_VALUE) | $minDateText
Java6 Date | Date(0L) | $zeroDateText
Java8 DateTime | ofEpochSecond(0L, 0,) | $zeroEpocLocalDateTime
""")
}
実行結果。
Java8 DateTime | LocalDateTime.MIN | -999999999-01-01T00:00
Java6 Date | LocalDateTime.MIN | 169087565-03-15T04:51:43
Java6 Date | Date(Long.MIN_VALUE) | 292269055-12-02T04:47:04
Java6 Date | Date(0L) | 1970-01-01T12:00:00
Java8 DateTime | ofEpochSecond(0L, 0,) | 1970-01-01T12:00:00
実行結果でもすでに触れていますが、この差分を吸収するには、エポックタイムを利用します。
LocalDateTime
を使用する側の最小値を、エポックタイムでの最小値に定めてしまうことで、互換性を維持するというわけです。
LocalDateTime.ofEpochSecond(0L, 0, ZoneOffset.UTC)
を使用することで、エポックタイムの最小値を得られるので、LocalDateTime
を使用する側では、これを使用すれば、Java6 を使用しているプロジェクトとの互換を取ることができるようになります。