使用 NodaTime 解析包含 BST 和 GMT 的日期和时间字符串

Parsing date and time strings containing BST and GMT with NodaTime

NodaTime好久了第一次接

虽然围绕这个领域存在一些问题,但我只想将这个问题集中在使用 NodaTime 将字符串解析为一些日期时间对象上。因此,我正在考虑处理分别包含 GMT 和 BST 的字符串。

奇怪的是,关于 BST 的一些琐事 here 具有两种不同的含义。在我们的遗留 Java 系统中,我们在很多自定义代码中使用 BST 和 GMT。

所以,我整理了一些快速而肮脏的测试来让我开始。最后一个测试用例失败。

[Theory]
[InlineData("2021-03-28 00:00:00 GMT")]
[InlineData("2021-03-28 02:00:00 GMT")]
[InlineData("2021-03-28 02:00:00 BST")]
public void parsing_dates(string dateString)
{
    ZonedDateTimePattern pattern = ZonedDateTimePattern.CreateWithInvariantCulture("yyyy'-'MM'-'dd HH':'mm':'ss z", DateTimeZoneProviders.Tzdb);
    
    ParseResult<ZonedDateTime> parseResult = pattern.Parse(dateString);

    if (!parseResult.Success)
    {
        testOutputHelper.WriteLine(parseResult.Exception.ToString());
    }
    else
    {
        testOutputHelper.WriteLine(parseResult.Value.ToInstant().ToString());
    }
    
    parseResult.Success.Should().BeTrue();
}

“2021-03-28 02:00:00 BST”字符串的异常:

NodaTime.Text.UnparsableValueException: The specified time zone identifier is not recognized. Value being parsed: '2021-03-28 02:00:00 ^BST'. (^ indicates error position.)

我已经用一些疲倦的眼睛浏览了文档,但似乎找不到我要找的东西。
https://nodatime.org/3.0.x/userguide/text
https://nodatime.org/3.0.x/userguide/zoneddatetime-patterns

有没有办法将 GMT 和 BST 解析为 ZonedDateTime?

不,解析时不能使用缩写。它们是模棱两可的,而且通常在各个方面都很可怕。 (“GMT”在这里是一个特例 - IANA 确实 将其作为时区 ID 包括在内,但我仍然建议避免使用它。)如果您可以更好地获取数据那样,那就太好了。

否则,您需要在请求 Noda Time 解析数据之前调整数据。

如果您的数据 始终 在 Europe/London 时区,并且只是“BST”或“GMT”,那么您可以根据此调整您的输入(包括用于消除歧义的 UTC 偏移量)然后解析调整后的版本。

这是一个完整的例子。请注意,我必须调整您的测试数据,因为“2021-03-28 02:00:00 GMT”无效。下面的示例显示了三月过渡的前一分钟,以及三月过渡的瞬间......以及十月的消歧。

using NodaTime;
using NodaTime.Text;
using System;

class Program
{
    static void Main()
    {
        string[] inputs =
        {
            "2021-03-28 00:00:00 GMT",
            "2021-03-28 00:59:00 GMT",
            "2021-03-28 02:00:00 BST",
            "2021-10-31 01:59:00 BST",
            "2021-10-31 01:59:00 GMT"
        };
        foreach (var input in inputs)
        {
            TweakAndParse(input);
        }
    }

    private static readonly ZonedDateTimePattern pattern =
        ZonedDateTimePattern.CreateWithInvariantCulture(
            "uuuu-MM-dd HH:mm:ss z '('o<g>')'",
            DateTimeZoneProviders.Tzdb);

    static void TweakAndParse(string input)
    {
        string tweaked = input.EndsWith("GMT") ? input[..^3] + "Europe/London (+00)"
            : input.EndsWith("BST") ? input[..^3] + "Europe/London (+01)"
            : throw new ArgumentException("Unexpected input");

        ZonedDateTime zdt = pattern.Parse(tweaked).Value;
        Console.WriteLine($"{input} => {zdt.ToInstant()}");
    }
}

输出:

2021-03-28 00:00:00 GMT => 2021-03-28T00:00:00Z
2021-03-28 00:59:00 GMT => 2021-03-28T00:59:00Z
2021-03-28 02:00:00 BST => 2021-03-28T01:00:00Z
2021-10-31 01:59:00 BST => 2021-10-31T00:59:00Z
2021-10-31 01:59:00 GMT => 2021-10-31T01:59:00Z