DateTime - 奇怪的夏令时行为

时间:2012-03-01 23:39:38

标签: .net datetime dst

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

Sat-Mar-2012 15:59 UTC = Sun 01-Apr-2012 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-Apr-01 02:59:00.0000 +11:00
2012年4月1日03:00:00.0000 +10:00(当地)= 2012年4月1日02:00:00.0000 +10:00(当地)
15:17:16

将1分钟添加到原始日期时间会使本地时间为3AM,但也会将偏移设置为+10小时。 将1分钟添加到UTC日期并正确解析将本地时间设置为2 AM,偏移量为+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-Mar-31 15:59:00.0000 Z
2012年3月31日16:00:00.0000 Z(Utc)= 2012年3月31日16:00:00.0000 Z(Utc)
15: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-Apr-01 02:59:00.0000 +11:00
2012年4月1日03:00:00.0000 +10:00(当地)= 2012年4月1日02:00:00.0000 +10:00(当地)
15:17:16

任何人都能解释一下吗?

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

==要突出显示的其他方案

这只是简单的时间跨度添加,从夏令时结束前1分钟的非模糊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).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-Mar-31 15:59:00.0000 Z
Original in Local:2012-Apr-01 02:59:00.0000 +11:00
本地+ 1分钟:2012年4月1日 02 :00:00.0000 +10:00
UTC:1分钟:2012年3月31日 16 :00:00.0000 Z

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

原文:UTC:2012-Mar-31 15:59:00.0000 Z
Original in Local:2012-Apr-01 02:59:00.0000 +11:00
本地+ 1分钟:2012年4月1日 03 :00:00.0000 +10:00
UTC:1分钟:2012年3月31日 17 :00:00.0000 Z

2 个答案:

答案 0 :(得分:29)

我认为问题出在执行转换

您正在解析假设普遍时间,但随后隐式转换为“本地”类型 - 值为2:59:59。当您要求“本地”值添加一分钟时,它只是在本地值上添加一分钟,而不考虑时区。然后,当您打印偏移量时,系统正在尝试计算当地时间凌晨3点的偏移量... +10。

你有效地得到了:

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

是的,这有点痛苦 - DateTime is painful in general,这是我写Noda Time的主要原因,其中“区域中的日期/时间”与“本地日期”有不同的类型/ time“(或”本地日期“或”当地时间“),很明显你在任何一个点使用它。

我不清楚你在这里想要实现什么 - 如果你可以更具体,我可以告诉你在诺达时间你会做什么,虽然可能存在一些固有的含糊之处(来自当地日期的转换/ “划分”日期/时间的时间可以有0,1或2个结果。

编辑:如果目的只是为了记住时区以及瞬间,那么在Noda Time中您需要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);
    }
}

有关此问题的更多信息,请参阅calendar arithmetic上的说明。

答案 1 :(得分:0)

我处理这个问题的方法是将DateTime视为浮点数 - 当你操纵它们时,它们需要特殊处理,而当你向用户显示它们时。我用了一个我写的小库来包装它们:

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

始终将它们视为UTC + TimeZoneInfo。这样你就可以完成你通常所做的所有典型数学运算,只运行UTC到UTC,并且只在最后一步处理本地DateTimes,以一种不错的格式向用户显示它们。这种结构的另一个好处是你可以用他们习惯的格式更准确地向用户显示一个干净的时区,而不是每次都在TimeZoneInfo类中搜索。