使用DateTimeFormatter将字符串解析为ZonedDateTime

时间:2017-08-17 20:12:13

标签: java-8 timezone java-time datetime-parsing zoneddatetime

我尝试将此String解析为ZonedDateTime

"Mon 14 Aug 2017 02:00 AM CEST"

这是我最后的尝试:

System.out.println("Test ZonedDateTime: " + ZonedDateTime.parse(
            "Mon 14 Aug 2017 02:00 AM CEST",
            DateTimeFormatter.ofPattern("EEEE dd M yyyy KK:mm a z")));

回复:

Exception in thread "main" java.time.format.DateTimeParseException: Text 'Mon 14 Aug 2017 02:00 AM CEST' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.ZonedDateTime.parse(ZonedDateTime.java:597)
at be.hypertux.test.localtime.Main.main(Main.java:17)

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

一个问题是,CESTCET这样的短时区名称是ambiguous and not standard。理想情况是使用IANA timezones names(始终采用Continent/City格式,例如America/Sao_PauloEurope/Berlin

我假设CESTCentral Europe Summer Time,这是used by lots of different countries(这就是为什么它含糊不清:你不知道它是哪个国家或地区,因为它也是范围很广。)

虽然大多数缩写都未被识别(由于其含糊不清),但出于复古兼容性原因,会假设某些“默认值”。在我使用的版本( JDK 1.8.0_131 )中,它默认为Europe/Paris,但不确定这是否是您需要的。并不能保证适用于所有缩写。在这种情况下,您可以定义要使用的首选时区(这将是一个任意选择,但由于CEST不明确,因此没有其他方法。)

另一个问题是星期几是英文(AugMon),而您没有指定一个java.util.Locale。在这种情况下,DateTimeFormatter采用系统的默认语言环境(它可能不是英语 - 检查Locale.getDefault()的值)。无论如何,即使在运行时也可以在不事先通知的情况下更改默认语言环境,因此最好在处理本地化数据时指定一个(例如一周中的某一天名)。

因此,您必须指定一个区域设置并将任意时区定义为在找到CEST这样的模糊名称时使用的首选时区。为此,您可以使用java.time.format.DateTimeFormatterBuilder,一组首选时区和java.time.format.TextStyle

// create set of preferred timezones
Set<ZoneId> zones = new HashSet<>();
// my arbitrary choice for CEST
zones.add(ZoneId.of("Europe/Brussels"));
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // date and time
    .appendPattern("EEE dd MMM yyyy hh:mm a ")
    // timezone short name with custom set of preferred zones
    .appendZoneText(TextStyle.SHORT, zones)
    // create formatter (use English locale for month and day of week)
    .toFormatter(Locale.ENGLISH);

String input = "Mon 14 Aug 2017 02:00 AM CEST";
System.out.println(ZonedDateTime.parse(input, formatter));

输出将是:

  

2017-08-14T02:00 + 02:00 [欧洲/布鲁塞尔]

请注意,我使用Europe/Brussels作为首选时区。您可以使用ZoneId.getAvailableZoneIds()检查所有可用的区域名称(并相应地选择)。

我正在使用hh作为小时,即hour-clock-of-am-pm field(值从1到12)。但是在您的代码中,您使用了KK,这是小时的上午字段(值为0到11)。检查哪一个最适合您的情况。

时区是一个区域在其历史记录中拥有,拥有和将拥有的所有不同offsets的集合,以及夏令时开始和结束的日期等。如果2个区域在这方面有一些差异历史,他们将有不同的时区(即使他们今天使用相同的规则)。

仅仅因为巴黎和布鲁塞尔今天使用相同的规则(CETCEST),这并不意味着它将永远像这样(因为时区规则是由政府和法律和不保证将来任何时候都不会更改它们。)

这就是为什么你必须定义一些特定的时区而不是依赖含糊不清的短名称(即使它们的使用很常见且很普遍)。

答案 1 :(得分:0)

为了使您的格式字符串有效,您的日期需要格式化为:Monday 14 8 2017 02:00 AM CEST

取出E并添加几个M,然后就可以了。