ZonedDateTime с неправильным часовым поясом, полученным из базы данных

У меня есть сущность под названием «Turno» с атрибутом ZonedDateTime.

   @Column(name = "fecha", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private ZonedDateTime fechaTurno;

При сохранении этого объекта у меня не возникает проблем, поскольку значение и часовой пояс правильно сохраняются в PostgreSQL. Проблема в том, что при получении строк из серверной части часовой пояс преобразуется в UTC.

Здесь видно как хранится дата (правильно)

И затем, когда я получаю эти строки, часовой пояс — «UTC».

Я уже настроил свой проект с помощью этих строк

Application.properties

spring.jackson.time-zone=America/Argentina/Buenos_Aires
spring.jpa.properties.hibernate.jdbc.time_zone=America/Argentina/Buenos_Aires

main.java


@PostConstruct
public void init(){
    TimeZone.setDefault(TimeZone.getTimeZone("America/Argentina/Buenos_Aires"));
}

Мне нужно получить дату в правильном часовом поясе (GMT-03). Или мне придется явно изменять каждый раз, когда я получаю эту дату.

Каково определение типа столбца в базе данных?

aled 29.05.2024 18:49

@aled Судя по картинке timestamp with time zone.

Anonymous 29.05.2024 19:04

Я считаю, что ваш удивительный результат задокументирован. Из документации PostgreSQL: Для timestamp with time zone внутренне сохраненное значение всегда находится в формате UTC… Так почему же PostgreSQL показывал -03 как «часовой пояс»? Я не знаю. Я предполагаю, что он применяет ваш часовой пояс по умолчанию. Что произойдет, если вы сохраните и получите значения в других часовых поясах? Похоже, после получения вам придется самостоятельно конвертировать в Америку/Аргентину/Буэнос-Айрес.

Anonymous 29.05.2024 19:11

(Для многих СУБД «метка времени с часовым поясом» на самом деле обещает слишком много и дает только «метку времени с часовым поясом UTC».)

Anonymous 29.05.2024 19:13

Я считаю, что обычное использование: -03 или -03:00 — это смещение UTC. Америка/Аргентина/Буэнос-Айрес — часовой пояс. Часовой пояс — это область, в которой используется одно и то же время и которая включает прошлые и известные будущие смещения UTC в этой области. В Буэнос-Айресе было летнее время (DST) до 2000 года, а затем с 2007 по 2009 год, и, следовательно, летом он находился со смещением UTC -2.

Anonymous 29.05.2024 19:20

Поясните свои скриншоты и примеры. Это первый снимок экрана из инструмента базы данных, такого как pgAdmin? На втором снимке экрана показан объект ZonedDateTime, но как был создан экземпляр этого объекта?

Basil Bourque 29.05.2024 21:58
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
6
78
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

вр; доктор

сущность под названием «Turno», имеющая атрибут ZonedDateTime.

Неправильный класс. Используйте OffsetDateTime для TIMESTAMP WITH TIME ZONE типа SQL.

Данные примера разумны

Что касается данных вашего примера, 2024-05-27 09:00:00-03 — это тот же момент, что и 2024-05-27T12:00Z. -03 означает отставание от UTC на три часа. Z — это сокращение от «смещение от UTC на ноль часов-минут-секунд». В тот момент Argentina/Buenos_Aires использовало смещение на три часа до UTC. Таким образом, 9 утра в этой зоне совпадает с полуднем по всемирному координированному времени, но компенсируется отрицательными тремя часами.

Другими словами, 2024-05-27 09:00:00-03 и 2024-05-27T12:00Z — это два способа взглянуть на одну точку на временной шкале.

JDBC сопоставляется с OffsetDateTime

ZonedDateTime не сопоставлен с типом SQL в JDBC.

Этот недостаток связан с тем, что стандарт SQL распознает только смещение от UTC, а не часовой пояс. Типы, определенные в стандарте, TIMESTAMP WITH TIME ZONE и TIMESTAMP WITHOUT TIME ZONE, являются неправильными; авторы стандартов имели в виду смещение от UTC. (Всего лишь одна из многих особенностей — это стандарт SQL.)

Смещение против зоны

В чем разница между смещением и зоной? Смещение — это всего лишь несколько часов, минут и секунд вперед или назад от временного меридиана UTC. Часовой пояс – это гораздо больше. Часовой пояс — это именованная история прошлых, настоящих и будущих изменений смещения, используемого людьми определенного региона по решению своих политиков.

Для обмена со столбцом базы данных типа, подобного 👉🏽 стандартному типу SQL TIMESTAMP WITH TIME ZONE, используйте только класс OffsetDateTime в Java.

В прямом JDBC:

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

… и:

myPreparedStatement.setObject( … , odt ) ;

Примените желаемый часовой пояс.

ZoneId z = ZoneId.of( "xx" ) ;
ZonedDateTime zdt = odt.at

Postgres хранит в формате UTC (смещение нуля).

Кроме того, помните, что Postgres всегда хранит значения TIMESTAMP WITH TIME ZONE со смещением от UTC, равным нулю часов-минут-секунд — всегда. Любое смещение или зона, указанная во входных данных, используется для корректировки даты и времени по UTC. Любое полученное значение всегда указывается в формате UTC — всегда.

После использования любого предоставленного смещения или зоны для адаптации к UTC Postgres отбрасывает это смещение/зону. Все, что остается, — это дата и время, видимые со смещением нуля.

Итак, это еще одна причина, по которой вы не можете использовать ZonedDateTime со своим TIMESTAMP WITH TIME ZONE столбцом: 👉🏽 не существует известного часового пояса, который можно было бы применить к полученному значению UTC.

Spring

Очевидно, Spring может динамически применять определенный часовой пояс к значению UTC, полученному из Postgres. Точно не знаю, так как Spring не использую. Но это то, что я понял из предоставленной вами информации.

Лично я бы предпочел получить точное значение из базы данных, а затем обработать данные. Но разумные люди могут с этим не согласиться.

Остерегайтесь промежуточного программного обеспечения

Вы сказали:

Здесь видно как хранится дата (правильно)

Нет, не правильно. Если то, что вы показываете на этом снимке экрана, взято из инструмента базы данных, такого как pgAdmin, этот инструмент вам лжет.

Postgres хранит значения TIMESTAMP WITH TIME ZONE только со смещением, равным нулю. Таким образом, отображаемое смещение на три часа (-03) от UTC на самом деле не соответствует действительности.

К сожалению, некоторые инструменты и промежуточное программное обеспечение предпочитают динамически применять часовой пояс по умолчанию к значению TIMESTAMP WITH TIME ZONE, хранящемуся в формате UTC. Эта антифункция создает иллюзию сохранения часового пояса.

При использовании таких инструментов с этой ошибочной антифункцией я предлагаю установить часовой пояс по умолчанию в UTC со смещением, равным нулю. Это может быть настройка сеанса, но зависит от инструмента.

Это невероятно полезная и исчерпывающая информация. Ваше здоровье!

David Conrad 29.05.2024 21:48

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