使用 Noda Time 解析不明确的日期时间

Parsing ambiguous datetime with Noda Time

我用的是Noda Time,代码如下:

var pattern = ZonedDateTimePattern.CreateWithInvariantCulture(
    "yyyy-MM-dd HH:mm:ss z", 
    DateTimeZoneProviders.Tzdb);

var parsed = pattern.Parse("2017-11-05 01:00:00 America/Los_Angeles");
Console.WriteLine(parsed.Value);

这导致 UnparsableValueException 消息:

The local date/time is ambiguous in the target time zone

据我了解,问题是由于夏令时,这个特定时间可能会出现两次。在 02:00,时钟拨回一小时 01:00。 NodaTime 不知道字符串指的是 01:00 中的哪个 "version",因此会抛出异常。

对我来说,解析结果是哪个版本的时间并不重要,我只是想避免异常,并获得尽可能接近实际的日期。少一小时或多一小时都可以。最好的方法是什么?

我能想到的唯一办法就是拆分字符串,逐个解析,然后加上一个小时,但感觉完全不对。有更好的解决方案吗?

每个 https://github.com/nodatime/nodatime/blob/2.2.x/src/NodaTime.Web/Markdown/2.0.x/zoneddatetime-patterns.md(或生成到的任何地方)

If the pattern does not contain an offset specifier ("o<...>") the local date and time represented by the text is interpreted according to the ZoneLocalMappingResolver associated with the pattern. A new pattern can be created from an existing one, just with a different resolver, using the WithResolver method. If the resolver throws a SkippedTimeException or AmbiguousTimeException, these are converted into UnparsableValueException results. Note that a pattern without an offset specifier will always lead to potential data loss when used with time zones which aren't a single fixed offset, due to the normal issues of time zone transitions (typically for daylight saving time).

建议

var lenientpattern = ZonedDateTimePattern
                    .CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss z", DateTimeZoneProviders.Tzdb)
                    .WithResolver(Resolvers.LenientResolver); //or any of the other resolvers
var parsed = lenientpattern.Parse("2017-11-05 01:00:00 America/Los_Angeles");
Console.WriteLine(parsed.Value);

ZonedDateTimePatternclass有个Resolver属性。解析器的作用是执行分区 date/times 的映射并处理跳过和不明确的时间 - 无法映射的时间,因为它们将发生 never(跳过)或 不止一次(不明确)由于夏令时。

ZonedDateTimePattern source code shows that the default resolver is Resolvers.StrictResolver。正如您已经发现的那样,如果映射不明确或被跳过,此解析器将抛出异常。

各种resolvers are available. The best match for your "just give me a valid date and time please!" requirement is likely to be the LenientResolver,其行为如下:

Ambiguity is handled by returning the earlier occurrence, and skipped times are shifted forward by the duration of the gap.

我们可以通过在 ZonedDateTimePattern 实例上附加对 WithResolver() 的调用来指定此解析器(Resolver 属性 没有 public setter):

var pattern = ZonedDateTimePattern.CreateWithInvariantCulture(
    "yyyy-MM-dd HH:mm:ss z",
    DateTimeZoneProviders.Tzdb).WithResolver(Resolvers.LenientResolver);

var parsed = pattern.Parse("2017-11-05 01:00:00 America/Los_Angeles");
Console.WriteLine(parsed.Value);

输出:

2017-11-05T01:00:00 America/Los_Angeles (-07)