为什么不能OffsetDateTime解析' 2016-08-24T18:38:05.507 + 0000'在Java 8中

时间:2016-08-24 22:14:23

标签: java date

表达式

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

导致以下错误:

  

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

另一方面,

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

按预期工作。

DateTimeFormatter's doc page提到没有冒号的区域偏移作为示例。我究竟做错了什么?我宁愿不破坏我的日期字符串来安抚Java。

5 个答案:

答案 0 :(得分:4)

您正在调用以下方法。

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

它使用DateTimeFormatter.ISO_OFFSET_DATE_TIME作为DateTimeFormatter,如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);

答案 1 :(得分:2)

Paypal 错误地将偏移量发送为 +0000,这实际上与他们的规范 https://developer.paypal.com/docs/api/transaction-search/v1/ 相矛盾,后者说它必须在 Internet date/time format 中,偏移量写为

 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,因此您也应该在将其发送到他们的 API 之前执行 .truncatedTo(SECONDS)

答案 2 :(得分:2)

更新

感谢 Ole V.V.建议使用这种更简单的模式:

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

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

原答案

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

演示:

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 是零时区偏移的 timezone designator。它代表祖鲁语并指定 Etc/UTC 时区(时区偏移为 +00:00 小时)。

Trail: Date Time 了解有关现代 Date-Time API 的更多信息。


检查 documentation page of DateTimeFormatter 以获取完整的模式字母列表。

答案 3 :(得分:1)

默认格式应为DateTimeFormatter.ISO_OFFSET_DATE_TIME,并使用以下区域偏移值进行定义:

static final OffsetIdPrinterParser INSTANCE_ID_Z = new OffsetIdPrinterParser("+HH:MM:ss", "Z");

答案 4 :(得分:1)

虽然DateTimeFormatter的模式语言不提供无法容纳非冒号形式的区域偏移的代码,但这并不意味着处理区域偏移的预定义实例接受无冒号形成。 one-arg版本的OffsetDateTime.parse()指定它使用DateTimeFormatter.ISO_OFFSET_DATE_TIME作为格式化程序,格式化程序的文档指定它支持三种格式,如the docs of ZoneOffset.getId()中所述。这些格式(从ISO-8601中提取)都不符合您的非冒号形式。

但不要担心:只需使用OffsetDateTime.parse()中的两个arg,提供适当的格式化程序。这有点不太方便,但非常可行。