使用不必要的时间和时区解析和格式化 LocalDate

Dar*_*noj 5 java datetime date datetime-format java-8

编辑 :

我打开了一个错误,并且已被 Oracle 确认。您可以在此处遵循解决方案:https://bugs.java.com/bugdatabase/view_bug.do ?bug_id=JDK-8216414


我正在与一个 LDAP 存储库交互,该存储库存储一个人的出生日期以及时间和时区,如下所示:

  • 如果出生日期为“27-12-2018”,则 LDAP 字符串为“20181227000000+0000”。

我找不到使用相同模式解析和格式化生日的方法。

以下代码适用于格式化,但不适用于解析:

LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMdd'000000+0000'";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);

// Outputs correctly 20181227000000+0000
date.format(birthdateFormat);

// Throw a DatetimeParseException at index 0
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
Run Code Online (Sandbox Code Playgroud)

以下代码适用于解析,但不适用于格式化

LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMddkkmmssxx";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);

// Throws a UnsupportedTemporalTypeException for ClockHourOfDay not supported
// Anyway I would have an unwanted string with non zero hour, minute, second, timezone
date.format(birthdateFormat);

// Parse correctly the date to 27-12-2018
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
Run Code Online (Sandbox Code Playgroud)

哪种模式可以同时满足解析和格式化?

我是否被迫使用两种不同的模式?

我这么问是因为该模式是在属性文件中配置的。我只想在此属性文件中配置 1 个模式。我想外部化该模式,因为 LDAP 不是我项目的一部分,它是共享资源,并且我不能保证格式不会更改。

Ole*_*.V. 1

我建议:

\n\n
    LocalDate date = LocalDate.of(2018, Month.DECEMBER, 27);\n    String pattern = "yyyyMMddHHmmssxx";\n    DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);\n\n    // Outputs 20181227000000+0000\n    String formatted = date.atStartOfDay(ZoneOffset.UTC).format(birthdateFormat);\n    System.out.println(formatted);\n\n    // Parses to 2018-12-27T00:00Z\n    OffsetDateTime odt = OffsetDateTime.parse("20181227000000+0000", birthdateFormat);\n    System.out.println(odt);\n    // Validate\n    if (! odt.toLocalTime().equals(LocalTime.MIN)) {\n        System.out.println("Unexpected time of day: " + odt);\n    }\n    if (! odt.getOffset().equals(ZoneOffset.UTC)) {\n        System.out.println("Unexpected time zone offset: " + odt);\n    }\n    // Converts to 2018-12-27\n    date = odt.toLocalDate();\n    System.out.println(date);\n
Run Code Online (Sandbox Code Playgroud)\n\n

LDAP 字符串表示日期、时间和 UTC 偏移量。好的解决方案是尊重这一点,并在格式化时生成所有这些(将一天中的时间设置为 00:00 并将偏移量设置为 0)并解析所有这些(最多也验证它们以捕获是否出现任何意外)。如果您知道如何进行LocalDate和之间的转换,则非常简单。OffsetDateTime

\n\n

编辑 3:允许配置模式

\n\n
\n

\xe2\x80\xa6 模式在属性文件中配置\xe2\x80\xa6 我只想在此属性文件中配置 1\n 模式。

\n\n

\xe2\x80\xa6 我不保证格式不能改变。

\n
\n\n

考虑到该模式可能有一天不包含一天中的时间和/或没有 UTC 偏移量,请在上面的代码中使用此格式化程序:

\n\n
    DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()\n            .appendPattern(pattern)\n            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)\n            .toFormatter()\n            .withZone(ZoneOffset.UTC);\n
Run Code Online (Sandbox Code Playgroud)\n\n

这定义了一天中的默认时间(午夜)和默认偏移量 (0)。只要在 LDAP 的字符串中定义了时间和偏移量,就不会使用默认值。

\n\n

如果您认为它变得太复杂,那么使用两种配置的格式,一种用于格式化,一种用于解析,可能是您最好的解决方案(最不烦人的解决方案)。

\n\n

编辑:避免类型转换

\n\n

我认为上面的解决方案很好。LocalDate但是,如果您坚持避免从ZonedDateTimeusingatStartOfDayOffsetDateTimeusing 的转换toLocalDate,可以通过以下技巧实现:

\n\n
    DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()\n            .appendValue(ChronoField.YEAR, 4, 4, SignStyle.NEVER)\n            .appendValue(ChronoField.MONTH_OF_YEAR, 2, 2, SignStyle.NEVER)\n            .appendValue(ChronoField.DAY_OF_MONTH, 2, 2, SignStyle.NEVER)\n            .appendLiteral("000000+0000")\n            .toFormatter();\n\n    // Outputs 20181227000000+0000\n    String formatted = date.format(birthdateFormat);\n    System.out.println(formatted);\n\n    // Parses into 2018-12-27\n    date = LocalDate.parse("20181227000000+0000", birthdateFormat);\n    System.out.println(date);\n
Run Code Online (Sandbox Code Playgroud)\n\n

我指定每个字段的确切宽度,以便格式化程序可以知道在解析时在字符串中的何处分隔它们。

\n\n

编辑2:这是解析中的错误吗?

\n\n

我立即期望yyyyMMdd\'000000+0000\'能够同时进行格式化和解析。您可以尝试向 Oracle 提交错误并查看他们的说法,但我不会\xe2\x80\x99 太乐观。

\n