Я сохраняю дату / время в базе данных как UTC и вычисляю их внутри своего приложения обратно к местному времени на основе определенного часового пояса. Скажем, например, у меня есть следующая дата / время:
01/04/2010 00:00
Скажем, это для страны, например. Великобритания, в которой соблюдается летнее время (летнее время), и в это время мы переходим на летнее время. Когда я конвертирую эту дату в UTC и сохраняю ее в базе данных, она фактически сохраняется как:
31/03/2010 23:00
Поскольку дата будет скорректирована -1 час на летнее время. Это отлично работает, когда вы наблюдаете летнее время во время отправки. Однако что происходит, когда часы возвращаются обратно? Когда я извлекаю эту дату из базы данных и конвертирую ее в местное время, эта конкретная дата-время будет отображаться как 31/03/2010 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());
Для справки: Поддержка значений индекса часовых поясов Microsoft. microsoft.com/en-gb/help/973627/ Tony 15.04.2020
Вы не настраиваете дату перехода на летнее время в зависимости от того, наблюдаете ли вы их в настоящее время - вы настраиваете ее в зависимости от того, соблюдается ли летнее время в тот момент, который вы описываете . Таким образом, в случае января вы не применяете корректировку.
Однако есть проблема - иногда местное время неоднозначно. Например, 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 в Сиднее.
Вам не следует не вызывать ToLocalTime по результату TimeZoneInfo.ConvertTimeFromUtc - он будет предполагать, что он вызывается в формате UTC DateTime (если только у него действительно не будет типа DateTimeKind.Local, чего в данном случае не будет).
Таким образом, если в этом случае вы точно сохраняете 22:39, вы не точно сохраняете текущее время в формате UTC.
Ответ 2 · рейтинг 2 · Jeffrey L Whitledge
Хорошо, что вы пытаетесь сохранить дату и время в формате UTC. Обычно лучше и проще рассматривать UTC как фактическую дату и время, а местное время - просто псевдонимы для этого. И UTC абсолютно критично, если вам нужно выполнить какие-либо математические вычисления для значений даты / времени, чтобы получить временные интервалы. Я обычно управляю датами внутри как UTC и конвертирую в местное время только при отображении значения пользователю (если это необходимо).
Ошибка, с которой вы столкнулись, заключается в том, что вы неправильно назначаете местный часовой пояс значениям даты / времени. В январе в Великобритании неверно интерпретировать местное время как летнее. Вы должны использовать часовой пояс, который действовал в то время и в месте, которое представляет значение времени.
Перевод времени назад для отображения полностью зависит от требований системы. Вы можете отображать время как местное время пользователя или как исходное время для данных. Но в любом случае настройки перехода на летнее / летнее время должны применяться в соответствии с целевым часовым поясом и временем.
Ответ 3 · рейтинг 1 · Thomas
Вы можете обойти это, также сохранив конкретное смещение, используемое при преобразовании в UTC. В вашем примере вы бы сохранили дату как что-то вроде
31/12/2009 23:00 +0100
При отображении этого для пользователя вы можете использовать то же смещение для преобразования или их текущее локальное смещение по вашему выбору.
У этого подхода также есть свои проблемы. Время - штука беспорядочная.
Ответ 4 · рейтинг 1 · Hans Passant
Метод 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).
Ответ 5 · dawg
Да, это так. Кроме того, дни корректировки будут иметь 23 или 25 часов, поэтому идиома предыдущего дня в то же время является местным временем - 24 часа неверно 2 дня в году.
Исправление состоит в том, чтобы выбрать один стандарт и придерживаться его. Сохранение дат в формате UTC и отображение как локальных - это довольно стандартно. Просто не используйте ярлык для выполнения вычислений локально (+ - что-то) = новое время, и все в порядке.
Ответ 6 · MK.
Это огромный недостаток, но это не недостаток хранения времени в формате UTC (потому что это единственное разумное решение - сохранение местного времени всегда катастрофа). Это недостаток концепции перехода на летнее время. Настоящая проблема в том, что информация о часовом поясе меняется. Правила перехода на летнее время динамичны и историчны. Они время, когда переход на летнее время в США в 2010 году, отличается от того, когда оно началось в 2000 году. До недавнего времени Windows даже не содержала этих исторических данных, поэтому было практически невозможно делать что-то правильно. Вам нужно было использовать базу данных tz, чтобы понять это правильно. Теперь я просто погуглил, и оказалось, что .NET 3.5 и Vista (я предполагаю, что и Windows 2008) сделали некоторые улучшения, а System.TimeZoneInfo фактически обрабатывает исторические данные. Взгляните на это.