Вычитание даты и времени UTC и не UTC в C#

#c# #datetime #timespan

#c# #дата и время #промежуток времени

Вопрос:

Я предположил, что при вычитании 2 даты и времени фреймворк проверит их часовой пояс и выполнит соответствующие преобразования.

Я протестировал это с помощью этого кода:

 Console.WriteLine(DateTime.Now.ToUniversalTime() - DateTime.UtcNow.ToUniversalTime());
Console.WriteLine(DateTime.Now.ToUniversalTime() - DateTime.UtcNow);
Console.WriteLine(DateTime.Now - DateTime.UtcNow);
  

Вывод:

 -00:00:00.0020002
-00:00:00.0020001
01:59:59.9989999
  

К моему удивлению, DateTime.Now - DateTime.UtcNow соответствующее преобразование не выполняется автоматически. В то же время DateTime.UtcNow это то же DateTime.UtcNow.ToUniversalTime() самое, что и, поэтому, очевидно, существует некоторый внутренний флаг, который указывает часовой пояс.

Правильно ли это, не выполняет ли эта платформа соответствующее преобразование часового пояса автоматически, даже если информация уже присутствует? Если это так, ToUniversalTime() безопасно ли применять как для UTC, так и для не UTC datetimes, т. Е. Уже UTC datetime не будет неправильно исправлено ToUniversalTime() ?

Комментарии:

1. Другой результат в mono: ideone.com/AfFUpV

2. @zerkms Я на VS2015, я не знаю, имеет ли это значение. Можете ли вы проверить ideone с помощью Visual Studio? Я лично не часто использовал ideone, поэтому не могу понять, как это сделать.

3. В DateTime нет часового пояса, в нем просто хранятся дата и время. Таким образом, он не знает, в каком часовом поясе находится это DateTime.

4. Вы просто констатируете факт, легко видимый из справочного источника . Вместо этого используйте DateTimeOffset .

Ответ №1:

Тип System.DateTime не содержит информации о часовом поясе, только .Kind свойство, которое указывает, является ли оно локальным или UTC. Но до .NET 2.0 не было даже .Kind свойства.

Когда вы вычитаете (или выполняете другую арифметику, например == , или > , вкл.) два DateTime значения, их «виды» вообще не учитываются. Учитывается только количество тиков. Это обеспечивает совместимость с .NET 1.1, когда не существовало никаких типов.

Функциональность, которую вы запрашиваете (и ожидаете), находится в более новом и богатом типе System.DateTimeOffset . В частности, если вы выполните вычитание DateTimeOffset.Now - DateTimeOffset.UtcNow , вы получите желаемый результат. DateTimeOffset Структура не имеет локального флага / UTC; вместо этого она содержит весь часовой пояс, например 02:00 в вашем регионе.

Комментарии:

1. Да, исходя из Qt, QDateTime содержит время UTC плюс смещение часового пояса. Я ожидал, что DateTime будет использовать его, и был немного смущен, когда Ханс Пассант упомянул DateTimeOffset, потому что это звучало как TimeSpan, а не как DateTime должно было быть.

2. Не очень правильное объяснение, ИМХО. В .NET Framework мало что остается (или когда-либо оставалось) связанным с обратной совместимостью .NET 1.0 / 1.1, .NET 2.0 — это совместимость, к которой стремятся.

3.@MahmoudAl-Qudsi Тип DateTimeOffset , о котором я упоминал, был введен в .NET 2.0. Когда я описывал, почему функциональность DateTime была выбрана так, как она была, когда .NET 2.0 был представлен и DateTime приобрел Kind свойство, я немного догадывался. Однако, когда я описал, как DateTime это работает на самом деле, начиная с .NET 2.0, я был прав.

Ответ №2:

Фреймворк ничего не делает с датой, если она уже UTC:

  internal static DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
{
    if (dateTime.Kind == DateTimeKind.Utc)
    {
        return dateTime;
    }
    CachedData cachedData = s_cachedData;
    return ConvertTime(dateTime, cachedData.Local, cachedData.Utc, flags, cachedData);
}
  

Комментарии:

1. Хотя вы должны быть осторожны DateTimeKind.Unknown (что часто может быть связано с запросом к БД). В этом случае, если вы вызовете ToUniversalTime , он примет локальный и преобразует в UTC, а если вы вызовете ToLocalTime , он примет UTC и преобразует в локальный.

2. @ChrisChilvers Это важное наблюдение.

3. Вопрос сосредоточен на случае, когда ToUniversalTime() не используется. Объяснение того, как ConvertTimeToUtc это работает, не помогает ответить на заданный вопрос. В этом случае метод не вызывается. Если вы хотите опубликовать исходный код, опубликуйте код для public static TimeSpan operator -(DateTime d1, DateTime d2) .

4. @JeppeStigNielsen , один из вопросов заключался в том, безопасно ли применять ToUniversalDateTime к дате и времени UTC.

5. Вы правы, я слишком много внимания уделял одному аспекту вопроса (тому, который в теме).