1.4Kпросмотров
5 февраля 2026 г.
questionScore: 1.5K
Есть тут какая-то проблема?
CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, close_date TIMESTAMPTZ
);
INSERT INTO users (close_date) VALUES (NULL), (NULL);
----------------------------------------------
set timezone = 'UTC';
select from users; set timezone = 'Europe/Moscow';
UPDATE users
SET close_date = now() at time zone 'utc'; set timezone = 'UTC';
select from users; Ну очевидно, если спросил - значит есть. Результат будет примерно такой: если выполнить в 20 по мск, то в close_date будет 14 часов по utc. То есть 3 часа исчезнут😬
Вся проблема в том, что 'at time zone' возвращает не timestampz, а timestamp. По итогу: 1. now() at time zone 'utc' отнимаем -3, чтобы сделать utc, возвращает timestamp.
2. при сравнении происходит приведение timestamp к timestampz, postgres зная, что в сессии установлен мск таймзона приводит к utc, то есть еще раз делает -3. Мне это ногу прострелило из-за Npgsql.EnableLegacyTimestampBehavior=true, перенесенного из легаси системы. Потому что при таком флаге, если делаете запрос с DateTimeOffset.UtcNow - это превращается в now() at time zone 'utc', то есть только что созданные записи запрос не видет🚬 Без этого флага будет просто now() и локальная дата просто переведется в utc при присваивании.
Кстати да, никогда до этого не задумывался, но timestampz и timestamp работают не так же как и DateTimeOffset и DateTime - оффсет не хранится в БД. Если вы видите в каком-нибудь datagrip или psql, что дата выводиться с оффетом на конце, то это просто для удобного просмотра. Мне повезло 🙂 такого не должно было возникнуть, потому что по дефолту для всех сессий стоит timezone='UTC' в конфиге постгри. Да, если заменить в запросе 'Europe/Moscow' на 'UTC", то отниматься ничего не будет, поведение будет ожидаемым. Ну и бэст практис - это:
1. Генерировать дату на сервисе приводя к UTC(ака DateTimeOffset.UtcNow)
2. Использовать DateTimeOffset в ef core запросах.
3. Использовать timezone='UTC', если необходим Npgsql.EnableLegacyTimestampBehavior=true
4. Если третий пункт не про вас, то просто не писать это вручную now() at time zone 'utc', проверять чтобы его не было. Ладно хоть у меня это на селекте случилось, представляю как было бы весело на вставке данных такое получить🎧 PS: пример может не во всех playground работать, реакцию поставьте что ли🤑