TimeZoneInfo SupportsDaylightSavingTime не возвращает то, что я ожидаю, почему? - PullRequest
2 голосов
/ 05 марта 2019

Когда я начал работать с часовыми поясами, мне было нелегко, но чем больше я погружался в тему и тем больше терял себя. В Stack Overflow уже есть десятки похожих вопросов, в основном пытающихся решить конкретные проблемы, но ни один из них (включая ответы) не помог мне понять тему так, как я хотел. Кроме того, как просили другие, время от времени это выглядит как изменения часового пояса, и Microsoft приходится спешить с обновлениями, распространяемыми через Центр обновления Windows, чтобы справиться с этим.

В моем случае я заметил, что как минимум 2 TZ неверны:

  • Алтайское стандартное время
  • Стандартное время Аргентины

Поскольку, как я упоминал выше, TZ взяты из локальной системы, я пытался искать обновления, но у меня их нет. Так что либо я действительно упускаю что-то важное, либо Microsoft не заботится об этих двух часовых поясах.

Информация об обновлениях DST / TZ

Для Летнее время , Microsoft имеет четкую политику и заявляет:

Microsoft прилагает усилия для включения этих изменений в Windows и публикует обновление через Центр обновления Windows (WU). Каждое обновление DST / TZ, выпущенное через WU, будет содержать самые последние данные времени, а также будет заменять любое ранее выпущенное обновление DST / TZ

Последние обновления можно найти на специальном сайте Microsoft Tech Community

Чтобы понять, я создал простое консольное приложение (см. Код ниже), проблема в том, что этого было недостаточно.

Обзор наиболее важных классов и методов, использованных в примере

        private static ConsoleColor DefaultColor;
    static void Main(string[] args)
    {
        DefaultColor = Console.ForegroundColor;

        foreach (var timeZoneInfo in TimeZoneInfo.GetSystemTimeZones().OrderBy(tz => tz.Id))
        {
            var firstQuart = new DateTime(2019, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var secondQuart = new DateTime(2019, 4, 1, 0, 0, 0, DateTimeKind.Utc);
            var thirdQuart = new DateTime(2019, 7, 1, 0, 0, 0, DateTimeKind.Utc);
            var lastQuart = new DateTime(2019, 10, 1, 0, 0, 0, DateTimeKind.Utc);

            if (timeZoneInfo.Id == "Altai Standard Time" || 
                timeZoneInfo.Id == "Argentina Standard Time" ||
                timeZoneInfo.Id == "GMT Standard Time"
                )
           {
                Log($"{timeZoneInfo.DisplayName} (ID: {timeZoneInfo.Id})", ConsoleColor.Yellow);
                Log($"StandardName: {timeZoneInfo.StandardName}");
                Log($"DST: {timeZoneInfo.SupportsDaylightSavingTime}");
                Log($"Daylight Name: {timeZoneInfo.DaylightName}");
                Log();
                Log($"UTC Offset: {timeZoneInfo.BaseUtcOffset}");
                Log($"Dates for each quarter in this year");

                var convertedFirstQuart = TimeZoneInfo.ConvertTimeFromUtc(firstQuart, timeZoneInfo);
                var convertedSecondQuart = TimeZoneInfo.ConvertTimeFromUtc(secondQuart, timeZoneInfo);
                var convertedThirdQuart = TimeZoneInfo.ConvertTimeFromUtc(thirdQuart, timeZoneInfo);
                var convertedLastQuart = TimeZoneInfo.ConvertTimeFromUtc(lastQuart, timeZoneInfo);

                Log();
                Log($"First quarter: {TimeZoneInfo.ConvertTimeFromUtc(firstQuart, timeZoneInfo)}", ConsoleColor.Green);
                Log($"DST (DateTime.IsDaylightSavingTime): {convertedFirstQuart.IsDaylightSavingTime()}");
                Log($"DST (TimeInfo.IsDaylightSavingTime(DateTime): {timeZoneInfo.IsDaylightSavingTime(convertedFirstQuart)}");
                Log($"Ambiguous/Invalid: {timeZoneInfo.IsAmbiguousTime(convertedFirstQuart)}/{timeZoneInfo.IsInvalidTime(convertedFirstQuart)}");
                Log();
                Log($"Second quarter: {TimeZoneInfo.ConvertTimeFromUtc(secondQuart, timeZoneInfo)}", ConsoleColor.Green);
                Log($"DST (DateTime.IsDaylightSavingTime): {convertedSecondQuart.IsDaylightSavingTime()}");
                Log($"DST (TimeInfo.IsDaylightSavingTime(DateTime): {timeZoneInfo.IsDaylightSavingTime(convertedSecondQuart)}");
                Log($"Ambiguous/Invalid: {timeZoneInfo.IsAmbiguousTime(convertedSecondQuart)}/{timeZoneInfo.IsInvalidTime(convertedSecondQuart)}");
                Log();
                Log($"Third quarter: {TimeZoneInfo.ConvertTimeFromUtc(thirdQuart, timeZoneInfo)}", ConsoleColor.Green);
                Log($"DST (DateTime.IsDaylightSavingTime): {convertedThirdQuart.IsDaylightSavingTime()}");
                Log($"DST (TimeInfo.IsDaylightSavingTime(DateTime): {timeZoneInfo.IsDaylightSavingTime(convertedThirdQuart)}");
                Log($"Ambiguous/Invalid: {timeZoneInfo.IsAmbiguousTime(convertedThirdQuart)}/{timeZoneInfo.IsInvalidTime(convertedThirdQuart)}");
                Log();
                Log($"Last quarter: {TimeZoneInfo.ConvertTimeFromUtc(lastQuart, timeZoneInfo)}", ConsoleColor.Green);
                Log($"DST (DateTime.IsDaylightSavingTime): {convertedLastQuart.IsDaylightSavingTime()}");
                Log($"DST (TimeInfo.IsDaylightSavingTime(DateTime): {timeZoneInfo.IsDaylightSavingTime(convertedLastQuart)}");
                Log($"Ambiguous/Invalid: {timeZoneInfo.IsAmbiguousTime(convertedLastQuart)}/{timeZoneInfo.IsInvalidTime(convertedLastQuart)}");
                Log("==============================================================");
                Log();
            }
        }

        Console.ReadKey();
    }

    private static void Log(string message = "", ConsoleColor? color = null)
    {
        if(color.HasValue)
            Console.ForegroundColor = color.Value;

        Console.WriteLine(message);
        Console.ForegroundColor = DefaultColor;
    }
}

