Java 基于解析的TemporalAccessor有条件地创建LocalDateTime、ZonedDateTime或OffsetDateTime
给定的Java 基于解析的TemporalAccessor有条件地创建LocalDateTime、ZonedDateTime或OffsetDateTime,java,datetime,Java,Datetime,给定的DateTimeFormatter定义为: public static final DateTimeFormatter DATE_TIME = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append( ISO_LOCAL_DATE ) .optionalStart().appendLiteral( ' ' ).optionalEnd() .optionalS
DateTimeFormatter
定义为:
public static final DateTimeFormatter DATE_TIME = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append( ISO_LOCAL_DATE )
.optionalStart().appendLiteral( ' ' ).optionalEnd()
.optionalStart().appendLiteral( 'T' ).optionalEnd()
.append( ISO_LOCAL_TIME )
.optionalStart().appendLiteral( ' ' ).optionalEnd()
.optionalStart().appendZoneOrOffsetId().optionalEnd()
.toFormatter();
我希望根据解析是否包含区域id、偏移量或两者都不包含,有条件地创建LocalDateTime
、ZonedDateTime
或OffsetDateTime
到目前为止,我已经:
DATE_TIME.parse(
text,
(temporal) -> {
// see if there is an offset
final ZoneOffset offset = temporal.query( TemporalQueries.offset() );
if ( offset != null ) {
return OffsetDateTime.from( temporal );
}
// see if there is a tz
final ZoneId zoneId = temporal.query( TemporalQueries.zoneId() );
if ( zoneId != null ) {
return ZonedDateTime.from( temporal );
}
// otherwise its a LocalDateTime
return LocalDateTime.from( temporal );
}
);
但我发现,区域偏移永远不会被“识别”-即使文本包含偏移,它也总是作为区域id报告。例如,给定“1999-12-31 12:59:59+02:00”
,我希望出现OffsetDateTime
。但是,“+02:00”
始终解析为区域id。考虑到分区和偏移之间的相互作用,它最终会起作用。但是作为一个正确性问题(可能有点过头了),我想把它们看作是OffsetDateTime
我是否错过了一些东西来做出区分
谢谢 看起来这实际上是Java8中的一个bug。如果将
DateTimeFormatter#parse
的值存储在Temporal
变量中并打印其getClass()
,则在使用Java 8编译和运行时会收到以下信息:
class java.time.ZonedDateTime
但是,在使用Java 11编译和运行时,输出是您所期望的:
class java.time.OffsetDateTime
我将四处搜索特定的bug报告,并编辑这个答案,如果我设法找到它的话。这样,我们就可以确定错误的原因以及修复错误的Java版本。这并不是真正的答案。Jacob G.已经提供了这一点。我只想补充一下 Java8的变通方法 从
TemporalQueries.zone()
或TemporalQueries.ZoneId()
中获得的ZoneId
在从字符串解析偏移量时是一个ZoneOffset
。编辑:UTC+02:00
不会显示为ZoneOffset
,但在ZoneId
上调用normalized()
会将其转换为一个。现在,instanceof
操作符将告诉您得到了哪些
for (String text : new String[] {
"1999-12-31 12:59:59",
"1999-12-31 12:59:59 +02:00",
"1999-12-31 12:59:59 UTC+02:00",
"1999-12-31 12:59:59 America/Porto_Velho",
}) {
TemporalAccessor parsed = DATE_TIME.parse(
text ,
(temporal) -> {
// see if there is an offset or tz
final ZoneId zoneOrOffset = temporal.query( TemporalQueries.zone() );
if ( zoneOrOffset != null ) {
ZonedDateTime zdt = ZonedDateTime.from( temporal );
// EDIT: call normalized() to convert a ZoneId
// with constant offset, e.g., UTC+02:00, to ZoneOffset
if (zoneOrOffset.normalized() instanceof ZoneOffset) {
return zdt.toOffsetDateTime();
} else {
return zdt;
}
}
// otherwise it's a LocalDateTime
return LocalDateTime.from( temporal );
}
);
System.out.format(Locale.ENGLISH, "%-30s %s%n", parsed.getClass(), parsed);
}
此代码段的输出为:
适用于Java9及更高版本的更简单方法
你工作太努力了。使用DateTimeFormatter.parseBest()
应该在Java8中完成您的工作,并且在Java9和更高版本中也可以工作
String text = "1999-12-31 12:59:59 +02:00";
TemporalAccessor parsed = DATE_TIME.parseBest(text,
OffsetDateTime::from, ZonedDateTime::from, LocalDateTime::from);
System.out.println(parsed.getClass());
System.out.println(parsed);
Java 9上的输出:
编辑:这种方法不会将
UTC+02:00
作为时区的字符串制作成OFfsetDateTime
。我无法复制。当我试图用你的代码解析1999-12-31 12:59:59+02:00
时,我确实得到了一个OffsetDateTime
。我使用的是Java 9.0.4。出于好奇,您将结果分配给什么类型<代码>时态?@erickson时态
将是一个不错的选择。它比TemporalAccessor
(我在“答案”中使用了它,它满足了我在那里的需要,即使Object
也可以工作)。你的答案让我走上了“正确的道路”。我几乎可以使用Java8实现它,尽管出于某种原因JDK决定将ZoneRegion
非公共化。在某些情况下(例如UTC+02:00),分区将作为分区返回。在其他情况下,当ZoneOffset
用于ZoneId
时,我可以使用instanceof实现此功能。但是,由于ZoneRegion
是非公开的,我无法做到这一点(没有糟糕的反思,ofc)@SteveEbersole我明白了。升级到Java9+不是一个选项吗?这是在Hibernate内部使用的(我正在重新处理查询中的时态文字支持)。不幸的是,我们仍然使用Java8作为基线,所以我需要支持它。更糟糕的是,我需要在8和9+上支持它:(我也没有在中找到它。我确信它一定在某个地方。#parseBest
工作得很好,尽管我确实看到了与您在Java 8中所说的略有不同的行为,在Java 8中,它从不返回OffsetDateTime
。我总是得到LocalDateTime
或zoneDateTime
。例如e value“1999-12-31 12:59:59+02:00”
我得到了一个ZoneDateTime
,其区域id(ZoneDateTime#getZone
)为“+02:00”
。一般来说,这对我来说很好-我通常对ZoneDateTime感兴趣,因为它更“持久性就绪”格式,尽管我有兴趣了解其后果。再次感谢Ole!这似乎与我的观察结果一致。我特意为Java 9和以后版本采用了更简单的方式。再次感谢您的报告。
String text = "1999-12-31 12:59:59 +02:00";
TemporalAccessor parsed = DATE_TIME.parseBest(text,
OffsetDateTime::from, ZonedDateTime::from, LocalDateTime::from);
System.out.println(parsed.getClass());
System.out.println(parsed);
class java.time.OffsetDateTime
1999-12-31T12:59:59+02:00