Pull to refresh
23
0
Aleksandr Goida @ETman

Software Developer

Send message

Представляю, как через 10 лет во всех резюме IT специалистов появится новый скилл: знаком с квантовой механикой. А вообще, очень интересное направление. Когда-то читал об оптических транзисторах и, даже, молекулярных.

Потому что при использовании C# вы будете делать компенсирующие вещи, такие как конвертация в UTC и обратно, учет Kind и прочее. При «связке» вам ничего ненадо делать, т.к. работаете так, словно зон нет ровно до момента вывода данных. Я обобщаю, конечно, т.к. работать надо будет с DateTimeOffset, что чуть-чуть иначе, чем с DateTime.
Никто не спорит, что с UTC это можно. Но зачем руками, когда DateTimeOffset позволяет все тоже самое с меньшими усилиями?
Такой оптимизм рождает потом баги.
Речь о том, что для правильной работы с UTC-all-the-time вам надо постоянно помнить о том, что:
— какая зона у юзера
— правильно ли я произвел конвертацию
— правильно ли передался Kind в момент, когда это важно

C DateTimeOffset вам придется об этом думать только в момент перед выдачей результата (на экран или во внешнюю систему).
Это же только пример, показывающий природу неоднозначности DateTime.
Давайте рассмотрим случай:
— из базы достается ДТ значение без оффсета (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);


Результат:
utc: 04/04/2017 10:00:00
local: 04/04/2017 10:00:00


Решение: Надо поставить 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);

Результат:
display1: 04/04/2017 13:00:00
display2: 04/04/2017 20:00:00

Следует учесть, что такие строчки, как TimeZoneInfo.ConvertTimeToUtc(userInput1, server1Timezone); будут выполняться самой системой, т.е. это придется как-то решать. И если пропустите, то никто не упадет и не скажет об ошибке. Например, в SQL и в C# у вас могут использоваться такие вещи, которые оперируют с локальной таймзоной сервра. Например, GETDATE() или TimeZoneInfo.ConvertTime(). Вам надо будет постоянно следить за правильностью кода.

DateTimeOffset убирает «предположения» из работы с датами.
Как я понял, Вы говорите только о случае, когда вы десериализуете значение, но упускаете из виду ситуацию, когда десериализованная дата передается в другой компонент, который тоже имеет логику вокруг таймзон.
Не понял вопрос. Кому «им»?
Что только люди ни придумают лишь бы UTC не хранить.

DateTimeOffset эквивалентно хранению UTC. При условии, что используется базовое смещение от UTC.
Про сервер. Потому что значения, сохраненные, как бы, в UTC после переезда сервера в другую зону, тоже переедут, т.к. рассчитывались относительно таймзоны смещения сервера. Задачу можно решить, но опять же, за счет больших напрягов для девелоперов: необходимо использовать только правильные методы работы с датами.
Потому что DateTime приводится к UTC в зависимости от Kind. Если проскочит неверный, то возникнет ошибка, которую вы просто так не отловите. Например, кто-то забыл выставить Kind, или наоборот выставил, когда не надо было. Вручную конвертацию производить? Без четкого обозначения смещения, вы обрекаете девелопера помнить об этом постоянно. А потом еще и тикеты из саппорта разгребать.
Имеет смысл или нет зависит от проекта. Если у вас не было поддержки таймзон, то переписывать в любом случае придется. И если сравнивать два подхода с UTC-all-the-time и DateTimeOffset, то при равных затратах на переписывание, второй проще технически.
Оба варианта решают задачу. 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, то проблем почти не будет.

Information

Rating
Does not participate
Location
Lozenets, Sofiya, Болгария
Date of birth
Registered
Activity