Некорректное время (часовой пояс) в ответе

Работаю с использованием CamundaBPM, собираю статистику по задачам и не только, выполняю запрос на собственный эндпоинт (на RestController) и получаю список процессов и их задач

@GetMapping("/history")
public ResponseEntity<List<ProcessDto>> getHistoryProcesses(HttpServletRequest request, HttpServletResponse response) {
    ...
}

Класс:

public class ProcessDto {
private String id;
private Date created;
...
}

Тайм зона стоит Europe/Moscow, дата (если смотреть через дебаггер) верная и имеет такой вид:

created: Mon Nov 01 14:16:40 MSK 2021 - тип Date, если его раскрыть, то

fastTime: 1635765400581

cdate: 2021-11-01T14:16:40.581+0300

В дебаг-консоли отображается верное время: DEBUG o.s.w.s.m.m.a.HttpEntityMethodProcessor - Writing [[ProcessDto(id=2fe11c6f-3b05-11ec-978e-0242ac110003, created=Mon Nov 01 14:16:40 MSK 2021, ownerName (truncated)...

Но проблема в том, что в ответе время отображается (в браузере, в постмане) на 3 часа меньше, чем должно быть."created": "2021-11-01T11:16:40.581+0000", и имеет уже такой вид

Я предполагаю, что проблема в сериализации (?) дат такого типа, нужно менять конфиг Jackson или что-то еще?


Ответы (1 шт):

Автор решения: Alex Rudenko

Время, которое отображается в консоли, выводится в формате по умолчанию, заданном в методе Date::toString:

public String toString()
Converts this Date object to a String of the form:
dow mon dd hh:mm:ss zzz yyyy

Jaskson использует свой класс для форматирования даты StdDateFormat без часового пояса и с локалью en_US, используя формат ISO-8601:

ObjectMapper mapper = new ObjectMapper();

System.out.println(mapper.getDateFormat());
// DateFormat com.fasterxml.jackson.databind.util.StdDateFormat: (timezone: null, locale: en_US, lenient: null)

Соответственно, для изменения формата даты нужно либо сконфигурировать ObjectMapper:

ObjectMapper mskDateMapper = new ObjectMapper();
mskDateMapper.setDateFormat(new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US));
mskDateMapper.getDateFormat().setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));

ProcessDto p = new ProcessDto("id", new Date());
System.out.println(p);
System.out.println("----");
System.out.println(mskDateMapper.writerWithDefaultPrettyPrinter().writeValueAsString(p));

Вывод:

ProcessDto [id=id, created=Wed Dec 01 00:05:41 EET 2021]  // toString, default time zone
----
{
  "id" : "id",
  "created" : "Wed Dec 01 01:05:41 MSK 2021"
}

Либо задать формат на уровне сериализуемого DTO класса (тогда настройки маппера будут проигнорированы):

static class ProcessDto {
    private final String id;
    
    @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="EEE MMM dd HH:mm:ss z yyyy", timezone="Asia/Novosibirsk", locale = "en_US")
    private final Date created;
    // конструктор, геттеры, toString
}

Вывод в часовом поясе Новосибирска:

ProcessDto [id=id, created=Wed Dec 01 00:10:29 EET 2021]
----
{
  "id" : "id",
  "created" : "Wed Dec 01 05:10:29 NOVT 2021"
}
→ Ссылка