Пример кода для сравнения времени в разных часовых поясах в C# - PullRequest
3 голосов
/ 20 июня 2020

Может ли кто-нибудь объяснить, почему переменная comparisonTime вычисляется с использованием logi c, как показано ниже, особенно используя следующие строки:

local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1].DaylightDelta;

и

TimeSpan comparisonTime = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();

Структура TimeSpan

 using System;
    
    public struct StoreInfo
    {
       public String store;
       public TimeZoneInfo tz;
       public TimeSpan open;
       public TimeSpan close;
    
       public bool IsOpenNow()
       {
          return IsOpenAt(DateTime.Now.TimeOfDay);
       }
    
       public bool IsOpenAt(TimeSpan time)
       {
          TimeZoneInfo local = TimeZoneInfo.Local;
          TimeSpan offset = TimeZoneInfo.Local.BaseUtcOffset;
    
          // Is the store in the same time zone?
          if (tz.Equals(local)) {
             return time >= open & time <= close;
          }
          else {
             TimeSpan delta = TimeSpan.Zero;
             TimeSpan storeDelta = TimeSpan.Zero;
    
             // Is it daylight saving time in either time zone?
             if (local.IsDaylightSavingTime(DateTime.Now.Date + time))
                delta = local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1].DaylightDelta;
    
             if (tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(DateTime.Now.Date + time, local, tz)))
                storeDelta = tz.GetAdjustmentRules()[tz.GetAdjustmentRules().Length - 1].DaylightDelta;
    
             TimeSpan comparisonTime = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();
             return comparisonTime >= open & comparisonTime <= close;
          }
       }
    }

1 Ответ

1 голос
/ 21 июня 2020

TimeZoneInfo.GetAdjustmentRules возвращает массив из AdjustmentRule объектов, каждый из которых определяет корректировки времени, сделанные для перехода на летнее время. Это включает в себя дату начала и окончания, а также количество времени, в течение которого часы корректируются, и когда эти правила go вступают в силу (правила меняются, например, в США был цикл апрель-октябрь, а теперь его март-ноябрь) . Правила: обычно отсортированы от самого старого к самому новому.

Рассматриваемый код неверен (см. Ниже), находит последнее правило в списке (local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1]) и вычисляет, сколько время изменяется этим правилом (DaylightDelta - в США это обычно один час, но в некоторых частях света могут быть установлены другие значения, например 30 минут)

Второй бит кода, о котором вы спрашиваете пытается нормализовать прошедшее время, если в одной / обеих зонах в настоящее время соблюдается летнее время. Это достигается путем добавления различий между двумя DaylightDelta (количество часов, сдвинутых вперед / назад) и двумя BaseUtcOffset (разница зоны без летнего времени и UT C). Затем он добавляет это время к исходному времени и вместо этого использует его для сравнения.

Код неверен по нескольким причинам (и это, возможно, не является исчерпывающим):

  • Это предполагает, что правила настройки упорядочены. В документации MSDN говорится, что они «обычно заказываются», но не дает никаких гарантий

  • Предполагается, что последнее правило корректировки является текущим. Как отмечалось выше, они не заказываются. Но они также могут содержать будущие правила, которые еще не вступили в силу

  • Это не учитывает время, близкое к полуночи, и может быть перенесено на следующий день. Это верно для обеих ветвей кода. Хотя открытие магазина после полуночи маловероятно, но вполне вероятно (я не знаю ваших бизнес-правил). Если магазин открыт с 8:00 до 2:00, тесты не пройдут в течение 1:00, когда он должен быть успешным

  • Аналогично, для магазина, где часы проходят до полуночи, расчет летнего времени может оказаться даже более неточно, если время попадает в границы перехода на летнее время.

  • TimeZoneInfo.Local можно изменить с помощью системных настроек. Фактически, в зависимости от того, отмечен ли на панели управления «Автоматически переходить на летнее время», информация о местном часовом поясе всегда может возвращать false для IsDaylightSavings и массив нулевой длины для local.GetAdjustmentRules. Это означает, что один и тот же код, запущенный на двух компьютерах, может возвращать разные результаты, даже если они находятся рядом друг с другом!

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

Работа с часовыми поясами и переходом на летнее время является сложной задачей. Правила все время меняются, а инструменты во фреймворке довольно неуклюжи, и с ними сложно работать (если вы их понимаете). Что еще хуже, они не всегда обновляются!

Я не собираюсь пытаться предоставить вам фиксированный код. Я предпочитаю оставлять все, что связано со временем и расчетами летнего времени, экспертам. Я настоятельно рекомендую заглянуть в библиотеку NodaTime для подобных вычислений. Он полностью посвящен теме, а правила часовых поясов регулярно обновляются. На их странице Почему мы существуем упоминаются некоторые из вещей, которые я упомянул выше, как и это сообщение в блоге , написанное одним из авторов библиотеки, Джоном Скитом.

...