Учитывая, что мой локальный TZ - GMT, и мы используем DST, вывод будет следующим:

enter image description here enter image description here

TimeZoneInfo.SupportsDaylightSavingTime () : Официальная документация

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

var zones = TimeZoneInfo.GetSystemTimeZones();
foreach(TimeZoneInfo zone in zones)
{
   if (! zone.SupportsDaylightSavingTime)
      Console.WriteLine(zone.DisplayName);
}

TimezoneInfo.IsDaylightSavingTime (DateTime) : Официальная документация

Указывает, попадает ли указанная дата и время в диапазон переход на летнее время для часового пояса текущего TimeZoneInfo объект.

DateTime.IsDaylightSavingTime () : Официальная документация

Указывает, находится ли этот экземпляр DateTime в дневном свете. сохранение временного диапазона для текущего часового пояса.

Важно понимать (сначала я этого не делал), что метод IsDaylightSavingTime в экземпляре DateTime всегда возвращает запрошенную информацию с учетом Часовой пояс локальной системы .

Анализировать вывод

Сосредоточив внимание на Аргентине по стандартному времени , мы видим, что TimeZoneInfo.SupportsDaylightSavingTime , возвращают true , это неверная информация, потому что везде, где я искал это, я нашел противоположный результат.

Даже если поддержка DST кажется неправильной, преобразование UTC DateTime в ART TZ с использованием C # всегда дает правильный результат.

Что заставляет меня думать, что я до сих пор не понимаю всю картину здесь, это то, что TimeInfo.IsDaylightSavingTime (DateTime) return false , что я и ожидал.

Постоянное летнее время

Согласно википедии https://en.wikipedia.org/wiki/Daylight_saving_timeПереход к «постоянному переходу на летнее время» (пребывание в летние часы в течение всего года без смены времени) иногда поддерживается и в настоящее время осуществляется в некоторых юрисдикциях, таких как Аргентина, Беларусь, [78] Канада (например, Саскачеван), Исландия, Кыргызстан,Малайзия, Марокко, Намибия, Сингапур, Турция, Туркменистан и Узбекистан. [164] Это может быть результатом следования часового пояса соседнего региона, политической воли или других причин.

