为什么不减去两个本地日期时间值似乎占夏时制?

我在玩一些C#代码,试图了解如何在夏令时中减去C#中的DateTime对象。

根据Google和其他消息来源,2017年东部标准时区的夏令时“春季前进”活动是在3月12日凌晨2点。因此,当天的前几个小时是:

   12:00am - 1:00am
    1:00am - 2:00am
   (There was no 2:00am - 3:00am hour due to the "spring ahead")
    3:00am - 4:00am

所以,如果我要计算那个时间段上午1:00到4:00之间的时间差,我预计结果是2小时。

然而,我试图模拟这个问题的代码是返回一个3小时的TimeSpan。

码:

TimeZoneInfo easternStandardTime = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

DateTime oneAm = TimeZoneInfo.ConvertTime(new DateTime(2017, 03, 12, 01, 00, 00), easternStandardTime);
DateTime fourAm = TimeZoneInfo.ConvertTime(new DateTime(2017, 03, 12, 04, 00, 00), easternStandardTime);

TimeSpan difference = (fourAm - oneAm);

Console.WriteLine(oneAm);
Console.WriteLine(fourAm);
Console.WriteLine(TimeZoneInfo.Local.IsDaylightSavingTime(oneAm));
Console.WriteLine(TimeZoneInfo.Local.IsDaylightSavingTime(fourAm));
Console.WriteLine(difference);

在我的电脑上,这会产生:

2017-03-12 01:00:00.000 -5
2017-03-12 04:00:00.000 -4
False
True
03:00:00

所有这些输出都如预期的那样 - 除了3小时的最终值之外,正如我上面提到的那样,我希望是2个小时。

很显然,我的代码并不能正确地模拟我想到的情况。 什么是缺陷?


注意:

// These are just plain unspecified DateTimes
DateTime dtOneAm = new DateTime(2017, 03, 12, 01, 00, 00);
DateTime dtFourAm = new DateTime(2017, 03, 12, 04, 00, 00);

// The difference is not going to do anything other than 4-1=3
TimeSpan difference1 = dtFourAm - dtOneAm;

// ... but we have a time zone to consider!
TimeZoneInfo eastern = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

// Use that time zone to get DateTimeOffset values.
// The GetUtcOffset method has what we need.
DateTimeOffset dtoOneAmEastern = new DateTimeOffset(dtOneAm, eastern.GetUtcOffset(dtOneAm));
DateTimeOffset dtoFourAmEastern = new DateTimeOffset(dtFourAm, eastern.GetUtcOffset(dtFourAm));

// Subtracting these will take the offset into account!
// It essentially does this: [4-(-4)]-[1-(-5)] = 8-6 = 2
TimeSpan difference2 = dtoFourAmEastern - dtoOneAmEastern;

// Let's see the results
Console.WriteLine("dtOneAm: {0:o} (Kind: {1})", dtOneAm, dtOneAm.Kind);
Console.WriteLine("dtFourAm: {0:o} (Kind: {1})", dtFourAm, dtOneAm.Kind);
Console.WriteLine("difference1: {0}", difference1);

Console.WriteLine("dtoOneAmEastern: {0:o})", dtoOneAmEastern);
Console.WriteLine("dtoFourAmEastern: {0:o})", dtoFourAmEastern);
Console.WriteLine("difference2: {0}", difference2);

结果:

dtOneAm: 2017-03-12T01:00:00.0000000 (Kind: Unspecified)
dtFourAm: 2017-03-12T04:00:00.0000000 (Kind: Unspecified)
difference1: 03:00:00

dtoOneAmEastern: 2017-03-12T01:00:00.0000000-05:00)
dtoFourAmEastern: 2017-03-12T04:00:00.0000000-04:00)
difference2: 02:00:00

请注意, DateTime在其Kind属性中携带DateTimeKind ,默认情况下它是Unspecified的。 它不属于任何特定的时区。 DateTimeOffset没有类型,它有一个Offset ,它告诉你本地时间距UTC有多远。 这些都不会给你时区。 这就是TimeZoneInfo对象正在做的事情。 请参阅时区标记wiki中的“时区!=偏移量”。

我认为你可能感到沮丧的部分是,由于几个历史原因,即使你可能有DateTimeKind.LocalDateTime对象也不会理解数学时区。 它可能已经被实施以观察当地时区的过渡,但是并没有这样做。

你也可能会对Noda Time感兴趣,它以一种更明智和有目的的方式为.NET中的日期和时间提供了一个非常不同的API。

