到 UTC 的本地不明确 DateTime

Local Ambiguous DateTime to UTC

提问人:Ant 提问时间:10/10/2023 最后编辑:Matt Johnson-PintAnt 更新时间:10/11/2023 访问量:84

问:

基本上,我如何将我知道是 GMT 的日期字符串转换为 UTC,其中日期不明确,因为它位于夏令时切换范围内。

例如,“29/10/2023 01:30:00” - 在格林威治标准时间中,时钟在 02:00 返回,这可能是第一次或第二次 - 在 C# 中,如果你在这个对象上,它将始终返回 01:30。.ToUniversalTime()DateTime

有没有办法告诉它到 UTC 转换“嘿,这是 BST 时间”,下次告诉它这是非 BST 时间?

据我所知,我可以找到该对象,但它不允许我在使用它转换为 UTC 时指定非夏令时模式。TimeZoneInfo

对于上下文,这是针对调度应用程序的,例如每 15 分钟发生一次事件,数据来自外部 API,它以字符串形式为我提供日期时间,时区位于单独的属性中,例如“GMT BST”和“GMT”。

.NET 日期时间 时区

评论

2赞 Luuk 10/10/2023
夏令时 (DST) 的 UTC 和 GMT 都不会更改。但是,一些使用 GMT 的国家/地区在 DST 期间切换到不同的时区。
0赞 Zdeněk Jelínek 10/10/2023
一种解决方案是使用 TimeZoneInfo.IsAmbiguousTime,如果返回 true,则使用 TimeZoneInfo.GetAmbiguousTimeOffset 中的最大值。我觉得这有点骇人听闻,因为有些历史案例并不那么简单。一个合适的解决方案可能是浏览区域的调整规则,找到相关的规则,然后从那里开始。但即便如此,也可能是不确定的。也许使用NodaTime库可以成为您的解决方案?
0赞 Luuk 10/10/2023
这回答了你的问题吗?使用格式为 PST/CEST/UTC/等的时区解析 DateTime
0赞 JP Alioto 10/11/2023
为了跟进@Luuk的评论,UTC中没有模棱两可的时间。请参阅 TimeZoneInfo.IsAmbiguousTime 的文档
0赞 Matt Johnson-Pint 10/11/2023
@ZdeněkJelínek - 实际上,这完全是正确的方法。它正确地使用引擎盖下的调整规则,并以高性能的方式进行。有关示例,请参阅我的答案。GetAmbiguousTimeOffsets

答:

1赞 Matt Johnson-Pint 10/11/2023 #1

通过您使用“GMT”和“BST”,我将假设您给出的是英国的时间示例,英国使用 GMT (UTC+0) 表示标准时间,使用 BST (UTC+1) 表示夏令时。假设您可以从输入值中判断出您处于哪个阶段,那么在转换为 UTC 时,您确实可以使用该信息。

下面是一个适用于任何时区的扩展方法:

public static DateTime ToUniversalTime(this DateTime dt, TimeZoneInfo tz, bool isDst)
{
    // If we're not dealing with unspecified kind, then the normal ToUniversalTime method is used.
    // Thus, the tz and isDst parameters are ignored.
    if (dt.Kind != DateTimeKind.Unspecified)
    {
        return dt.ToUniversalTime();
    }

    // Handle invalid values (impossible values in the local time zone, due to advancing local time).
    if (tz.IsInvalidTime(dt))
    {
        throw new ArgumentException("Invalid local date and time for the specified time zone.");
    }

    // For ambiguous values, choose the offset for the indicated time zone and isDst flag.
    if (tz.IsAmbiguousTime(dt))
    {
        TimeSpan[] offsets = tz.GetAmbiguousTimeOffsets(dt);
        Array.Sort(offsets);
        TimeSpan offset = isDst ? offsets[1] : offsets[0];
        DateTimeOffset dto = new DateTimeOffset(dt, offset);
        return dto.UtcDateTime;
    }

    // Simple case
    return TimeZoneInfo.ConvertTimeToUtc(dt, tz);
}

并将其与您的示例一起使用:

// For the UK, use "Europe/London" (or "GMT Standard Time" on Windows before .NET 6)
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Europe/London");
DateTime london = new DateTime(2023, 10, 29, 1, 30, 0);

DateTime utcFromDst = london.ToUniversalTime(tz, true);
DateTime utcFromStd = london.ToUniversalTime(tz, false);

Console.WriteLine($"London: {london}");
Console.WriteLine();
Console.WriteLine($"UTC:    {utcFromDst} (assuming input was daylight time)");
Console.WriteLine($"UTC:    {utcFromStd} (assuming input was standard time)");

请参阅它在 .NET Fiddle 上的工作