今週に入ってからSpringMVCでWebAPIをごりごり書いているのですが、いざ日付型を扱うというところになってちょっとはまったのでメモ書き。
謀ったなJackson
SpringMVCのJSON処理は標準でJacksonを使うようになっています。
Jacksonで日付型をフォーマットするためには
@JsonFormat(pattern = "yyyy/MM/dd") private Date date;
とかやるわけなんですが、これだとタイムゾーンがGMTで計算されて変になったりします。
@JsonFormat(pattern = "yyyy/MM/dd", timezone = "Asia/Tokyo") private Date date;
とか書くこともできるんですが、さすがにこれを一々指定するのは気が重い。
@JsonFormat
のJavaDocのtimezone
部分を見ると、
TimeZone
to use for serialization (if needed). Special value ofDEFAULT_TIMEZONE
can be used to mean "just use the default", where default is specified by the serialization context, which in turn defaults to system defaults (TimeZone.getDefault()
) unless explicitly set to another locale.
とか書いてあるので、
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
と、アプリの初期時にやっておけば良いと思ったそこの貴方は残念賞。
Jacksonで明示的にデフォルト指定をしない場合のタイムゾーンは "GMT" 固定です。
com.fasterxml.jackson.databind.cfg.BaseSettings
を見ると、昔は TimeZone.getDefault()
を使っていたのかな、と思うようなコメントアウト(!!)があって目を疑います。
private static final TimeZone DEFAULT_TIMEZONE = // TimeZone.getDefault() /* [databind#915] 26-Sep-2015, tatu: Should be UTC, plan to change * it so for 2.7 */ TimeZone.getTimeZone("GMT");
エスパーするとしたら、タイムゾーンの環境依存で発見しにくいバグに悩まされて、デフォルトをGMT固定に変更したとかでしょうか...
尚、Jacksonの公式WikiにあるFAQの日付処理のトピックでは、一番最初に「タイムゾーン何も指定しなければGMTだから、よろしく」と書いてありました。 http://wiki.fasterxml.com/JacksonFAQDateHandling
これで勝てねば貴様は無能だ
以下のようなクラスをプロジェクトの適当な場所に作ればOKです。
@Configuration public class WebConfiguration extends WebMvcConfigurerAdapter { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() .timeZone("Asia/Tokyo") .dateFormat(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")); converters.add(new MappingJackson2HttpMessageConverter(builder.build())); } }
上のコードではついでにデフォルトの日付フォーマットも変更してます。