用Noda Time解析模棱两可的日期时间

时间:2018-02-14 10:07:51

标签: c# .net nodatime

我使用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消息:

  

目标时区的本地日期/时间不明确

根据我的理解,问题是由于夏令时,这个特定时间可能会发生两次。在02:00,时钟返回1小时到01:00。 NodaTime不知道字符串引用的01:00的“版本”,并且因此引发了异常。

对我而言,解析导致的时间版本并不重要,我只想避免异常,并获得尽可能接近现实的日期。少了一个小时就可以了。最好的方法是什么?

我能想到的唯一方法是分割字符串并单独解析部分,然后再添加一个小时,但这感觉完全错误。有更好的解决方案吗?

2 个答案:

答案 0 :(得分:5)

ZonedDateTimePattern类具有Resolver属性。解析器的作用是执行映射到分区日期/时间并处理跳过和模糊的时间 - 无法映射的时间,因为它们将发生从不(跳过)或不止一次(模棱两可)由于夏令时。

ZonedDateTimePattern source code表示默认解析程序为Resolvers.StrictResolver。正如您已经发现的,如果映射不明确或跳过,则此解析程序会抛出异常。

有各种resolvers可供使用。最适合您的“请给我一个有效的日期和时间!”要求可能是LenientResolver,其行为如下:

  

通过返回较早的匹配来处理歧义,并跳过   时间间隔向前移动。

我们可以通过在WithResolver()实例上追加ZonedDateTimePattern来调用此解析器(Resolver属性没有公共设置器):

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)

答案 1 :(得分:3)

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

  

如果模式不包含偏移说明符(" o< ...&#;;"),则根据与模式关联的ZoneLocalMappingResolver解释文本表示的本地日期和时间。可以使用WithResolver方法从现有模式创建新模式,只需使用不同的解析程序。如果解析器抛出SkippedTimeException或AmbiguousTimeException,则会将这些转换为UnparsableValueException结果。请注意,由于时区转换的正常问题(通常用于夏令时),没有偏移说明符的模式在使用不是单个固定偏移的时区时将始终导致潜在的数据丢失。

提示

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);