Reputation: 6873
Following code prints output with Date in String with timeZone offset.
String BERLIN_TIME_ZONE = "Europe/Berlin";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ssZ");
String dateTimeInString = formatter
.format(ZonedDateTime.of(LocalDateTime.now(), ZoneId.of(BERLIN_TIME_ZONE)));
System.out.println(dateTimeInString); // 2021-06-07T02:12:08+0200
This works perfectly fine. Now, at this point when we have dateTimeInString
so I want to convert it back to LocalDateTime
object by using parse
method.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ssZ");
System.out.println(LocalDateTime.parse(dateTimeInString,formatter));
but this parsing is throwing an exception.
Exception in thread "main" java.time.format.DateTimeParseException: Text '2021-06-07T02:12:08+0200' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {SecondOfMinute=8, MinuteOfHour=12, HourOfAmPm=2, OffsetSeconds=7200, NanoOfSecond=0, MilliOfSecond=0, MicroOfSecond=0},ISO resolved to 2021-06-07 of type java.time.format.Parsed
at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:492)
at de.finhome.A.main(A.java:19)
Caused by: java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {SecondOfMinute=8, MinuteOfHour=12, HourOfAmPm=2, OffsetSeconds=7200, NanoOfSecond=0, MilliOfSecond=0, MicroOfSecond=0},ISO resolved to 2021-06-07 of type java.time.format.Parsed
at java.base/java.time.LocalDateTime.from(LocalDateTime.java:461)
at java.base/java.time.format.Parsed.query(Parsed.java:235)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
... 2 more
Caused by: java.time.DateTimeException: Unable to obtain LocalTime from TemporalAccessor: {SecondOfMinute=8, MinuteOfHour=12, HourOfAmPm=2, OffsetSeconds=7200, NanoOfSecond=0, MilliOfSecond=0, MicroOfSecond=0},ISO resolved to 2021-06-07 of type java.time.format.Parsed
at java.base/java.time.LocalTime.from(LocalTime.java:431)
at java.base/java.time.LocalDateTime.from(LocalDateTime.java:457)
... 4 more
I have check different resources but couldn't figure out the issue.
Upvotes: 1
Views: 1491
Reputation: 18568
This does not look like a situation where one should use a LocalDateTime
.
There are two alternatives: ZonedDateTime
as you already use and OffsetDateTime
, which you might want to use...
If you want to provide a zone, take the current moment in time in that zone and print the result without the explicit zone but with the offset of that zone, then I recommend to stick to a ZonedDateTime
for most of the time.
Do something like this:
public static void main(String[] args) {
// provide a zone
String BERLIN_TIME_ZONE = "Europe/Berlin";
// use ZonedDateTime.now() directly and pass the zone
ZonedDateTime nowBerlin = ZonedDateTime.now(ZoneId.of(BERLIN_TIME_ZONE));
// then create a String that actually keeps the offset and omits the zone
String dateTimeInString = nowBerlin.format(
DateTimeFormatter.ISO_OFFSET_DATE_TIME
);
// print the result
System.out.println(dateTimeInString);
// if you now want to convert back, use an OffsetDateTime
OffsetDateTime nowPlus2h = OffsetDateTime.parse(dateTimeInString);
// and print the result
System.out.println(nowPlus2h.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
// if you want it to not just be a time of 2 hours offset, apply the zone again
ZonedDateTime nowAgainBerlin = nowPlus2h.atZoneSameInstant(
ZoneId.of(BERLIN_TIME_ZONE)
);
// check if that's the same as parsed in the beginning
if (nowAgainBerlin.equals(nowBerlin)) {
System.out.println("Same instant in Berlin");
} else {
System.out.println("Something went wrong in Berlin");
}
// and if you really want to get a LocalDateTime, you can get it like this
LocalDateTime localBerlin = nowBerlin.toLocalDateTime();
LocalDateTime localAgainBerlin = nowPlus2h.toLocalDateTime();
System.out.println(localBerlin + " == " + localAgainBerlin);
}
This just output
2021-06-07T15:16:47.259+02:00
2021-06-07T15:16:47.259+02:00
Same instant in Berlin
2021-06-07T15:16:47.259 == 2021-06-07T15:16:47.259
If you want to not have millis in your output, then define a formatter that suits your desires, but still, you won't be able to parse zones or offsets to a LocalDateTime
.
Upvotes: 1
Reputation: 44150
Change the hour token from hh
to HH
. Lowercase h
is 0-12, or what the docs call clock-hour-of-am-pm. Uppercase H
is 0-23, or what the docs call hour-of-day.
While the formatter can oblige quite happily when outputting the date, when you try to read in the same string it creates an ambiguity. The parser doesn't know whether ...T01:...
is supposed to be 1AM or PM. Since it cannot parse unambiguously, it fails.
Alternatively, add something into your format string for AM/PM (you can use a
for that). Then the parser will be able to unambiguously interpret an hour such as 01.
Seems you also want to change LocalDateTime.parse
to ZonedDateTime.parse
so you're not throwing away the zone.
Upvotes: 2