Reputation: 9443
Given a DateTimeFormatter
defined as:
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();
I'd like to conditionally create a LocalDateTime
, ZonedDateTime
or OffsetDateTime
based on whether the parsing included a zone-id, an offset or neither.
So far I have:
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 );
}
);
What I have found though is that a zone-offset is never "recognized" - even if the text includes an offset, it is always reported as a zone-id. E.g. given "1999-12-31 12:59:59 +02:00"
I'd expect an OffsetDateTime
. However the "+02:00"
is always parsed as a zone-id. Ultimately it works given the reciprocity between zoned and offset. But as a matter of (probably way over the top) correctness I'd like to treat these as OffsetDateTime
.
Am I missing something to be able to make that distinction?
Thanks!
Upvotes: 2
Views: 283
Reputation: 86323
This is not really an answer. Jacob G. already provided that. I’d just like to supplement.
The ZoneId
that you get from either TemporalQueries.zone()
orTemporalQueries.zoneId()
is a ZoneOffset
when an offset has been parsed from the string. EDIT: UTC+02:00
doesn’t come out as a ZoneOffset
, but calling normalized()
on the ZoneId
converts it into one. Now the instanceof
operator will tell you which you have got.
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);
}
Output from this snippet is:
class java.time.LocalDateTime 1999-12-31T12:59:59 class java.time.OffsetDateTime 1999-12-31T12:59:59+02:00 class java.time.OffsetDateTime 1999-12-31T12:59:59+02:00 class java.time.ZonedDateTime 1999-12-31T12:59:59-04:00[America/Porto_Velho]
You were working too hard. Using DateTimeFormatter.parseBest()
ought to have done your job in Java 8 and does work in Java 9 and later.
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);
Output on Java 9:
class java.time.OffsetDateTime 1999-12-31T12:59:59+02:00
EDIT: This way doesn’t make an OFfsetDateTime
out of a string with UTC+02:00
as time zone, though.
Upvotes: 1
Reputation: 29710
It looks like this was actually a bug in Java 8. If you store the value of DateTimeFormatter#parse
in a Temporal
variable and print its getClass()
, you receive the following when compiling and running with Java 8:
class java.time.ZonedDateTime
However, when compiling and running with Java 11, the output is what you expect:
class java.time.OffsetDateTime
I'm going to search around for the specific bug report and edit this answer if I manage to find it. That way, we can determine the cause of the bug as well as the version of Java it was fixed in.
Upvotes: 2