3 回答
TA贡献1863条经验 获得超2个赞
Zohar基本上是正确的。关键点是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得到确实具有的值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的切换发生在PST 02:00,因此两个值仍处于标准时间。
那么,如何在没有黑客的情况下获得正确的答案呢?只需确保将输入值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文件时间,而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在这里,因为没有Kind一个DateTimeOffset,所以只有一个将其转换方式。
最后一件事。请注意,这DateTime.AddHours(10)并不能弥补DST的差距。因此,当您谈论太平洋标准时间上午8点时,由于弹簧向前的间隙,实际仅经过了9个小时。10个实际经过的时间为太平洋标准时间上午9点。如果在添加10小时之前保持值的DateTimeOffset类型不变,则可以轻松地对此进行更正。
TA贡献1995条经验 获得超2个赞
更新
尽管我仍然支持我的第一个版本说明,但是如果时间实际上是一天的节光时间,则转换为本地时间将给出错误的结果测试。我不确定为什么。我在rextester上玩过您的代码,试图为此提出解决方案。我发现的最好的解决方案非常麻烦-它涉及DateTime根据DateTime您所获得的实例创建一个新实例,结果是FromFileTimeUtc:
var tzInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
var reminderstarttime = new DateTime(2018, 3, 10, 22, 0, 0);
var referencetime = reminderstarttime.AddHours(10); // ReferencedTime is in DST;
var isRemDstWithNormal = tzInfo.IsDaylightSavingTime(reminderstarttime);
var isRefDstWithNormal = tzInfo.IsDaylightSavingTime(referencetime);
var reminderStartTimeToUtc = reminderstarttime.ToFileTimeUtc();
var referenceTimeToUtc = referencetime.ToFileTimeUtc();
var reminderStartTimeFromUtc = DateTime.FromFileTimeUtc(reminderStartTimeToUtc);
var referencetimeFromUtc = DateTime.FromFileTimeUtc(referenceTimeToUtc);
var isRemDSTFromFileTime = tzInfo.IsDaylightSavingTime(reminderStartTimeFromUtc);
var isRefTimeDSTFromFileTime = tzInfo.IsDaylightSavingTime(referencetimeFromUtc);
var referenceTimeFromFileTimeUnspecified = new DateTime(referencetimeFromUtc.Ticks);
var isReferenceTimeFromFileTimeUnspecifiedDTS = tzInfo.IsDaylightSavingTime(referenceTimeFromFileTimeUnspecified);
Console.WriteLine("isRemDstWithNormal: " + isRemDstWithNormal +
"\nisRefDstWithNormal: " + isRefDstWithNormal +
"\nisRemDSTFromFileTime " + isRemDSTFromFileTime +
"\nisRefTimeDSTFromFileTime: " + isRefTimeDSTFromFileTime +
"\nisReferenceTimeFromFileTimeUnspecifiedDTS: "+ isReferenceTimeFromFileTimeUnspecifiedDTS);
我承认这可能是解决方法,而不是解决方案,但我想我在这个问题上花了更多时间,所以我已经负担得起了。也许对日期时间,文件时间和时区有更多经验的人可以对此有所了解。
第一版
问题是您referencetime的Kind财产是DateTimeKind.Unspecified。如果DateTimeKind.Local在的构造函数中指定reminderstarttime,则会得到准确的结果。
请参见方法文档中的“备注”部分DateTime.ToFileTimeUtc:
ToFileTimeUtc方法使用Kind属性来确定当前DateTime对象是本地时间,UTC时间还是被视为UTC时间的未指定类型的时间。如果是本地时间,它将在转换为Windows文件时间之前将时间转换为UTC。
(强调我的)
这是我的测试代码:
var tzInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
// This is the only change, except replacing the space with \n in the Console.WriteLine:
//var reminderstarttime = new DateTime(2018, 3, 10, 22, 0, 0);
var reminderstarttime = new DateTime(2018, 3, 10, 22, 0, 0, DateTimeKind.Local);
var referencetime = reminderstarttime.AddHours(10); // ReferencedTime is in DST;
var isRemDstWithNormal = tzInfo.IsDaylightSavingTime(reminderstarttime);
var isRefDstWithNormal = tzInfo.IsDaylightSavingTime(referencetime);
var reminderStartTimeToUtc = reminderstarttime.ToFileTimeUtc();
var referenceTimeToUtc = referencetime.ToFileTimeUtc();
var reminderStartTimeFromUtc = DateTime.FromFileTimeUtc(reminderStartTimeToUtc);
var referencetimeFromUtc = DateTime.FromFileTimeUtc(referenceTimeToUtc);
var isRemDSTFromFileTime = tzInfo.IsDaylightSavingTime(reminderStartTimeFromUtc);
var isRefTimeDSTFromFileTime = tzInfo.IsDaylightSavingTime(referencetimeFromUtc);
Console.WriteLine("isRemDstWithNormal: " + isRemDstWithNormal +
"\nisRefDstWithNormal: " + isRefDstWithNormal +
"\nisRemDSTFromFileTime " + isRemDSTFromFileTime +
"\nisRefTimeDSTFromFileTime: " + isRefTimeDSTFromFileTime);
结果:
isRemDstWithNormal: False
isRefDstWithNormal: False
isRemDSTFromFileTime False
isRefTimeDSTFromFileTime: False
- 3 回答
- 0 关注
- 150 浏览
添加回答
举报