Благодаря deHaar и всем остальным, кто внес свой вклад в комментарии, я наконец заставил его работать так, как я планировал, поэтому я собираюсь опубликовать краткий ответ на тот случай, если кому-то еще это понадобится.
Ключ использовал стандартную строку даты в запросе / ответе JSON вместе с надлежащими объектами Java 8 DateTime. То, что я отправлял, просто было недостаточно.
Таким образом, в соответствии с ISO-8601 форматами даты и времени:
Время выражается в UTC (Всемирное координированное время) со специальным обозначением UTC ("Z ").
Обратите внимание, что буква" T "появляется буквально в строке для обозначения начала элемента времени, как указано в ISO 8601.
Значение, мой APIдолжен общаться только в этих стандартизированных строках даты в формате UTC, а не в настраиваемых строках даты, например:
{
"dateFrom": "2019-12-01T16:00:00Z",
"dateTo": "2019-12-01T16:30:00Z"
}
Еще одна вещь, которую я сделал, как упоминалось в комментариях, была установка часового пояса БД в UTC и ничего не оставлял на волю случая,Так как я использую пружинную загрузку, было два способа сделать это:
In application.properties
установить свойство: spring.jpa.properties.hibernate.jdbc.time_zone=UTC
Или вСтрока подключения jdbc: serverTimezone=UTC
Я также добавил параметр useLegacyDatetimeCode=false
в строку выше, потому что я читал, что без него параметр serverTimezone
не действует(кто-то может исправить меня, если я ошибаюсь по этому поводу).
Сейчас идут споры, есть ли у java.sql.Timestamp
информация о часовом поясе или нет. Я прошел SO, чтобы узнать больше об этом, и оказалось, что в предыдущих версиях (до Java 8) действительно не было информации о часовом поясе (например, здесь ). Однако, как я ясно увидел в отладчике, у объекта Timestamp информация о зоне хранится отдельно, но она есть.
Согласно Mark Rotteveel из комментариев
a java.sql.Timestamp по спецификации представляет время в часовом поясе JVM по умолчанию
, что имеет смысл, потому что когда я изменил часовой пояс JVM, значения Timestamp также изменились.
Теперь, чтобы решить эту проблему, я видел множество предложений по установке часового пояса по умолчанию на UTC (потому что, если бы он не был установлен, он естественным образом откатился бы на JVM). Я также попробовал это, используя следующий код
System.setProperty("user.timezone", "UTC");
, и он работал, потому что теперь он конвертировал значения db в UTC, но что мне не понравилось в этом подходе, так это то, что он изменился все до времени UTC (дух), включая мои логи. Просто было неестественно делать это «насильно», как это.
Еще одно открытие, которое было для меня очень значимым, это то, что все говорили, что нужно полностью переключиться на java.time и егоклассы, которые дали 80% ответов на SO, довольно устарели и бесполезны, поскольку они предлагают использовать старые классы для таких вещей, как разбор, преобразование и т. д. (все еще не уверен, что смогу ли я полностью отказаться от java.sql.Timestamp
, так какна данный момент документы говорят, что это тип для сопоставления с форматом datetime
в БД, но сейчас я оставлю это).
Наконец, я создалнебольшой класс утилит, чтобы помочь мне с нужными мне преобразованиями.
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class DateTimeUtil {
public static Timestamp getTimestamp(String utcDateTime) {
LocalDateTime localDateTime = LocalDateTime.parse(utcDateTime, DateTimeFormatter.ISO_DATE_TIME);
return Timestamp.from(localDateTime.atZone(ZoneId.of("UTC")).toInstant());
}
public static String getUTCString(Timestamp dbTimestamp) {
return dbTimestamp.toInstant().atZone(ZoneId.of("UTC")).toString();
}
}
Первая функция используется для анализа строковой даты из запроса JSON.
Я выбрал LocalDateTime
вместо ZonedDateTime
из-за этой информации из java.time
doc:
По возможности рекомендуется использовать более простой класс без часового пояса. Широкое использование часовых поясов обычно усложняет приложение.
( РЕДАКТИРОВАТЬ: Код был обновлен с использованием LocalDateTime
неверный, потому что он удаляет часовой пояс UTC, а затем ведет себя точно так же, как и в ситуации с JVM. Извините, я проверил это неправильно)
Вторая функция используется для отправки ответа в формате JSON. Пока значения хранятся в БД так, как они должны быть, когда я их получал обратно, они конвертировались в мой местный часовой пояс (так как JVM находится в другом часовом поясе). Вот почему нужно было сказать: «Эй, это значение - часовой пояс UTC, покажите это так, а не в моем местном часовом поясе».
Итак, прежде чем сохранить свою сущность в БД, я установил ее метку времени с помощью getTimestamp()
, которая сохраняет его как UTC, и когда я получаю значение и готовлю свой DTO для ответа, я использую getUTCString()
, который такжеделает его форматом UTC в формате ISO, при этом БД действует так, как будто она находится в UTC, поэтому она не может добавить что-то свое к миксу И связь между клиентом и API стандартизирована, и каждый точно знает, чего ожидать.