DateTime-奇怪的夏令时行为



我的本地时区是(UTC+10:00)堪培拉、墨尔本、悉尼

2012年3月31日星期六15:59 UTC=2012年4月1日星期日02:59+11:00
2012年3月31日星期六16:00 UTC=2012年4月1日星期日02:00+10:00

夏令时在4月的第一个星期日凌晨3点结束,时钟倒退1小时。

给定以下代码。。。。

DateTime dt1 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal);
DateTime dt2 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal).AddMinutes(1);
DateTime dt3 = DateTime.Parse("31-Mar-2012 16:00", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal);
Console.WriteLine("{0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1);
Console.WriteLine("{0:yyyy-MMM-dd HH:mm:ss.ffff K} ({1}) = {2:yyyy-MMM-dd HH:mm:ss.ffff K} ({3})", dt2, dt2.Kind, dt3, dt3.Kind);
Console.WriteLine("{0} : {1} : {2}", dt1.ToUniversalTime().Hour, dt2.ToUniversalTime().Hour, dt3.ToUniversalTime().Hour);

我得到以下输出

2012年4月1日02:59:00.0000+11:00
2012-Apr-01 03:00:00.0000+10:00(本地)=2012-Apr-1 02:00:00.000+10:00(当地)
15:17:16

在原始日期时间上增加1分钟会使本地时间为3AM,但也会将偏移量设置为+10小时。将UTC日期增加1分钟并进行正确解析,可将当地时间设置为上午2点,偏移量为+10 UTC。

使用重复

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc);
DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).AddMinutes(1);
DateTime dt3 = new DateTime(2012, 03, 31, 16, 0, 0, DateTimeKind.Utc);

DateTime dt1 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
DateTime dt2 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal).AddMinutes(1);
DateTime dt3 = DateTime.Parse("31-Mar-2012 16:00", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); 

给出

2012年3月31日15:59:00.0000 Z
2012年3月31日16:00:00.0000 Z15:16:16

如预期

使用再次重复

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime();
DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime().AddMinutes(1);
DateTime dt3 = new DateTime(2012, 03, 31, 16, 0, 0, DateTimeKind.Utc).ToLocalTime();

给出原始

2012年4月1日02:59:00.0000+11:00
2012-Apr-01 03:00:00.0000+10:00(本地)=2012-Apr-1 02:00:00.000+10:00(当地)
15:17:16

有人能解释一下吗?

如果我使用TimeZoneInfo从UTC转换为澳大利亚东部标准时间,我会得到正确的时间,但我会丢失DateTime实例中作为DateTime.Kind=DateTimeKind.Unspecified 的偏移信息

==突出显示的附加场景

这只是一个简单的时间跨度添加,从一个不明确的UTC日期开始,在夏令时结束前1分钟。

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc);  
DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime();  
Console.WriteLine("Original in UTC     : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1);  
Console.WriteLine("Original in Local   : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.ToLocalTime());  
Console.WriteLine("+ 1 Minute in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.AddMinutes(1).ToLocalTime());  
Console.WriteLine("+ 1 Minute in UTC   : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.AddMinutes(1));  
Console.WriteLine("=====================================================");
Console.WriteLine("Original in UTC     : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.ToUniversalTime());  
Console.WriteLine("Original in Local   : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2);  
Console.WriteLine("+ 1 Minute in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.AddMinutes(1));  
Console.WriteLine("+ 1 Minute in UTC   : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.AddMinutes(1).ToUniversalTime());  

给出

原始时间UTC:2012年3月31日15:59:00.00 Z
当地原件:2012-Apr-01 02:59:00.0000+11:00
+当地时间1分钟:2012-Apr-0102:00:00.0000+10:00
+UTC 1分钟:2012年3月31日16:00:00.0000 Z

=============================================

原始时间UTC:2012年3月31日15:59:00.00 Z
当地原件:2012-Apr-01 02:59:00.0000+11:00
+当地时间1分钟:2012-Apr-0103:00:00.0000+10:00
+UTC 1分钟:2012年3月31日17:00:00.0000 Z

我认为问题在于执行转换时的

您正在解析假定的通用时间,但随后隐式转换为"本地"类型,其值为2:59:59。当你要求"本地"值加一分钟时,它只是在本地值上加一分钟,而不考虑时区。当您打印偏移量时,系统正试图在当地时间凌晨3点计算出偏移量。。。即+10。

如此有效地实现了:

  • 解析步骤1:将字符串视为通用字符串(UTC 15:59)
  • 解析步骤2:将结果转换为本地(2:59本地)
  • 添加:在本地时间,不应用时区值(本地时间3:00)
  • 格式化步骤1:请求偏移量,因此计算出当地时间映射到什么(17:00UTC)
  • 格式化步骤2:将偏移量计算为本地和通用之间的差(+10)

是的,这一切都有点痛苦——DateTime总体上是痛苦的,这也是我写Noda Time的主要原因,在Noda Time中,"区域中的日期/时间"one_answers"本地日期/时间(或"本地日期"或"本地时间")有不同的类型,很明显你在任何时候都在使用哪个。

我不清楚你在这里到底想实现什么——如果你能更具体一点,我可以向你展示你在Noda Time会做什么,尽管可能存在一些固有的模糊性(从本地日期/时间到"分区"日期/时间的转换可能会有0、1或2个结果)。

编辑:如果目的只是记住时区和时刻,在野田时间,你会想要ZonedDateTime,就像这样:

using System;
using NodaTime;
class Program
{
    static void Main(string[] args)
    {
        var zone = DateTimeZone.ForId("Australia/Melbourne");
        ZonedDateTime start = Instant.FromUtc(2012, 3, 31, 15, 59, 0)
                                     .InZone(zone);
        ZonedDateTime end = start + Duration.FromMinutes(1);
        Console.WriteLine("{0} ({1})", start.LocalDateTime, start.Offset);
        Console.WriteLine("{0} ({1})", end.LocalDateTime, end.Offset);
    }
}

有关这方面的更多信息,请参阅日历算术的注释。

我处理这一问题的方法是将DateTime的处理方式有点像Floats——当你操纵它们时,与向用户显示它们时相比,它们需要特殊处理。我用我写的一个小图书馆来包装它们:

https://github.com/b9chris/TimeZoneInfoLib.Net

并始终将它们视为UTC+TimeZoneInfo。这样,你就可以做所有通常会做的数学运算,只在UTC到UTC之间操作,并且只在以某种漂亮的格式向用户显示本地DateTimes的最后一步处理它们。这种结构的另一个好处是,您可以更准确地以用户习惯的格式向用户显示干净的时区,而不是每次都在TimeZoneInfo类中抓取。

最新更新