6

表达方式

OffsetDateTime.parse("2016-08-24T18:38:05.507+0000")

导致以下错误:

java.time.format.DateTimeParseException:无法在索引 23 处解析文本“2016-08-24T18:38:05.507+0000”

另一方面,

OffsetDateTime.parse("2016-08-24T18:38:05.507+00:00")

按预期工作。

DateTimeFormatter 的文档页面提到了不带冒号的区域偏移作为示例。我究竟做错了什么?我宁愿不破坏我的日期字符串来安抚Java。

4

5 回答 5

7

您正在调用以下方法。

public static OffsetDateTime parse(CharSequence text) {
    return parse(text, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}

它使用 uses DateTimeFormatter.ISO_OFFSET_DATE_TIMEas DateTimeFormatterwhich,如javadoc中所述,执行以下操作:

ISO 日期时间格式化程序,用于格式化或解析带有偏移量的日期时间,例如“2011-12-03T10:15:30+01:00”。

如果你想解析一个不同格式的日期,2016-08-24T18:38:05.507+0000你应该使用OffsetDateTime#parse(CharSequence, DateTimeFormatter). 以下代码应该可以解决您的问题:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
OffsetDateTime.parse("2016-08-24T18:38:05.507+0000", formatter);
于 2016-08-24T22:47:17.420 回答
2

尽管DateTimeFormatter的模式语言没有为无法适应您的无冒号形式的区域偏移量提供代码,但这并不意味着处理区域偏移量的预定义实例接受无冒号形式。的单参数版本OffsetDateTime.parse()指定它DateTimeFormatter.ISO_OFFSET_DATE_TIME用作其格式化程序,并且该格式化程序的文档指定它支持三种格式,如ZoneOffset.getId() 的文档中所述。这些格式(取自 ISO-8601)都不符合您的无冒号格式。

但不用担心:只需使用来自的两个参数OffsetDateTime.parse(),提供适当的格式化程序。这有点不方便,但相当可行。

于 2016-08-24T22:31:41.410 回答
2

Paypal 错误地发送了偏移量,因为+0000这实际上与他们的规范相矛盾https://developer.paypal.com/docs/api/transaction-search/v1/说它必须是写入偏移量的Internet 日期/时间格式作为

 time-numoffset  = ("+" / "-") time-hour ":" time-minute

:是必需的。

为了解决这个问题,应该创建一个自定义的日期时间格式化程序,但避免使用简单的模式,yyyy-MM-dd'T'HH:mm:ssZ因为如果输出中突然出现毫秒,它将失败。

public static final DateTimeFormatter PAYPAL_DATE_TIME_FORMAT = new DateTimeFormatterBuilder()
    .parseCaseInsensitive()
    .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
    .parseLenient()
    .appendPattern("Z")
    .parseStrict()
    .toFormatter();

这是一种方法,但它有一个缺陷,即当 Paypal 更正其输出时,它将无法:正确解析偏移量。

此外,Paypal 不支持 nanos,因此您也应该.truncatedTo(SECONDS)在将其发送到他们的 API 之前这样做

于 2021-07-22T04:26:25.260 回答
2

更新

感谢 Ole VV 提出这种更简单的模式:

DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                        .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                        .appendPattern("[XXX][XX][X]")
                        .toFormatter(Locale.ENGLISH);

如果单位(例如月、日、小时等)可以是一位数或两位数,则原始答案仍然有用。如果单位为个位数,则此替代模式将失败。

原始答案

解决方案是使用DateTimeFormatter带有可选模式的 a。允许我们在DateTimeFormatter方括号中指定可选模式。

演示:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(
                "u-M-d'T'H:m:s[.[SSSSSSSSS][SSSSSSSS][SSSSSSS][SSSSSS][SSSSS][SSSS][SSS][SS][S]][XXX][XX][X]",
                Locale.ENGLISH);
        
        // Test
        Stream.of(
                "2021-07-22T20:10:15+0000",
                "2021-07-22T20:10:15+00:00",
                "2021-07-22T20:10:15+00",
                "2021-07-22T20:10:15.123456789+0000",
                "2021-07-22T20:10:15.12345678+0000",
                "2021-07-22T20:10:15.123+0000",
                "2021-07-22T20:10:15.1+0000"                
        ).forEach(s -> System.out.println(OffsetDateTime.parse(s, dtf)));
    }
}

输出:

2021-07-22T20:10:15Z
2021-07-22T20:10:15Z
2021-07-22T20:10:15Z
2021-07-22T20:10:15.123456789Z
2021-07-22T20:10:15.123456780Z
2021-07-22T20:10:15.123Z
2021-07-22T20:10:15.100Z

Z输出中的 是零时区偏移的时区指示符。它代表 Zulu 并指定Etc/UTC时区(时区偏移量为+00:00小时)。

从Trail: Date Time了解有关现代日期时间 API 的更多信息。


查看文档页面以DateTimeFormatter获取模式字母的完整列表。

于 2021-07-22T19:13:36.057 回答
1

预计默认格式将DateTimeFormatter.ISO_OFFSET_DATE_TIME使用以下区域偏移值定义:

static final OffsetIdPrinterParser INSTANCE_ID_Z = new OffsetIdPrinterParser("+HH:MM:ss", "Z");
于 2016-08-24T22:30:48.490 回答