JS 和 .NET 夏令时行为之间的区别
Difference between JS and .NET daylight saving behavior
我试图让 C# 和 JS 在 JS Date
和 .NET DateTime
对象上添加分钟时生成相同的结果。
我的系统使用的是希腊文化设置,但很可能你会在你的机器上得到类似的结果(如果你尝试使用这些值)。
我有这个 JS 代码使用 moment.js:
var dt1 = new Date(2020, 0, 19, 0, 0, 0);
var m1 = moment(dt1);
[m1.toString(), m1.add(100980, 'minutes').toString()]
> ["Sun Jan 19 2020 00:00:00 GMT+0200", "Sun Mar 29 2020 04:00:00 GMT+0300"]
..LinqPad 中的 C# 代码:
var ci = CultureInfo.CreateSpecificCulture("el-GR"); //CultureInfo.InvariantCulture; //CultureInfo.CurrentCulture;
var dt = new DateTime(2020, 1, 19, 0, 0, 0, 0, ci.Calendar, DateTimeKind.Local);
dt.ToString("G", ci).Dump();
var dt2 = dt.AddMinutes(100980);
dt2.ToString("G", ci).Dump();
> 19/1/2020 12:00:00 πμ
> 29/3/2020 3:00:00 πμ
...并且在使用不变文化打印时:
var ci = CultureInfo.CreateSpecificCulture("el-GR"); //CultureInfo.InvariantCulture; //CultureInfo.CurrentCulture;
var dt = new DateTime(2020, 1, 19, 0, 0, 0, 0, ci.Calendar, DateTimeKind.Local);
dt.ToString("G", CultureInfo.InvariantCulture).Dump();
var dt2 = dt.AddMinutes(100980);
dt2.ToString("G", CultureInfo.InvariantCulture).Dump();
> 01/19/2020 00:00:00
> 03/29/2020 03:00:00
谁能告诉我为什么我会得到不同的结果?
系统设置相同,我尝试同时使用 CurrentCulture
和 CreateSpecificCulture("el-GR")
,但与 JS 结果没有任何相似之处。
我什至在 JS 中创建了自己的 addMinutes()
,但我仍然遇到同样的问题。
似乎 JS 恰好在开始日期后的 100980 分钟进行了夏令时切换(如果您添加 100979 分钟就会看到),但 C# 没有!根据Wikipedia,JS行为是正确的行为。但到目前为止,我非常确定 .NET 也是正确的。根据我所做的快速测试,C# 似乎在 70.2 天前进行了切换。
有什么想法可以让我在向日期添加分钟时在两种语言之间获得相同的结果吗?
更新:
试过NodaTime,好像和JavaScript一致:
var dt0 = new DateTime(2020, 1, 19, 0, 0, 0, 0, ci.Calendar, DateTimeKind.Local).AcServerToUtc();
Duration duration = Duration.FromMinutes(100980);
Instant start = Instant.FromDateTimeUtc(dt0.AcMakeKindUtc());
Instant future = start + duration; // Or now.Plus(duration)
var zone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
future.InZone(zone).LocalDateTime.ToString("G", CultureInfo.InvariantCulture).Dump();
> 03/29/2020 04:00:00
所以,这似乎是一个可能的解决方案!
我最终使用了 NodaTime
,正如我在上面的问题更新中所写的那样。
它仍然不同意moment.js
所有的时间,但我觉得它更多时候同意它。
我想夏令时是一个巨大的混乱,没有人能很好地处理它。
问题是 AddMinutes
调用是在 DateTime
和 DateTimeKind.Local
上进行的。尽管被标记为这种类型,DateTime
对象上的 math 并未考虑时区。 即使是当地时间。
由于您使用的是本地类型,因此您可以简单地转换为 UTC,计算一下,然后再转换回来。
var dt2 = dt.ToUniversalTime().AddMinutes(100980).ToLocalTime();
您可能还想研究使用 DateTimeOffset
类型,并使用 TimeZoneInfo.ConvertTime
执行时区转换。
我试图让 C# 和 JS 在 JS Date
和 .NET DateTime
对象上添加分钟时生成相同的结果。
我的系统使用的是希腊文化设置,但很可能你会在你的机器上得到类似的结果(如果你尝试使用这些值)。
我有这个 JS 代码使用 moment.js:
var dt1 = new Date(2020, 0, 19, 0, 0, 0);
var m1 = moment(dt1);
[m1.toString(), m1.add(100980, 'minutes').toString()]
> ["Sun Jan 19 2020 00:00:00 GMT+0200", "Sun Mar 29 2020 04:00:00 GMT+0300"]
..LinqPad 中的 C# 代码:
var ci = CultureInfo.CreateSpecificCulture("el-GR"); //CultureInfo.InvariantCulture; //CultureInfo.CurrentCulture;
var dt = new DateTime(2020, 1, 19, 0, 0, 0, 0, ci.Calendar, DateTimeKind.Local);
dt.ToString("G", ci).Dump();
var dt2 = dt.AddMinutes(100980);
dt2.ToString("G", ci).Dump();
> 19/1/2020 12:00:00 πμ
> 29/3/2020 3:00:00 πμ
...并且在使用不变文化打印时:
var ci = CultureInfo.CreateSpecificCulture("el-GR"); //CultureInfo.InvariantCulture; //CultureInfo.CurrentCulture;
var dt = new DateTime(2020, 1, 19, 0, 0, 0, 0, ci.Calendar, DateTimeKind.Local);
dt.ToString("G", CultureInfo.InvariantCulture).Dump();
var dt2 = dt.AddMinutes(100980);
dt2.ToString("G", CultureInfo.InvariantCulture).Dump();
> 01/19/2020 00:00:00
> 03/29/2020 03:00:00
谁能告诉我为什么我会得到不同的结果?
系统设置相同,我尝试同时使用 CurrentCulture
和 CreateSpecificCulture("el-GR")
,但与 JS 结果没有任何相似之处。
我什至在 JS 中创建了自己的 addMinutes()
,但我仍然遇到同样的问题。
似乎 JS 恰好在开始日期后的 100980 分钟进行了夏令时切换(如果您添加 100979 分钟就会看到),但 C# 没有!根据Wikipedia,JS行为是正确的行为。但到目前为止,我非常确定 .NET 也是正确的。根据我所做的快速测试,C# 似乎在 70.2 天前进行了切换。
有什么想法可以让我在向日期添加分钟时在两种语言之间获得相同的结果吗?
更新:
试过NodaTime,好像和JavaScript一致:
var dt0 = new DateTime(2020, 1, 19, 0, 0, 0, 0, ci.Calendar, DateTimeKind.Local).AcServerToUtc();
Duration duration = Duration.FromMinutes(100980);
Instant start = Instant.FromDateTimeUtc(dt0.AcMakeKindUtc());
Instant future = start + duration; // Or now.Plus(duration)
var zone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
future.InZone(zone).LocalDateTime.ToString("G", CultureInfo.InvariantCulture).Dump();
> 03/29/2020 04:00:00
所以,这似乎是一个可能的解决方案!
我最终使用了 NodaTime
,正如我在上面的问题更新中所写的那样。
它仍然不同意moment.js
所有的时间,但我觉得它更多时候同意它。
我想夏令时是一个巨大的混乱,没有人能很好地处理它。
问题是 AddMinutes
调用是在 DateTime
和 DateTimeKind.Local
上进行的。尽管被标记为这种类型,DateTime
对象上的 math 并未考虑时区。 即使是当地时间。
由于您使用的是本地类型,因此您可以简单地转换为 UTC,计算一下,然后再转换回来。
var dt2 = dt.ToUniversalTime().AddMinutes(100980).ToLocalTime();
您可能还想研究使用 DateTimeOffset
类型,并使用 TimeZoneInfo.ConvertTime
执行时区转换。