using NodaTime;

...

// Start with just the local values.
// They are local to *somewhere*, who knows where?  We didn't say.
LocalDateTime ldtOneAm = new LocalDateTime(2017, 3, 12, 1, 0, 0);
LocalDateTime ldtFourAm = new LocalDateTime(2017, 3, 12, 4, 0, 0);

// The following won't compile, because LocalDateTime does not reference
// a linear time scale!
// Duration difference = ldtFourAm - ldtOneAm;

// We can get the 3 hour period, but what does that really tell us?
Period period = Period.Between(ldtOneAm, ldtFourAm, PeriodUnits.Hours);

// But now lets introduce a time zone
DateTimeZone eastern = DateTimeZoneProviders.Tzdb["America/New_York"];

// And apply the zone to our local values.
// We'll choose to be lenient about DST gaps & overlaps.
ZonedDateTime zdtOneAmEastern = ldtOneAm.InZoneLeniently(eastern);
ZonedDateTime zdtFourAmEastern = ldtFourAm.InZoneLeniently(eastern);

// Now we can get the difference as an exact elapsed amount of time
Duration difference = zdtFourAmEastern - zdtOneAmEastern;


// Dump the output
Console.WriteLine("ldtOneAm: {0}", ldtOneAm);
Console.WriteLine("ldtFourAm: {0}", ldtFourAm);
Console.WriteLine("period: {0}", period);

Console.WriteLine("zdtOneAmEastern: {0}", zdtOneAmEastern);
Console.WriteLine("zdtFourAmEastern: {0}", zdtFourAmEastern);
Console.WriteLine("difference: {0}", difference);
ldtOneAm: 3/12/2017 1:00:00 AM
ldtFourAm: 3/12/2017 4:00:00 AM
period: PT3H

zdtOneAmEastern: 2017-03-12T01:00:00 America/New_York (-05)
zdtFourAmEastern: 2017-03-12T04:00:00 America/New_York (-04)
difference: 0:02:00:00

我们可以看到三个小时的时间,但这并不意味着与经过的时间相同。 这只意味着两个地方的价值观在时钟上的位置相距三个小时。 NodaTime理解这些概念之间的差异,而.Net的内置类型则不然。

一些后续阅读为你:

  • 无论如何,DateTime有什么问题?
  • DateTime更有趣
  • 针对DateTime.Now的情况
  • .NET开发人员的五种常见夏令时反模式
  • 哦,还有一件事。 你的代码有这个...

    DateTime oneAm = TimeZoneInfo.ConvertTime(new DateTime(2017, 03, 12, 01, 00, 00), easternStandardTime);
    

    由于您创建的DateTime是未指定的类型,因此您要求将计算机的本地时区转换为东部时间。 如果你碰巧不在东部时间,你的oneAm变量可能不会是凌晨1点!


    所以这是在MSDN文档中解决的。

    基本上,当从另一个日期减去一个日期时,你应该使用DateTimeOffset.Subtract()而不是像这里那样进行算术减法。

    TimeSpan difference = fourAm.Subtract(oneAm);
    

    产生预期的2小时时差。


    好的,我对代码做了一些小的修改。 不知道这是你想要达到或不是,但这会给你你想要的...

    static void Main() {
            TimeZoneInfo easternStandardTime = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
            TimeZone timeZone = TimeZone.CurrentTimeZone;
    
            DateTime oneAm = TimeZoneInfo.ConvertTime(new DateTime(2017, 03, 12, 01, 00, 00), easternStandardTime);
            DateTime fourAm = TimeZoneInfo.ConvertTime(new DateTime(2017, 03, 12, 04, 00, 00), easternStandardTime);
    
            DaylightTime time = timeZone.GetDaylightChanges(fourAm.Year);
    
            TimeSpan difference = ((fourAm - time.Delta) - oneAm);
    
            Console.WriteLine(oneAm);
            Console.WriteLine(fourAm);
            Console.WriteLine(TimeZoneInfo.Local.IsDaylightSavingTime(oneAm));
            Console.WriteLine(TimeZoneInfo.Local.IsDaylightSavingTime(fourAm));
            Console.WriteLine(difference);
            Console.ReadLine();
        }
    
    链接地址: http://www.djcxy.com/p/36703.html

    上一篇: Why doesn't subtracting two local DateTime values appear to account for Daylight Saving Time?

    下一篇: Date difference in Javascript (ignoring time of day)