DateTime.ToLocalTime无法正常使用夏令时

时间:2017-11-07 14:42:58

标签: c# .net datetime timezone

我有一个DateTime个实例Kind = DateTimeKind.Utc和一个时间跨度。

var dt = DateTime.UtcNow;
var ts = TimeSpan.FromDays(1);

当我本地化dt然后添加ts时,我得到的结果与我添加ts后的结果不同,然后由于夏令时本地化。

var localizedFirst = dt.ToLocalTime() + ts; //Does account for daylight savings
var addedFirst = (dt + ts).ToLocalTime(); //Does not account for daylight savings
这似乎很奇怪。不应该从本地化中添加偏移量并从时间跨度中添加偏移量是可交换的和关联的吗?

我发现了一个类似的问题:Why doesn't DateTime.ToLocalTime() take into account daylight savings?这个问题更多地涉及将DateTime转换为String和从DateTime转换。我只使用TimeSpanDateTimeKind.Unspecified算术。

该问题的最佳答案建议使用DateTime,以便运行时假设未指定的日期为UTC,然后在本地化时将其正确转换。我很惊讶这实际上有效。如果我像这样创建一个新的var dt2 = new DateTime(dt.Ticks, DateTimeKind.Unspecified);

(dt2 + ts).ToLocalTime() 
dt2.ToLocalTime() + ts

然后,两个操作命令都会以夏令时返回正确的结果。

Utc

这对我来说似乎都很荒谬。为什么我需要将Unspecified日期转换为Local才能将其正确转换为dt?这似乎应该被认为是一个错误。

其他细节:

  • 框架:.NET 4.6.1
  • 我当地的时区:东部标准时间(美国)
  • 11/5/2017 2:36:13pm UTC使用的实际值:ts
  • TimeSpan.FromDays(699)使用的实际值:dt
  • 当地相当于11/5/2017 9:36:13am(dt + ts).ToLocalTime()
  • 10/5/2019 10:36:13am的价值:dt.ToLocalTime() + ts
  • 10/5/2019 9:36:13am的价值:{{1}}

2 个答案:

答案 0 :(得分:4)

此声明有效地要求提前一天,但所有其他属性(小时,分钟)保持不变:

var localizedFirst = dt.ToLocalTime() + ts;

虽然这个陈述询问在经过24小时(经过时间)之后当地时间是多少:

var addedFirst = (dt + ts).ToLocalTime();

将UTC中的所有内容保留到最后一分钟,然后转换为本地时间进行输出,这是一个很好的论据。

修改:或者相反,如果您不希望在添加或减少天数时更改本地小时数和分钟数,请在添加TimeSpan之前转换为本地时间。 然而,正如马特·约翰逊正确指出的那样,你可能会以这种方式结束本地时间,这个时间要么无效(时钟在那个时间前进),要么模棱两可(时钟会回来,所以那个时间发生了两次)。请参阅下面的评论,了解如何确定这一点。

答案 1 :(得分:4)

几点:

  • TimeSpan代表经过的持续时间。它的“天数”是标准天,正好是24小时。

  • 当天,在当地时区,由于DST回落过渡,有25个小时。

  • DateTime对象(+运算符或Add...函数)的添加始终不考虑时区。换句话说,无论原始.Kind属性是什么,输出都将具有相同的.Kind属性,但在加/减期间根本不考虑该类型。

  • 因此,在转换为本地时间后添加不会占25小时。这也是有问题的,因为它可能落在不存在或存在两次的本地时间值上。

因此,当您在代码注释中说“确实(或不支持)夏令时”时,从技术上讲,您已将其反转。由于UTC没有转换,localizedFirst变量是错误地假设本地日期为24小时的结果,而addedFirst变量是从本地时区正确应用DST规则的结果。在时间线上原始点之后经过24小时的点。

此外,设置DateTimeKind.Unspecified不会更改此情况的效果,因为DateTime.ToLocalTime()方法会将DateTimeKind.Unspecified视为DateTimeKind.UtcSee the table in the remarks of the documentation here.实际上,我试图复制您的结果,并且只是通过更改类型无法使dt2的值变为任何不同。如果可以的话,请详细说明这一点。

值得指出的是,消除这种混淆正是Noda Time库存在的原因。在Noda Time中,这些由两个非常不同的操作表示:

  • LocalDateTime + Period = LocalDateTime
  • Instant + Duration = Instant