Представляю, как через 10 лет во всех резюме IT специалистов появится новый скилл: знаком с квантовой механикой. А вообще, очень интересное направление. Когда-то читал об оптических транзисторах и, даже, молекулярных.
Потому что при использовании C# вы будете делать компенсирующие вещи, такие как конвертация в UTC и обратно, учет Kind и прочее. При «связке» вам ничего ненадо делать, т.к. работаете так, словно зон нет ровно до момента вывода данных. Я обобщаю, конечно, т.к. работать надо будет с DateTimeOffset, что чуть-чуть иначе, чем с DateTime.
Речь о том, что для правильной работы с UTC-all-the-time вам надо постоянно помнить о том, что:
— какая зона у юзера
— правильно ли я произвел конвертацию
— правильно ли передался Kind в момент, когда это важно
C DateTimeOffset вам придется об этом думать только в момент перед выдачей результата (на экран или во внешнюю систему).
Давайте рассмотрим случай:
— из базы достается ДТ значение без оффсета (DateTime)
— в коде C# вы конвертируете это в зону юзера, предполагая, что пришли UTC
1) Проблема с чтением из БД
var utc = new DateTime(2017, 4, 4, 10, 0, 0); // from DB expecting UTC
var targetTz = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
var local = TimeZoneInfo.ConvertTime(utc, targetTz);
Console.WriteLine("utc: {0}", utc);
Console.WriteLine("local: {0}", local);
Решение: Надо поставить Kind в Utс, или использовать TimeZoneInfo.ConvertTimeFromUtc().
2) Проблема с датами при сохранении
Суть примера: юзер вводит два раза одну и туже дату, но первую он вводит пока сервер в одной таймзоне, а вторую — когда в другой. Затем сервер пытается привести все к дате юзера и терпит неудачу, т.к. они не равны. А должны быть одинаковые.
var server1Timezone = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
var server2Timezone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var userInput1 = new DateTime(2017, 4, 4, 13, 0, 0);
var utc1 = TimeZoneInfo.ConvertTimeToUtc(userInput1, server1Timezone);
var userInput2 = new DateTime(2017, 4, 4, 13, 0, 0);
var utc2 = TimeZoneInfo.ConvertTimeToUtc(userInput2, server2Timezone);
var targetTz = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
var local1 = TimeZoneInfo.ConvertTimeFromUtc(utc1, targetTz);
var local2 = TimeZoneInfo.ConvertTimeFromUtc(utc2, targetTz);
Console.WriteLine("display: {0}", local1);
Console.WriteLine("display: {0}", local2);
Следует учесть, что такие строчки, как TimeZoneInfo.ConvertTimeToUtc(userInput1, server1Timezone); будут выполняться самой системой, т.е. это придется как-то решать. И если пропустите, то никто не упадет и не скажет об ошибке. Например, в SQL и в C# у вас могут использоваться такие вещи, которые оперируют с локальной таймзоной сервра. Например, GETDATE() или TimeZoneInfo.ConvertTime(). Вам надо будет постоянно следить за правильностью кода.
DateTimeOffset убирает «предположения» из работы с датами.
Как я понял, Вы говорите только о случае, когда вы десериализуете значение, но упускаете из виду ситуацию, когда десериализованная дата передается в другой компонент, который тоже имеет логику вокруг таймзон.
Про сервер. Потому что значения, сохраненные, как бы, в UTC после переезда сервера в другую зону, тоже переедут, т.к. рассчитывались относительно таймзоны смещения сервера. Задачу можно решить, но опять же, за счет больших напрягов для девелоперов: необходимо использовать только правильные методы работы с датами.
Потому что DateTime приводится к UTC в зависимости от Kind. Если проскочит неверный, то возникнет ошибка, которую вы просто так не отловите. Например, кто-то забыл выставить Kind, или наоборот выставил, когда не надо было. Вручную конвертацию производить? Без четкого обозначения смещения, вы обрекаете девелопера помнить об этом постоянно. А потом еще и тикеты из саппорта разгребать.
Имеет смысл или нет зависит от проекта. Если у вас не было поддержки таймзон, то переписывать в любом случае придется. И если сравнивать два подхода с UTC-all-the-time и DateTimeOffset, то при равных затратах на переписывание, второй проще технически.
Так и есть. Просто адепты UTC пропускают этот факт: все равно надо будет обеспечить хранение смещения. Толька в случае с UTC all the time самостоятельно с вытекающими из этого «напрягами».
Например, следующие вещи меня убедили в том, что DateTimeOffset лучше чем UTC all the time.
Меньше волнений из-за:
меньше волнений из-за сервера БД, т.к. он может работать в любой зоне (если кто-то случайно поменял ее — пофиг)
меньше кода проверок таймзоны входящих значений (например, например, проверка всяких Kind и действительно ли пришедшая дата в зоне N, а не UTC?)
проще соглашения по использованию дат внутри системы (не надо постоянно заботиться о передаче UTC)
проще обработка данных из разных таймзон, т.к. многие рутинные вещи выполняет фреймворк
новые C# проекты могут вовсе заботиться о конвертации только в момент, когда выводится пользователю. А сохранять в БД значения со смещением, которое предоставляется система. Т.е. разрабу вообще не надо будет думать о всяких конвертациях и UTC пока он не возвращает данные наружу (экран или внешние системы)
Простой переезд, т.к. в БД DateTimeOffset кастится к DateTime и обратно без ошибок, только отрезается информация о смещении. Поэтому на время переезда вы спокойно можете сосредоточиться только на базе, не трогая C#. Дат, обычно немного.
НО:
При чтении SQL DateTimeOffset в C# DateTime возникает исключение. Затраты на компенсацию соизмеримы с затратами по поддержке UTC. При этом смещение вам, скорее всего, все равно понадобится хранить рано или поздно. Только с подходом UTC вы обрекаете себя на дополнительные муки. Так же не стоит забывать о проблемах, которые связаны с DateTime.
Как определять таймзону не является большой проблемой. Проблема — сохранение этой информации в цикле разных расчетов и чтений-записи данных. Если эта информация всегда будет с date/time, то проблем почти не будет.
Представляю, как через 10 лет во всех резюме IT специалистов появится новый скилл: знаком с квантовой механикой. А вообще, очень интересное направление. Когда-то читал об оптических транзисторах и, даже, молекулярных.
— какая зона у юзера
— правильно ли я произвел конвертацию
— правильно ли передался Kind в момент, когда это важно
C DateTimeOffset вам придется об этом думать только в момент перед выдачей результата (на экран или во внешнюю систему).
— из базы достается ДТ значение без оффсета (DateTime)
— в коде C# вы конвертируете это в зону юзера, предполагая, что пришли UTC
1) Проблема с чтением из БД
Результат:
utc: 04/04/2017 10:00:00
local: 04/04/2017 10:00:00
Решение: Надо поставить Kind в Utс, или использовать TimeZoneInfo.ConvertTimeFromUtc().
2) Проблема с датами при сохранении
Суть примера: юзер вводит два раза одну и туже дату, но первую он вводит пока сервер в одной таймзоне, а вторую — когда в другой. Затем сервер пытается привести все к дате юзера и терпит неудачу, т.к. они не равны. А должны быть одинаковые.
Результат:
display1: 04/04/2017 13:00:00
display2: 04/04/2017 20:00:00
Следует учесть, что такие строчки, как TimeZoneInfo.ConvertTimeToUtc(userInput1, server1Timezone); будут выполняться самой системой, т.е. это придется как-то решать. И если пропустите, то никто не упадет и не скажет об ошибке. Например, в SQL и в C# у вас могут использоваться такие вещи, которые оперируют с локальной таймзоной сервра. Например, GETDATE() или TimeZoneInfo.ConvertTime(). Вам надо будет постоянно следить за правильностью кода.
DateTimeOffset убирает «предположения» из работы с датами.
DateTimeOffset эквивалентно хранению UTC. При условии, что используется базовое смещение от UTC.
Меньше волнений из-за:
Простой переезд, т.к. в БД DateTimeOffset кастится к DateTime и обратно без ошибок, только отрезается информация о смещении. Поэтому на время переезда вы спокойно можете сосредоточиться только на базе, не трогая C#. Дат, обычно немного.
НО:
При чтении SQL DateTimeOffset в C# DateTime возникает исключение. Затраты на компенсацию соизмеримы с затратами по поддержке UTC. При этом смещение вам, скорее всего, все равно понадобится хранить рано или поздно. Только с подходом UTC вы обрекаете себя на дополнительные муки. Так же не стоит забывать о проблемах, которые связаны с DateTime.