Зоар сделал это в основном правильно.Ключевым моментом является то, что DateTime.ToFileTimeUtc
, как и многие методы, работающие с DateTime
, зависит от Kind
, связанного со значением.Когда передается DateTimeKind.Unspecified
, этот конкретный метод предполагает, что ввод был уже в терминах UTC.Однако в вашем коде вы создаете эти значения, как если бы они соответствовали заданному часовому поясу.
Давайте сосредоточимся на виновнике:
var reminderStartTimeToUtc = (ulong)reminderstarttime.ToFileTimeUtc();
var referenceTimeToUtc = (ulong)referencetime.ToFileTimeUtc();
Поскольку оба reminderstarttime
и referencetime
имеют Kind == DateTimeKind.Unspecified
, их итоговые значения времени файла неверны.В частности:
reminderStartTimeToUtc: 131651928000000000
we wanted: 131652216000000000
difference: -288000000000 = -8 hours
referenceTimeToUtc: 131652288000000000
we wanted: 131652540000000000
difference: -252000000000 = -7 hours
Как видите, их значения отличаются от UTC для каждой соответствующей даты.
Преобразование их обратно в код с помощью DateTime.FromFileTimeUtc
возвращает значения, которые do have DateTimeKind.Utc
, который отбрасывает последующие проверки DST:
reminderStartTimeFromUtc: 2018-03-10 22:00:00 UTC
which is equivalent to: 2018-03-10 14:00:00 PST (UTC-8)
we wanted: 2018-03-10 22:00:00 PST (UTC-8)
referencetimeFromUtc: 2018-03-11 08:00:00 UTC
which is equivalent to: 2018-03-11 00:00:00 PST (UTC-8)
we wanted: 2018-03-11 08:00:00 PDT (UTC-8)
Обратите внимание, что переключение с PST на PDT происходит в 02:00 PST, поэтому оба значения по-прежнему в стандартномвремя.
Так, как мы можем исправить это без взлома?Просто убедившись, что наши входные значения соответствуют DateTimeKind.Utc
, перед тем как мы преобразуем время файла Windows.(DateTimeKind.Local
также будет работать, но здесь нет необходимости задействовать местный часовой пояс)
// First convert the DateTime values from their unspecified zone-specific times to UTC
var reminderStartTimeUtc = TimeZoneInfo.ConvertTimeToUtc(reminderstarttime, tzInfo);
var referenceTimeUtc = TimeZoneInfo.ConvertTimeToUtc(referencetime, tzInfo);
// Then convert THOSE values to file-times.
var reminderStartTimeToUtc = (ulong)reminderStartTimeUtc.ToFileTimeUtc();
var referenceTimeToUtc = (ulong)referenceTimeUtc.ToFileTimeUtc();
Остальная часть кода будет работать правильно, как есть, и вы получите ожидаемый результат.
Обратите внимание, что формулировка этих методов несколько сбивает с толку.DateTime.ToFileTimeUtc
означает, что вы конвертируете время файла, и что ввод DateTime
с .Kind == DateTimeKind.Unspecified
будет обрабатываться так, как если бы он был DateTimeKind.Utc
.Другой метод, DateTime.ToFileTime
, обрабатывает Unspecified
виды как Local
.Но они оба относятся к Utc
и Local
видам одинаково, и они оба генерируют время файла Windows, которое по своей сути основано на UTC.
В качестве альтернативы вышеприведенному подходу вымог бы использовать DateTimeOffset.ToFileTime
вместо этого.Смещение будет корректно учтено при преобразовании во время файла.
// construct a DateTimeOffset for each value
var reminderStartTimeDto = new DateTimeOffset(reminderstarttime, tzInfo.GetUtcOffset(reminderstarttime));
var referencetimeDto = new DateTimeOffset(referencetime, tzInfo.GetUtcOffset(referencetime));
// then just convert them to file times
var reminderStartTimeAsFileTime = reminderStartTimeDto.ToFileTime();
var referenceTimeAsFileTime = referencetimeDto.ToFileTime();
Обратите внимание, что здесь нет ToFileTimeUtc
, потому что * DateTimeOffset
нет, поэтому есть только один способ
И последнее.Обратите внимание, что DateTime.AddHours(10)
не учитывает разрыв DST.Таким образом, в то время как вы говорите о 8:00 по тихоокеанскому времени, было всего 9 фактических часов, из-за разрыва в упругом движении вперед.10 фактических прошедших часов будут 9 утра PDT.Вы можете легко исправить это, если вы сохраните свои значения в виде DateTimeOffset
типов до , добавив 10 часов.