TimeZoneInfo.Local vs TimeZoneInfo.FindSystemTimeZoneById
Я работал с классами DateTime
и TimeZoneInfo
, и я столкнулся с интересным результатом со следующим кодом:
var dstStart = new DateTime(2013, 3, 10, 2, 0, 0, DateTimeKind.Local);
var result = TimeZoneInfo.Local.IsDaylightSavingTime(dstStart);
Результатом этого является False
. Я бы подумал, что это будет True
(DST начинается 10 марта в 2:00)
Затем я попробовал аналогичный код, используя FindSystemTimeZoneById
вместо:
var myTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var result = myTimeZone.IsDaylightSavingTime(dstStart);
Результат этого на удивление True
.
Затем я проверил, чтобы эти объекты представляли один и тот же часовой пояс:
myTimeZone.Id == TimeZoneInfo.Local.Id // returns True (Both are "Eastern Standard Time")
Мой вопрос: почему эти результаты разные, и что более важно, как я могу сделать их одинаковыми?
Мой компьютер определенно находится в Eastern Standard Time
часовом поясе
Дополнительная информация:
Я перепробовал свои компьютерные часы, и я провел несколько тестов для сравнения объекта TimeZoneInfo
, который был возвращен каждым из вышеперечисленных методов. Вот моя тестовая программа
var timeZoneFromLookup = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var dstStart = new DateTime(2013, 3, 10, 2, 0, 0, DateTimeKind.Local);
// -- The following return true --
Console.WriteLine("Equal? {0}", TimeZoneInfo.Local.Equals(timeZoneFromLookup));
Console.WriteLine("Has Same Rules? {0}", TimeZoneInfo.Local.HasSameRules(timeZoneFromLookup));
Console.WriteLine("Same Id? {0}", TimeZoneInfo.Local.Id == timeZoneFromLookup.Id);
Console.WriteLine("Same Base UTC Offset? {0}", TimeZoneInfo.Local.BaseUtcOffset == timeZoneFromLookup.BaseUtcOffset);
Console.WriteLine("Same Daylight Name? {0}", TimeZoneInfo.Local.DaylightName == timeZoneFromLookup.DaylightName);
Console.WriteLine("Same Display Name? {0}", TimeZoneInfo.Local.DisplayName == timeZoneFromLookup.DisplayName);
Console.WriteLine("Same Standard Name? {0}", TimeZoneInfo.Local.StandardName == timeZoneFromLookup.StandardName);
Console.WriteLine("Same Support For DST? {0}",
TimeZoneInfo.Local.SupportsDaylightSavingTime == timeZoneFromLookup.SupportsDaylightSavingTime
);
Console.WriteLine("Same result as to whether date/time is ambiguous? {0}",
timeZoneFromLookup.IsAmbiguousTime(dstStart) == TimeZoneInfo.Local.IsAmbiguousTime(dstStart)
);
// -- The following return false --
Console.WriteLine("Same utc offset result? {0}",
timeZoneFromLookup.GetUtcOffset(dstStart) == TimeZoneInfo.Local.GetUtcOffset(dstStart)
);
Console.WriteLine("Same Conversion to UTC? {0}",
TimeZoneInfo.Local.GetUtcOffset(dstStart) == timeZoneFromLookup.GetUtcOffset(dstStart)
);
Console.WriteLine("Same result as to whether date/time is invalid? {0}",
timeZoneFromLookup.IsInvalidTime(dstStart) == TimeZoneInfo.Local.IsInvalidTime(dstStart)
);
Console.WriteLine("Same result as to whether date/time is DST? {0}",
timeZoneFromLookup.IsDaylightSavingTime(dstStart) == TimeZoneInfo.Local.IsDaylightSavingTime(dstStart)
);
Ответы
Ответ 1
Я немного размышлял, и я считаю, что несогласованность проистекает из того, как System.TimeZoneInfo+CachedData.GetCorrespondingKind(TimeZoneInfo timeZone)
возвращает DateTimeKind.Local
только в том случае, когда timeZone == this.m_localTimeZone
(т.е. когда аргумент был тем же самым экземпляром, что и свойство TimeZoneInfo.Local
основанный на).
В случае, когда вы передаете этот другой экземпляр TimeZoneInfo
, который вы получили от TimeZoneInfo.FindSystemTimeZoneById
, я ожидаю, что он вернет DateTimeKind.Unspecified
.
Это (возможно, между прочим) влияет на System.TimeZoneInfo.IsDaylightSavingTime(DateTime dateTime)
, где в случае, когда dateTime.Kind
является локальным, он выполняет преобразование между существенно TimeZoneInfo.Local
и вашим экземпляром TimeZoneInfo
и основывает преобразование на то, что GetCorrespondingKind
говорит о исходных и целевых часовых поясах (преобразование возвращает исходное datetime в случае, когда источник и цель являются локальными).
Ответ 2
Разница в поведении при использовании нелокального TimeZoneInfo
определяется в документации MSDN.
Причина, по которой первый результат False
заключается в том, что созданный вами объект DateTime технически неоднозначен. Нет 2:00 утра 10 марта 2013 года в местном часовом поясе EST.
В документе указывается, что метод IsDaylightSavingTime()
"зависит от отношения между часовым поясом, представленным объектом TimeZoneInfo, и свойством Kind для параметра dateTime". В таблице в разделе "Примечания" дается описание каждой из возможных комбинаций.
При указании часового пояса путем явного вызова FindSystemTimeZoneById
аргумент DateTime "Local Kind" сначала преобразуется из Local в указанный часовой пояс. На этом шаге неопределенное время разрешено в законное значение.
Попробуйте добавить это к вашему тесту:
var dstStart = new DateTime(2013, 3, 10, 2, 0, 0, DateTimeKind.Local);
dstStart = dstStart.ToUniversalTime();
dstStart = TimeZoneInfo.ConvertTime(dstStart, TimeZoneInfo.Utc, myTimeZone);
Конечное значение dstStart
становится '3/10/2013 3:00:00 AM' (если ваша машина по-прежнему находится в EST). Такое же преобразование происходит в IsDaylightSavingTime()
по параметру локального вида, что иллюстрирует, почему он возвращает True
.
Настоящий сюрприз заключается в том, что метод IsDaylightSavingTime()
не поднимает ArgumentException
в любом случае. В документации говорится, что он выдаст исключение, если будет указан недопустимый параметр DateTime
, тип которого DateTimeKind.Local
, но это явно не так.
Edit:
После просмотра исходного кода и комментариев для класса TimeZoneInfo
я пришел к выводу, что метод IsDaylightSavingTime()
не предназначен для исключения исключений. Это ошибка в документации.