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

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

01/04/2010 00:00

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

31/03/2010 23:00

поскольку дата будет скорректирована -1 часов для DST. Это отлично работает, когда вы наблюдаете DST во время представления. Однако, что происходит, когда часы настроены назад? Когда я вытаскиваю эту дату из базы данных и преобразую ее в локальное время, это конкретное время datetime будет рассматриваться как 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());

6 ответов


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

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

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

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

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

EDIT:

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

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 - предполагается, что он вызывается на UTC DateTime (если у него на самом деле нет DateTimeKind.Local, чего в данном случае не будет).

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


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

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

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


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

31/12/2009 23:00 +0100

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

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


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).


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


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

но в основном DST должен идти.