Начиная с версии 6.0, Npgsql выдает исключение при попытке сохранить поле DateTimeOffset
с ненулевым смещением.
Например:
public class User
{
public Guid Id { get; private set; }
public DateTimeOffset? LockedUntil { get; private set; }
public void Lock()
{
LockedUntil = DateTimeOffset.Now.Add(TimeSpan.FromDays(1));
}
}
// ...
builder.Property(u => u.Id);
builder.Property(u => u.Login);
// ...
var user = dbContext.Users.First(u => u.Id == someId);
user.Lock();
dbContext.SaveChanges();
Это вызывает исключение: System.ArgumentException: ... only offset 0 (UTC) is supported
Мне было интересно, почему?
Я имею в виду, что знаю, что это задокументированное поведение.
Между тем, похоже, что Postgres прекрасно работает с часовыми поясами, если тип timestamptz
.
SELECT '2024-04-10T15:00:00.000Z'::timestamptz = '2024-04-10T13:00:00.000-02:00'::timestamptz;
--> true
create table test_timestamptz(some_field timestamptz);
INSERT INTO test_timestamptz(some_field)
VALUES ('2024-04-10T15:00:00.000Z'), ('2024-04-10T13:00:00.000-02:00'), ('2024-04-10T20:00:00.000+05:00');
SELECT COUNT(*) FROM test_timestamptz WHERE some_field = '2024-04-10T15:00:00.000Z';
--> 3
SELECT COUNT(*) FROM test_timestamptz WHERE some_field = '2024-04-10T16:00:00.000+01:00';
--> 3
В моем проекте я хотел бы иметь возможность работать со значениями времени, имеющими разные часовые пояса (особенно сравнивать их). DateTimeOffset
дает мне это и скрывает математику ТЗ.
В конце концов у меня получился вот такой конвертер:
public class DateTimeOffsetToDateTimeConverter : ValueConverter<DateTimeOffset, DateTime>
{
public DateTimeOffsetToDateTimeConverter() : base(
v => v.UtcDateTime,
v => new DateTimeOffset(v))
{
}
}
Может быть это какое-то техническое ограничение? Или я что-то неправильно понял (C# не мой основной PL)
🤔 А знаете ли вы, что...
C# поддерживает LINQ (Language Integrated Query) для удобного запроса и обработки данных.
Это описано в документации Npgsql:
Распространенной ошибкой пользователей является мнение, что тип PostgreSQL
timestamp with time zone
хранит часовой пояс в базе данных. Это не так: сохраняется только временная метка UTC. Не существует единого типа PostgreSQL, который хранил бы одновременно дату/время и часовой пояс, подобно .NET DateTimeOffset. Чтобы сохранить часовой пояс в базе данных, добавьте отдельный текстовый столбец, содержащий идентификатор часового пояса.
И из документации PostgreSQL:
Для
timestamp with time zone
внутреннее значение всегда находится в формате UTC (всемирное координированное время, традиционно известное как среднее время по Гринвичу, GMT). Входное значение, для которого явно указан часовой пояс, преобразуется в формат UTC с использованием соответствующего смещения для этого часового пояса. Если во входной строке не указан часовой пояс, то предполагается, что он находится в часовом поясе, указанном системным параметром TimeZone, и преобразуется в формат UTC с использованием смещения часового пояса.
Когда выводится временная метка со значением часового пояса, она всегда преобразуется из UTC в текущий часовой пояс и отображается как местное время в этом поясе. Чтобы увидеть время в другом часовом поясе, либо измените часовой пояс, либо используйте конструкцию AT TIME ZONE (см. раздел 9.9.4).
И
Мы не рекомендуем использовать
type time with time zone
(хотя PostgreSQL поддерживает его для устаревших приложений и для соответствия стандарту SQL). PostgreSQL предполагает ваш местный часовой пояс для любого типа, содержащего только дату или время.
Что можно наблюдать с помощью:
select *
from test_timestamptz;
Что дает мне:
Таким образом, поведение, включенное в Npgsql начиная с версии 6.0, предназначено для того, чтобы избежать потенциальной путаницы.
Если желательно поведение версии до 6.0, нет необходимости в специальном конвертере — вы можете включить поведение устаревшего драйвера с помощью переключателя:
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);