Jackson / ObjectMapperでOffsetDateTimeをDeserializeしたらUTCになる
はじめに
JacksonでJSONとJavaObjectとの相互変換を行うことは、よくある?ことだと思う。
SpringBootでREST-APIを利用する場合なども含む。
この記事は、
Javaの型として、java.time.LocalDateTime
や java.time.OffsetDateTime
を利用する場合の話。
これらの型を利用する場合は、 com.fasterxml.jackson.datatype:jackson-datatype-jsr310
などを利用する。そんな時に陥った罠があったのでメモを残しておく。
何が起こるのか
JacksonのObjectMapperでJSON文字列をObjectへ変換する際に、Offset情報を付加した値を読み込ませているのにも関わらず、
読み込んだ後のObjectではOffsetが無効になり、 UTC
として扱われる。
以下のサンプルコードで示す。
実行結果はこちら
TimeZone: Asia/Tokyo
{
"localDateTime" : "2023-07-01T16:39:43.4490064",
"offsetDateTime" : "2023-07-01T05:39:43.4490064Z"
}
期待値は、 offsetDateTime
が 2023-07-01T05:39:43.4490064+0900
と出力されること。
これはSpringBootでREST-APIを組んだ場合でも発生する。
なんでそうなるのか
Jacksonは 2.7
以降で、デフォルトのTimeZoneを UTC
で扱うようになっている。
実際のソースコードは、 こちら のリンクから確認が可能。
抜粋したソースが以下。
/**
* We will use a default TimeZone as the baseline.
*/
private static final TimeZone DEFAULT_TIMEZONE =
// TimeZone.getDefault()
/* [databind#915] 05-Nov-2015, tatu: Changed to UTC, from earlier
* baseline of GMT (up to 2.6)
*/
TimeZone.getTimeZone("UTC");
JacksonのObjectMapperを生成する際に、デフォルトのTimeZoneを指定しない場合はこちらが利用される。
なお、コメントにも記載があるように 2.6
まではGMTであった模様...
対策
ObjectMapper
の生成時にデフォルトのTimeZoneを設定することで回避が可能。
以下、デフォルトのTimeZoneを設定した場合と設定しない場合の比較を行うソースコードと結果となる。
実行結果はこちら
TimeZone: Asia/Tokyo
--- Case1 ---
{
"localDateTime" : "2023-07-01T16:39:43.4490064",
"offsetDateTime" : "2023-07-01T05:39:43.4490064Z"
}
--- Case2 ---
{
"localDateTime" : "2023-07-01T16:39:43.4490064",
"offsetDateTime" : "2023-07-01T14:39:43.4490064+09:00"
}
Discussion