【问题标题】:How to parse "2021-11-20+01:00" formatted date string to ZonedDateTime - error如何将“2021-11-20+01:00”格式的日期字符串解析为 ZonedDateTime - 错误
【发布时间】:2021-12-31 01:53:54
【问题描述】:

如何将“2021-11-20+01:00”格式化日期解析为ZonedDateTime?我正在尝试:

String value = "2021-11-20+01:00";
ZonedDateTime zDate = ZonedDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-ddxxx"));

但是得到这个奇怪的错误:

java.time.format.DateTimeParseException: 文本 '2021-11-20+01:00' 可能 无法解析:无法从 TemporalAccessor 获取 ZonedDateTime: {OffsetSeconds=3600},ISO解析为2021-11-20类型 java.time.format.Parsed

...不支持的字段:InstantSeconds...

有什么建议吗? European VIES VAT ID checking system 在接收 XML (SOAP) 响应时使用此时间格式:<requestDate>2021-11-20+01:00</requestDate>OffsetDateTime.. 出现同样的错误。

有趣的是,Javadoc 说“三个字母 (x) 输出小时和分钟,并带有冒号,例如 '+01:30'”。那么为什么上述模式不起作用?

也试过这个 - 同样的错误:

ZonedDateTime zDate = ZonedDateTime.parse(value, DateTimeFormatter.ISO_OFFSET_DATE);

完整的错误日志:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2021-11-20 +01:00' could not be parsed: Unable to obtain OffsetDateTime from TemporalAccessor: {OffsetSeconds=3600},ISO resolved to 2021-11-20 of type java.time.format.Parsed
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
    at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:402)
    at javaapplication5.JavaApplication5.checkVATatVIES(JavaApplication5.java:162)
    at javaapplication5.JavaApplication5.main(JavaApplication5.java:65)
Caused by: java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {OffsetSeconds=3600},ISO resolved to 2021-11-20 of type java.time.format.Parsed
    at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:370)
    at java.base/java.time.format.Parsed.query(Parsed.java:235)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    ... 3 more
Caused by: java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {OffsetSeconds=3600},ISO resolved to 2021-11-20 of type java.time.format.Parsed
    at java.base/java.time.Instant.from(Instant.java:378)
    at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:365)
    ... 5 more
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: InstantSeconds
    at java.base/java.time.format.Parsed.getLong(Parsed.java:203)
    at java.base/java.time.Instant.from(Instant.java:373)
    ... 6 more

使用 OpenJDK 11。

【问题讨论】:

  • 根据定义,LocalDateTime 是没有定义偏移量的日期和时间。您可能想使用 OffsetDateTime 而不是 LocalDateTime。
  • 那个字符串的时间是多少?
  • 没有使用时间,只是日期 2021-11-20,格林威治标准时间 +1 小时。
  • 更新的问题 - 问题仍然存在..
  • 我在这些投票后修改了问题,所以我希望接近投票将被撤回。

标签: java parsing localdatetime datetimeformatter


【解决方案1】:

tl;博士

org.threeten.extra.OffsetDate.parse( "2021-11-20+01:00" )

org.threeten.extra.OffsetDate

Java 中内置的 java.time 类不提供用于将日期与偏移量结合起来的类。所以你的解析尝试毫无意义。

ThreeTen-Extra 项目旨在补充 java.time 的功能。它包括满足您特定需求的 OffsetDate 类。将库添加到您的项目中。

String input = "2021-11-20+01:00" ;
OffsetDate od = OffsetDate.parse( input ) ;

从该OffsetDate 对象中,您可以获得LocalDate 部分或ZoneOffset 部分。您还可以确定其他日期时间值,例如添加时间以获取OffsetDateTime

你评论了:

如果我在 21 日上午 00:30 提出请求,我会收到包含 20 日日期的响应。因为我们的时间是 UTC+2,而他们的时间是 UTC+1

顺便说一句,提示:我建议养成使用完整的小时偏移量和填充零的习惯,也可以使用分钟。虽然各种标准都允许,但我看到多个库在缩写时会失败。所以我推荐“+02:00”而不是“UTC+2”。

