为了账号安全,请及时绑定邮箱和手机立即绑定

IsDayLightSavingTime方法针对相同的时区和相同的时间返回不同的值

IsDayLightSavingTime方法针对相同的时区和相同的时间返回不同的值

C#
翻过高山走不出你 2021-04-27 13:25:58
以下代码检查DST中的特定时间,或者在正常的datetime和从filetime获得的值相同的时间内未返回不同的值: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);
查看完整描述

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类型不变,则可以轻松地对此进行更正。


查看完整回答
反对 回复 2021-05-16
?
万千封印

TA贡献1891条经验 获得超3个赞

这段代码工作正常。之所以给出此响应,是因为夏令时的开始是因为夏令时于2018年3月11日美国和加拿大在凌晨02:00开始。


查看完整回答
反对 回复 2021-05-16
?
拉风的咖菲猫

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


查看完整回答
反对 回复 2021-05-16
  • 3 回答
  • 0 关注
  • 150 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信