Хранение даты/времен как UTC в базе данных

Я храню дату/времена в базе данных как UTC и вычисляю их в моем приложении назад к местному времени на основе определенного часового пояса. Скажите, например, что у меня есть следующая дата/время:

01/04/2010 00:00

Скажите, что это для страны, например, Великобритании, которая наблюдает DST (Летнее время), и в определенное время мы находимся в переходе на летнее время. Когда я преобразовываю эту дату в UTC и храню его в базе данных, это на самом деле хранится как:

31/03/2010 23:00

Поскольку дата была бы скорректирована-1 час в течение DST. Это хорошо работает когда Ваше наблюдение DST во время представления. Однако, что происходит, когда часы корректируются назад? Когда я вытягиваю ту дату от базы данных и преобразовываю ее в местное время, что конкретная дата и время была бы замечена как 31/03/2009 23:00 когда в действительности это было обработано как 01/04/2010 00:00.

Исправьте меня, если я неправ, но не являюсь этим определенным дефектом при хранении времен как UTC?

Пример преобразования Часового пояса

В основном то, что я делаю, хранит дату/времена того, когда информация отправляется моей системе, чтобы позволить пользователям делать отчет о диапазоне. Вот то, как я храню дату/времена:

public DateTime LocalDateTime(string timeZoneId)
{
    var tzi = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
    return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi).ToUniversalTime().ToLocalTime(); 
}

Хранение как UTC:

var localDateTime = LocalDateTime("AUS Eastern Standard Time");
WriteToDB(localDateTime.ToUniversalTime());
11
задан James 6 April 2010 в 16:53
поделиться

6 ответов

Вы не настраиваете дату перехода на летнее время в зависимости от того, наблюдаете ли вы в настоящее время за ними - вы настраиваете ее в зависимости от того, наблюдается ли летнее время в данный момент. как только вы описываете . Таким образом, в случае января вы не применяете корректировку.

Однако есть - некоторые местные времена неоднозначны. Например, 1:30 31 октября 2010 года в Великобритании может представлять либо 01:30 UTC, либо 02:30 UTC, потому что часы переводятся с 2:00 на 1:00. Вы можете перейти из любого момента , представленного в формате UTC, в местное время, которое будет отображаться в этот момент, но операция не является обратимой.

Точно так же для вас вполне возможно установить местное время, которое никогда не встречается - 1:30 28 марта 2010 года не было в Великобритании, например, - потому что в 1:00 часы перешли на 2:00.

Суть в том, что если вы пытаетесь представить момент времени, вы можете использовать UTC и получить однозначное представление. Если вы пытаетесь представить время в определенном часовом поясе, вам понадобится сам часовой пояс (например, Европа / Лондон) и либо представление момента в формате UTC, либо местная дата и время со смещением в это конкретное время. (чтобы устранить неоднозначность перехода на летнее время). Другой вариант - сохранить только UTC и смещение от него; это позволяет вам указать местное время в этот момент, но это означает, что вы не можете предсказать, какое местное время будет через минуту, поскольку вы действительно не знаете часовой пояс. (Это то, что в основном хранится в DateTimeOffset .)

Мы надеемся сделать это достаточно простым для обработки в Noda Time , но вам все равно нужно знать это как возможность.

РЕДАКТИРОВАТЬ:

Указанный вами код неверен. Вот почему.Я изменил структуру кода, чтобы его было легче увидеть, но вы увидите, что он выполняет те же вызовы.

var tzi = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
var aussieTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi);
var serverLocalTime = aussieTime.ToLocalTime(); 
var utcTime = serverLocalTime.ToUniversalTime();

Итак, давайте подумаем прямо сейчас - это 13:38 по моему местному времени (UTC + 1, в Лондоне), 12:38 UTC, 22:39 в Сиднее.

Ваш код выдаст:

aussieTime = 22:39 (correct)
serverLocalTime = 23:39 (*not* correct)
utcTime = 22:39 (*not* correct)