这里有一些示例代码演示了您描述的场景。我们使用Africa/Brazzaville 的时区,因为它当时的偏移量是+01:00,我们使用Europe/Kaliningrad 作为它的偏移量,然后是+02:00。

LocalDate ld = LocalDate.of( 2021 , Month.NOVEMBER , 21 );
LocalTime lt = LocalTime.of( 0 , 30 );
ZoneId zKaliningrad = ZoneId.of( "Europe/Kaliningrad" );
ZonedDateTime zdtKaliningrad = ZonedDateTime.of( ld , lt , zKaliningrad );

ZoneId zBrazzaville = ZoneId.of( "Africa/Brazzaville" );
ZonedDateTime zdtBrazzaville = zdtKaliningrad.withZoneSameInstant( zBrazzaville );

OffsetDate od = OffsetDate.from( zdtBrazzaville );

OffsetDateTime odt = od.atTime( LocalTime.MIN );
ZonedDateTime asSeenInKaliningrad = odt.atZoneSameInstant( zKaliningrad );

运行时。

zdtKaliningrad = 2021-11-21T00:30+02:00[Europe/Kaliningrad]
zdtBrazzaville = 2021-11-20T23:30+01:00[Africa/Brazzaville]
od = 2021-11-20+01:00
odt = 2021-11-20T00:00+01:00
asSeenInKaliningrad = 2021-11-20T01:00+02:00[Europe/Kaliningrad]

就个人而言,我质疑日期偏移概念的实用价值,但这与您的问题无关。

在特定时区工作通常比仅使用偏移量更少问题。

我认为将日期与时区作为一个时刻(一天开始时的时间轴上的点)以及指定的时区而不是偏移量来跟踪更有意义。

String result = 
    LocalDate
        .of( 2021 , 11 , 20 )
        .atStartOfDay( ZoneId.of( "Africa/Brazzaville" ) )
        .toString() 
;

看到这个code run live at IdeOne.com

2021-11-20T00:00+01:00[非洲/布拉柴维尔]

对于代表一天的文本数据交换,我建议以扩展 ISO 8601 的格式传输此字符串 2021-11-20T00:00+01:00[Africa/Brazzaville],方法是在方括号中附加时区名称。

【讨论】:

  • 好处是微乎其微的,但例如,如果我在 21 日上午 00:30 提出请求,我会收到包含 20 日的响应......因为我们的时间是 UTC+2,而他们的时间是 UTC+1.. 1 小时的差异 - 但我认为这对 Java 造成了很大的问题.. :)
  • @ErnestasGruodis 我认为将日期与区域跟踪为一个时刻更有意义,即时间轴上一天开始时的点,以及指定的时区而不是偏移量.例如:LocalDate.of( 2021 , 11 , 20 ).atStartOfDay( ZoneId.of( "Africa/Brazzaville" ) )
【解决方案2】:

没有仅由日期和时区组成的 Java 类型。 ZonedDateTime 和 OffsetDateTime 正如它们的名字所暗示的那样包含日期和时间。因此,您需要创建一个格式化程序,假定一天中的默认时间:

String value = "2021-11-20+01:00";

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern("yyyy-MM-ddxxx");
builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
DateTimeFormatter formatter = builder.toFormatter();

ZonedDateTime zDate = ZonedDateTime.parse(value, formatter);

当然,一天中的小时可以是您选择的任何值:例如,12 表示中午。但它必须是有效的。

【讨论】:

  • 这是正确的方法。我建议不要编写格式模式,而是使用内置的DateTimeFormatter.ISO_OFFSET_DATEbuilder.append(DateTimeFormatter.ISO_OFFSET_DATE);
【解决方案3】:

您可以像这样将自己的模式指定为字符串:

String str = "2021-11-20+01:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd+HH:mm");
LocalDateTime date= LocalDateTime.parse(str, formatter);

【讨论】:

  • 是的,问题出在模式上。现在LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd+HH:mm")); 工作正常。
  • @ErnestasGruodis 是时间还是偏移量?
  • 这是 GMT 的偏移量 +1。嗯..
  • 更新的问题-也许这个答案也应该修改..谢谢。
猜你喜欢
  • 2022-01-03
  • 2020-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多