C# IsDayLightSavingTime方法为同一时区和同一时间返回不同的值
以下代码用于检查DST中的特定时间,或不返回正常日期时间和从filetime获得的相同时间的不同值:C# IsDayLightSavingTime方法为同一时区和同一时间返回不同的值,c#,.net,.net-4.5,dst,C#,.net,.net 4.5,Dst,以下代码用于检查DST中的特定时间,或不返回正常日期时间和从filetime获得的相同时间的不同值: var tzInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"); var reminderstarttime = new DateTime(2018, 3, 10, 22, 0, 0); var referencetime = reminderstarttime.AddHours(10); // Referen
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 = (ulong)reminderstarttime.ToFileTimeUtc();
var referenceTimeToUtc = (ulong)referencetime.ToFileTimeUtc();
var reminderStartTimeFromUtc = DateTime.FromFileTimeUtc((long)reminderStartTimeToUtc);
var referencetimeFromUtc = DateTime.FromFileTimeUtc((long)referenceTimeToUtc);
var isRemDSTFromFileTime = tzInfo.IsDaylightSavingTime(reminderStartTimeFromUtc);
var isRefTimeDSTFromFileTime = tzInfo.IsDaylightSavingTime(referencetimeFromUtc);
Console.WriteLine("isRemDstWithNormal: " + isRemDstWithNormal +
" isRefDstWithNormal: " + isRefDstWithNormal +
" isRemDSTFromFileTime " + isRemDSTFromFileTime +
" isRefTimeDSTFromFileTime: " + isRefTimeDSTFromFileTime);
此代码运行良好。之所以对夏令时做出这样的回应,是因为美国和加拿大的夏令时于2018年3月11日凌晨2:00开始。更新 虽然我仍然坚持我的第一个版本的解释,但如果转换为本地时间实际上是一个白天节省的时间,那么将给出错误的测试结果。我不知道确切的原因。 我在rextester上使用了您的代码,试图找到解决方案。我发现最好的一个是相当麻烦的-它涉及到根据从
FromFileTimeUtc
得到的DateTime
实例创建一个新的DateTime
实例:
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
,则会得到准确的结果
参见方法文档中的备注部分:
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
referencetimeFromUtc.Kind是Utc,因此不在DST中。查看来源:佐哈尔的说法基本正确。关键点在于,
DateTime.ToFileTimeUtc
,与处理DateTime
的许多方法一样,依赖于与值关联的种类。当传递DateTimeKind.Unspecified
时,此特定方法假定输入已经是UTC。然而,在代码中,您正在创建这些值,就好像它们是根据给定的时区创建的一样
让我们把注意力集中在罪犯身上:
var reminderStartTimeToUtc = (ulong)reminderstarttime.ToFileTimeUtc();
var referenceTimeToUtc = (ulong)referencetime.ToFileTimeUtc();
由于提醒开始时间
和引用时间
都具有种类==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,因此这两个值仍处于标准时间
那么,我们如何在没有黑客攻击的情况下得到正确的答案呢?在将文件转换为Windows文件时间之前,只需确保输入值为DateTimeKind.Utc
。(DateTimeKind.Local
也可以,但这里不需要涉及本地时区)
代码的其余部分将按原样正确执行,您将获得预期的结果
请注意,这些方法的措辞有些混乱DateTime.ToFileTimeUtc
表示您正在转换为文件时间,并且使用.Kind==DateTimeKind输入DateTime
。未指定的将被视为是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
上没有Kind
,所以只有一种转换方法
最后一件事。请注意,DateTime.AddHours(10)
不接受DST间隔。因此,当您谈论PDT上午8点时,由于弹簧前进间隙,实际只经过了9个小时。10个实际运行小时为太平洋夏令时上午9点。如果在添加10个小时之前,将值保留为DateTimeOffset
类型,则可以很容易地更正此问题。您误解了这个问题。OP错误地认为referencetime
和referencetimefromtc
是同一时间,但正如我在回答中所示,它们不是。根据,这是错误的。如果datetime.Kind是utc,则在返回答案之前,IsDaylightSavingTime
将日期时间转换为本地时间。Yu Xie,您指的是时区文档。我在这里使用TimeZoneInfo。文档:但这里的结果是否错误:AddHours(10)给出的DateTime为2018年11月3日上午8:00:00,这是太平洋标准时间的DST时间?这是一个问题。我正在查。但是我想,你在回答中说的是正确的。根据实现注释:FromFileTimeUtc将DateTimekind作为UTC返回datetime对象。因此,根据IsDayLightSavingTime实施,将首先将其转换为PST,然后检查DST。2018年11月3日上午8:00:00转换为UTC时,将是2018年10月3日上午1:00:00,这不确定为什么IsDayLightSavingTime的实施取决于种类属性。默认情况下,它应将给定时间视为所请求时区中的时间。不是吗?我已经编辑了我的答案。然而,正如我所写的,也许som
// 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();