Отправка объекта, содержащего поле даты в формате JSON, в API, кажется, что часовой пояс изменился, а время "изменилось"

Я работаю над двумя проектами Spring Boot, которые обмениваются данными между собой (первый проект вызывает API POST, определенный во втором проекте, и отправляет ему объект DTO в формате JSON. Оба проекта в настоящий момент работают на одном компьютере, поэтому они должны иметь тот же часовой пояс (я полагаю...)

У меня проблема с правильной отправкой формата даты. Я постараюсь объяснить, что делаю и с какой проблемой сталкиваюсь.

В первом проекте у меня есть этот объект DTO:

import java.util.Date;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonPropertyOrder(alphabetic=true)
public class OneRowReadTrain1DTO {
    
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = JsonFormat.DEFAULT_TIMEZONE)
    @JsonProperty("Time_Stamp")
    private Date timeStamp;
    
    private Double v1;
    private Double v2;
    ........................................
    ........................................
    ........................................
    CONSTRUCTOR AND GETTER AND SETTER METHODS
}

Как вы можете видеть, у меня есть это поле timeStamp, помеченное этой аннотацией для преобразования поля в JSON:

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = JsonFormat.DEFAULT_TIMEZONE)

он также должен установить часовой пояс по умолчанию

Затем в класс этого первого проекта у меня есть этот метод, который выполняет запрос POST к API, определенному во втором проекте:

@Scheduled(fixedRate=90000)
public void insertOneRowReadTrain1Job() {
    System.out.println("COUNTER: " + counter);
    
    OneRowReadTrain1DTO currentElement = excelRowAsDtoList.get(counter);
    System.out.println("CURRENT DTO: " + currentElement);
    
    String insertApiUrl = baseUrl + "oneRowReadTrain1/";
    try {
        uri = new URI(insertApiUrl);
    } catch (URISyntaxException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    
    ResponseEntity<Integer> result = restTemplate.postForEntity(uri, currentElement, Integer.class);
    
    System.out.println("result: " + result);
    System.out.println("--------------------------------------------------------------------------------------------"); 
    counter++;
    
}

Как видите, я выполняю вызов API этой строкой:

ResponseEntity<Integer> result = restTemplate.postForEntity(uri, currentElement, Integer.class);

Используя отладчик, значение поля timeStamp кажется правильным, фактически это значение поля:

OneRowReadTrain1DTO [timeStamp=Sat Oct 17 06:00:14 PDT 2009, v1=6.5718506, v2=538.47812, ......]

Как вы можете видеть, значение этого поля даты - timeStamp=Sat 17 Oct 06:00:14, и это ожидаемое значение.

Затем вызывается API, определенный во втором проекте, и здесь я получаю странное поведение с этим значением поля.

Во втором проекте вызываемый API:

@PostMapping(value = "/train/{trainName}", consumes = "application/json")
@CrossOrigin(origins = "*", allowedHeaders = "*")
public int insertTrend(@PathVariable("trainName") String trainName, 
                       @RequestBody Map<String, Object> jsonTrainInfo) throws IOException {
    
    int result = 0;
    
    System.out.println("trainName: " + trainName);
    System.out.println("JSON: " + jsonTrainInfo);
    
    result = trainService.insertOneRowReadTrain(trainName, jsonTrainInfo);
    
    return result;
}

Как видите, полученная полезная нагрузка находится в этом параметре метода:

@RequestBody Map<String, Object> jsonTrainInfo

Проблема в том, что я получаю что-то вроде этого:

{Time_Stamp=2009-10-17 13:00:14, v1=6.5718506,.....}

Как вы можете видеть, значение этого поля: Time_Stamp = 2009-10-17 13:00:14, где раздел даты (2009-10-17) верен, но раздел времени полностью неверен, фактически полученный раздел времени равен 13:00:14, а не ожидаемые 06:00:14 (те, которые присутствуют в отправленном объекте).

Теперь, насколько я знаю, 6:00 PDT равно 13:00 GMT, но почему у меня эта проблема? Мне нужно, чтобы полученная дата находилась в том же часовом поясе (или я что-то упустил?)

Почему, когда он поступает в Map jsonTrainInfo, часовой пояс кажется измененным?

Что не так? Что мне не хватает? Как я могу попытаться исправить эту проблему? я схожу с ума

1 ответ

Дата

В своем первом проекте вы сохраняете метку времени как java.util.Dateв котором хранится информация в миллисекундах с "эпохи" (1 января 1970 г., 00:00:00 по Гринвичу). Поскольку у меня нет всего вашего кода, я предполагаю, что то, что вы видите в отладчике, - этоtoString()представление вашего класса. Здесь вы видите правильный результат, потому чтоjava.util.Date использует местный часовой пояс внутренней системы, но за ним стоит мгновение без какой-либо информации о часовых поясах.

Сериализация

Ваш OneRowReadTrain1DTO class определяет формат даты с помощью:@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = JsonFormat.DEFAULT_TIMEZONE) аннотация, краткий обзор документации по свойству часового пояса:

Часовой пояс для сериализации (при необходимости). Специальное значение DEFAULT_TIMEZONE может использоваться для обозначения "просто использовать значение по умолчанию", где значение по умолчанию определяется контекстом сериализации, который, в свою очередь, по умолчанию используется в системе по умолчанию (UTC), если явно не установлен другой часовой пояс.
По умолчанию: "##default"

И еще посмотрите, что говорится в документации о DEFAULT_TIMEZONE:

Значение, указывающее, что следует использовать часовой пояс по умолчанию (из контекста десериализации или сериализации): аннотация не определяет значение для использования.
ПРИМЕЧАНИЕ: значение по умолчанию здесь НЕ означает значения по умолчанию для JVM, а по умолчанию привязки данных Джексона, обычно UTC, но могут быть изменены в ObjectMapper.

Настройка Джексона

По умолчанию весенняя загрузка использует Джексона для сериализации / десериализации объектов и, в частности, MappingJackson2HttpMessageConverter. Он использует ДжексонObjectMapperдля записи / чтения данных. Когда вы устанавливаете часовой пояс наJsonFormat.DEFAULT_TIMEZONE, оказывается, это время в формате UTC, поэтому вы получаете метку времени в другом приложении в другом часовом поясе.

Если ваши данные всегда должны содержать даты в PDT "часовой пояс", вы можете установить его в аннотации в вашем DTO, например:@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "America/Los_Angeles")(Если вам интересно, почему я использую часовой пояс America/Los_Angeles, это объясняется здесь). Еще вы можете попробовать добавитьspring.jackson.time-zone=America/Los_Angeles на ваш application.properties, который установит часовой пояс в JacksonProperties, который будет использоваться во всем вашем приложении.

Заключительное примечание

Предоставленное решение должно работать с java.util.Dateобратите внимание, однако, что использовать этот класс не рекомендуется, рассмотрите возможность использования классов из java 8java.time пакет вместо этого.

Другие вопросы по тегам