表达式
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。
答案 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);
如果单位(例如月、日、小时等)可以是一位数或两位数,原始答案仍然有用。如果单位是个位数,这种替代模式将失败。
解决方案是使用带有可选模式的 DateTimeFormatter
。 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
是零时区偏移的 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,提供适当的格式化程序。这有点不太方便,但非常可行。