使用 DateTimeFormatter 将字符串解析为 ZonedDateTime

Parsing a string into a ZonedDateTime with a DateTimeFormatter

我正在尝试将此 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)

有什么想法吗?

为了让您的格式字符串起作用,您的日期需要像这样格式化:Monday 14 8 2017 02:00 AM CEST

取出一个 E 并添加几个 M 就可以了。

一个问题是 CESTCET 等短时区名称是 ambiguous and not standard. The ideal is to use IANA timezones names(始终采用 Continent/City 格式,例如 America/Sao_PauloEurope/Berlin).

我假设 CESTCentral Europe Summer Time, which is used by lots of different countries(这就是它模棱两可的原因:你无法知道它是哪个国家或地区,因为它的范围太广了)。

尽管无法识别大多数缩写(由于其含糊不清),但出于追溯兼容性原因,假定有一些 "defaults"。在我使用的版本 (JDK 1.8.0_131) 中,它默认为 Europe/Paris,但不确定这是否是您需要的。并且不能保证对所有缩写都有效。在这种情况下,您可以定义要使用的首选时区(这将是一个任意选择,但没有其他方法,因为 CEST 不明确)。

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

因此,您必须指定一个语言环境并定义一个任意时区作为首选时区,以便在发现像 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]

请注意,我使用 Europe/Brussels 作为首选时区。您可以使用 ZoneId.getAvailableZoneIds().

检查所有可用的区域名称(并相应地选择)

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


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

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

这就是为什么您必须定义一些特定的时区而不是依赖模棱两可的短名称(即使它们的使用很常见和广泛)。