【问题标题】:Convert DateTime to DateTimeOffset taking into account SummerTime考虑 SummerTime 将 DateTime 转换为 DateTimeOffset
【发布时间】:2022-02-01 16:31:36
【问题描述】:

考虑下面的代码。当 CEST 从夏季时间变为冬季时间时,我有一个日期列表。我需要将它们转换为 UTC。但是使用此代码会迷路,我无法理解如何修复它。

DateTime firstDate = new DateTime(2020, 10, 24, 21, 0, 0);
DateTime lastDate = new DateTime(2020, 10, 25, 3, 0, 0);
DateTime[] dates = Enumerable.Range(0, (lastDate - firstDate).Hours + 1)
    .Select(r => firstDate.AddHours(r))
    .ToArray();

TimeZoneInfo tzo = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
List<DateTimeOffset> offsets = new List<DateTimeOffset>();
foreach(var date in dates)
{
    var timeSpan = tzo.GetUtcOffset(date);
    var offset = new DateTimeOffset(date, timeSpan);
    offsets.Add(offset);
}
10/24/2020 09:00:00 PM +02:00 = 19:00Z
10/24/2020 10:00:00 PM +02:00 = 20:00Z
10/24/2020 11:00:00 PM +02:00 = 21:00Z
10/25/2020 12:00:00 AM +02:00 = 22:00Z
10/25/2020 01:00:00 AM +02:00 = 23:00Z
10/25/2020 02:00:00 AM +01:00 = 01:00Z - What happened to 00:00Z?
10/25/2020 03:00:00 AM +01:00 = 02:00Z

【问题讨论】:

  • 如果考虑到偏移量,实际上是22:00
  • “实际上是 22:00”是什么意思? 什么是22:00? (如果您的意思是“缺失的行”,那么不,不是 - 这将有效地减去偏移量两次......)
  • @JonSkeet 这是对我的(现已删除)评论的回复。这个问题最初是问00:00:00在哪里”,我评论指出它就在那里:'12:00:00 AM`。
  • @RichardDeeming:啊哈。感谢您的上下文。

标签: c# datetime timezone timezone-offset datetimeoffset


【解决方案1】:

您正在从我所说的“本地日期/时间”转换为 UTC 值。由于夏令时转换(和其他时区变化),一些本地日期/时间值被跳过,一些被重复。

在您所展示的情况下,凌晨 2 点(含)和凌晨 3 点(不含)之间的每个当地时间都发生了两次,因为在凌晨 3 点(第一次),时钟回到了凌晨 2 点 - 这行:

10/25/2020 02:00:00 AM +01:00 = 01:00Z

... 显示 2am 的 second 映射。但是在 2020-10-25T00:00:00Z,由于时钟倒退,当地时间 也是凌晨 2 点。换句话说,您的转换是模棱两可的。

TimeZoneInfo documentation 声明:

如果dateTime不明确,或者转换后的时间不明确,则此方法会将不明确的时间解释为标准时间。

在这里,“标准时间”是任何不明确时间的第二次出现(因为它是从夏令时到标准时间的过渡)。

从根本上说,如果您只有本地值,那么您的信息不完整。如果您想将不明确的值视为夏令时而不是标准时间(或将其标记为不明确),您始终可以使用 TimeZoneInfo.IsAmbiguousTime(DateTime) 来检测。

【讨论】:

  • 谢谢!没有意识到我实际上缺少数据
【解决方案2】:

问题是 2020 年 10 月 25 日有两个凌晨 2 点 - 02:00 AM +02:0002:00 AM +01:00

由于源 DateTime 没有可用的偏移量,GetUtcOffset 方法无法确定值所指的 2AM,因此它默认使用非 DST 偏移量 - 02:00 AM +01:00

根据您实际尝试执行的操作,您可能可以通过将开始时间转换为 UTC、生成 UTC DateTimeOffset 值列表然后转换它们来解决此问题返回所需的时区:

DateTime firstDate = new DateTime(2020, 10, 24, 21, 0, 0);
TimeZoneInfo tzo = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
DateTimeOffset firstDateUtc = TimeZoneInfo.ConvertTime(firstDate, tzo, TimeZoneInfo.Utc);
DateTimeOffset[] utcDates = Enumerable.Range(0, 7).Select(r => firstDateUtc.AddHours(r)).ToArray();
DateTimeOffset[] offsets = Array.ConvertAll(utcDates, d => TimeZoneInfo.ConvertTime(d, tzo));

输出:

24/10/2020 21:00:00 +02:00 
24/10/2020 22:00:00 +02:00 
24/10/2020 23:00:00 +02:00 
25/10/2020 00:00:00 +02:00 
25/10/2020 01:00:00 +02:00 
25/10/2020 02:00:00 +02:00 
25/10/2020 02:00:00 +01:00 

或者,您可能想查看NodaTime,它为处理日期和时间提供了一个更清晰的模型。

【讨论】:

    猜你喜欢
    • 2012-12-05
    • 2013-07-25
    • 2019-09-02
    • 2012-05-01
    • 2011-06-24
    • 2020-10-07
    • 1970-01-01
    • 2015-03-13
    • 2013-07-21
    相关资源
    最近更新 更多