Итак, подведу итог, мои открытыевопросы:

  • Почему TimezoneInfo.SupportsDaylightSavingTime () return true но TimeInfo.IsDaylightSavingTime (DateTime) return false ?
  • Как я могу убедиться, что у меня есть последние обновления DST / TZ от Microsoft, помимо того, что я объяснил выше?

1 Ответ

2 голосов
/ 06 марта 2019

Короткий ответ

TimeZoneInfo.SupportsDaylightSavingTime учитывает все данные о часовых поясах, доступные в системе, а не только данные текущего года. И у Argentina Standard Time, и у Altai Standard Time есть периоды, когда действовал DST, в течение периодов времени, когда Windows отслеживает их.


Более длинный ответ

Документация для TimeZoneInfo.SupportsDaylightSavingTime (с которой вы уже связались в своем вопросе) объясняет:

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

Что немного менее понятно, так это то, что он имеет в виду конкретно TimeZoneInfo.AdjustmentRule объекты, возвращаемые методом TimeZoneInfo.GetAdjustmentRules, и что это all правил для система, а не только на текущий год.

Политика Microsoft гласит, что Windows отслеживает все изменения, начиная с 2010 года. Однако некоторые часовые пояса (например, для Аргентины) уже отслеживали изменения до того, как была написана политика, поэтому в некоторых случаях вы увидите более ранние данные.

В реестре Windows вы можете найти все данные о часовых поясах, о которых знает система, по следующему ключу:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones

Каждое из значений TimeZoneInfo.Id соответствует подразделу в этом ключе, а правила настройки (если таковые имеются) будут ниже Dynamic DST для этого.

Для Argentina Standard Time\Dynamic DST мы находим данные с 2006 по 2010 год.

image

Даже не расшифровывая данные, мы видим, что между годами были различия. Глядя на timeanddate.com здесь дает нам детали:

image

Похоже, что летнее время действовало в течение летних сезонов 2007-08 и 2008-09 годов. (В Аргентине, находящейся в южном полушарии, лето делится на два года.)

Действительно, мы можем видеть это из .NET:

var tz = TimeZoneInfo.FindSystemTimeZoneById("Argentina Standard Time");
var dt = new DateTime(2008, 1, 1);
var dst = tz.IsDaylightSavingTime(dt); // True

Таким образом, является подходящим для TimeZoneInfo.SupportsDaylightSavingTime для возврата True, потому что действительно есть действительные даты, которые были в летнее время для этого часового пояса.

То же самое можно найти и на Алтае. Windows отслеживает данные с 2010 года, и DST существовал в 2010 году .

image

Обратите внимание, что оно также имеет изменения в стандартное время в 2014 году и снова в 2016 году. Это еще одна причина, по которой может существовать правило корректировки. К сожалению, в Windows нет способа смоделировать это без использования переходов, помеченных как «начало DST» или «конец DST». Таким образом, некоторая часть этих лет вернется True с IsDaylightSavingTime, хотя ни одна из сторон перехода не считалась переходом на летнее время. Это известная проблема с часовыми поясами в Windows. Это компромисс, который гарантирует, что используется правильное смещение UTC, даже если переход предназначен для изменения стандартного времени вместо перехода на летнее время.

Если вам абсолютно необходимо знать, был ли переход связан с DST или нет, вы можете вместо этого использовать данные часового пояса IANA через библиотеку Noda Time . Свойство ZoneInterval.Savings скажет вам это. Однако , это говорит о «постоянном летнем времени». Даже в базе данных IANA они обычно рассматриваются как изменения стандартного времени, а не истинного перехода на летнее время. Таким образом, вы, вероятно, найдете случаи, когда кто-то может сказать: «Мы постоянно принимаем летнее время», но данные не совпадают.

Относительно вашего последнего вопроса:

Как я могу убедиться, что у меня есть последние обновления DST / TZ от Microsoft, помимо того, что я объяснил выше?

Достаточно просто убедиться, что вы используете Центр обновления Windows. Все обновления DST / TZ, перечисленные на сайте сообщества, развернуты с регулярными обновлениями для всех поддерживаемых версий Windows, независимо от того, в каком часовом поясе вы находитесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...