DateTimeFormatでは、JSONで受け取った日付をうまく取得できない場合があるという話

こんにちは。 エキサイト株式会社の三浦です。

Javaで日付形式の文字列を受け取る場合、 DateTimeFormat を使うことが多いと思います。 ですが実は、JSONで日付を受け取ろうとするとうまく行かない場合があります。

今回は、JSONで日付を受け取るにはどうするべきかを説明します。

JSON以外から、Javaで日付を受け取る方法

まずはGETリクエストで日付を受け取ることを考えてみます。

package sample;

import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;

@RequiredArgsConstructor
@RestController
@RequestMapping("sample")
public class SampleController {

    @GetMapping
    public String sample(@ModelAttribute SampleRequestModel sampleRequestModel) {
        return sampleRequestModel.datetime.toString();
    }

    public record SampleRequestModel(
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime datetime
    ) { }
}

このコードのように DateTimeFormat を使い、

http://localhost/sample?datetime=2022-01-01 00:00:01

このアクセスをすれば、

2022-01-01T00:00:01

こんな感じのレスポンスが返ってくるため、問題なく文字列の日付を受け取れていることがわかります。

JSONから、Javaで日付を受け取る方法

一方で、例えばどこかのAPIへアクセスし、レスポンスとして受け取ったJSONに日付が混ざっていた場合を考えましょう。

package sample;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDateTime;

@Data
public class SampleResponseModel {
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    LocalDateTime datetime;
}

こんなモデルで、

{
    "datetime": "2015-02-24 15:00:00"
}

このJSONを受け取ることを想定します。

先程のGETリクエストを考えると問題なさそうですが、実は実行すると

JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String \"2015-02-24 15:00:00\"

こんなエラーが起きてしまいます。

どうやら今回の日付のような、間にスペースが入っている日付は、JSONから受け取ろうとすると DateTimeFormat を使った場合エラーになってしまうようなのです。

ではどうするかというと、 JsonFormat を使えば問題ありません。

package sample;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.time.LocalDateTime;

@Data
public class SampleResponseModel {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    LocalDateTime datetime;
}

これで、無事受け取れるようになります。

終わりに

受け取る形式によって、同じデータでも微妙に必要な処理が変わってくる場合があります。 注意しましょう。