Вы должны не вызывать ToLocalTime в результате TimeZoneInfo.ConvertTimeFromUtc - он будет предполагать, что вызывается a UTC DateTime (если только он действительно не имеет DateTimeKind.Local, чего в данном случае не будет).

Таким образом, если вы точно сохраняете 22:39 в этом случае, вы не точно сохраняете текущее время в UTC.

32
ответ дан 3 December 2019 в 01:51
поделиться

Поправьте меня, если я ошибаюсь, но разве это не ошибка при сохранении времени в формате UTC?

Да, это так. Кроме того, дни корректировки будут иметь 23 или 25 часов, поэтому идиома предыдущего дня в то же время является местным временем - 24 часа неверно 2 дня в году.

Чтобы исправить это, нужно выбрать один стандарт и придерживаться его. Сохранение дат в формате UTC и отображение как локальных - это довольно стандартно. Просто не используйте ярлык для выполнения вычислений локально (+ - что-то) = новое время, и все в порядке.

0
ответ дан 3 December 2019 в 01:51
поделиться

Это огромный недостаток, но это не недостаток хранения времени в формате UTC (потому что это единственная разумная вещь - сохранение местного времени всегда катастрофа). Это недостаток концепции перехода на летнее время. Настоящая проблема в том, что информация о часовом поясе изменяется. Правила перехода на летнее время динамичны и историчны. Они время, когда переход на летнее время в США в 2010 году, отличается от того, когда оно началось в 2000 году. До недавнего времени Windows даже не содержала этих исторических данных, поэтому было практически невозможно делать что-то правильно. Вы должны были использовать базу данных tz , чтобы понять это правильно. Теперь я просто погуглил, и оказалось, что .NET 3.5 и Vista (я предполагаю, что и Windows 2008 тоже) внесли некоторые улучшения, а System.TimeZoneInfo фактически обрабатывает исторические данные. Взгляните на это .

Но в основном летнее время должно быть отменено.

0
ответ дан 3 December 2019 в 01:51
поделиться

Хорошо, что вы пытаетесь сохранить дату и время в формате UTC. Обычно лучше и проще думать о UTC как о фактической дате и времени, а местное время - это просто псевдонимы для этого. И UTC абсолютно критичен, если вам нужно выполнить какие-либо математические вычисления со значениями даты / времени, чтобы получить временные интервалы. Я обычно управляю датами внутри как UTC и конвертирую в местное время только при отображении значения пользователю (если это необходимо).

Ошибка заключается в том, что вы неправильно назначаете местный часовой пояс значениям даты / времени. В январе в Великобритании неправильно интерпретировать местное время как летнее. Вы должны использовать часовой пояс, который действовал в то время и в месте, которое представляет значение времени.

Перевод времени назад для отображения полностью зависит от требований системы. Вы можете отображать время как местное время пользователя или как исходное время для данных. Но в любом случае настройки перехода на летнее / летнее время должны применяться в соответствии с целевым часовым поясом и временем.

2
ответ дан 3 December 2019 в 01:51
поделиться

Это можно обойти, сохранив также конкретное смещение, используемое при преобразовании в UTC. В вашем примере вы бы сохранили дату как что-то вроде

31/12/2009 23:00 +0100

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

У этого подхода также есть свои проблемы. Время - штука беспорядочная.

1
ответ дан 3 December 2019 в 01:51
поделиться

Метод TimeZoneInfo.ConvertTimeFromUtc () решит вашу проблему:

using System;

class Program {
  static void Main(string[] args) {
    DateTime dt1 = new DateTime(2009, 12, 31, 23, 0, 0, DateTimeKind.Utc);
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
    Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(dt1, tz));
    DateTime dt2 = new DateTime(2010, 4, 1, 23, 0, 0, DateTimeKind.Utc);
    Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(dt2, tz));
    Console.ReadLine();
  }
}

Вывод:

12/31/2009 11:00:00 PM 
4/2/2010 12:00:00 AM

Вам понадобится .NET 3.5 или выше и работает на операционная система, которая сохраняет исторические изменения летнего времени (Vista, Win7 или Win2008).

1
ответ дан 3 December 2019 в 01:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: