Why is it possible to pass a LocalDateTime
object into the ZonedDateTime.from()
method, if it's not possible to parse a simple DateTime
, as follows?
@Test
public void test() throws Exception {
GregorianCalendar.from(ZonedDateTime.from(LocalDateTime.parse("2016-08-31T23:00:59")));
}
Result:
java.time.DateTimeException: Unable to obtain ZonedDateTime from TemporalAccessor: 2016-08-31T23:00:59 of type java.time.LocalDateTime
at java.time.ZonedDateTime.from(ZonedDateTime.java:565)
The LocalDateTime has no time zone; to convert the LocalDateTime to ZonedDateTime , we can use . atZone(ZoneId. systemDefault()) to create a ZonedDateTime containing the system default time zone and convert it to another time zone using a predefined zone id or offset.
In Java, with a given LocalDateTime object we can use the LocalDateTime. toEpochSecond(ZoneOffset offset) method to convert it to an epoch seconds value which is the number of seconds from the epoch of 1970-01-01T00:00:00Z as the example Java code below. The output as below.
In order to create a LocalDateTime object from a string, you can use the static LocalDateTime. parse() method. It takes a String and a DateTimeFormatter as a parameter. The DateTimeFormatter argument is used to specify the date/time pattern.
Why is it possible to pass a LocalDateTime object into the ZonedDateTime.from() method if it's not possible to parse a simple datetime?
Both LocalDateTime
and ZonedDateTime
implement the interface called Temporal
, which extends TemporalAccessor
.
LocalDateTime
is datetime without a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30
ZonedDateTime
is datetime with a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30+01:00 Europe/Paris.
Now, if you see the implementation of from(TemporalAccessor temporal)
method that is expecting the TemporalAccessor… Both LocalDateTime
and ZonedDateTime
implements the method of super interface TemporalAccessor
(Temporal extends TemporalAccessor
), which is normal (i.e. has a polymorphic behavior) to allow us to pass both the local as well as zoned datetime.
Code:
public static ZonedDateTime from(final TemporalAccessor temporal) {
if (temporal instanceof ZonedDateTime) {
return (ZonedDateTime) temporal;
}
try {
final ZoneId zone = ZoneId.from(temporal);
if (temporal.isSupported(INSTANT_SECONDS)) {
long epochSecond = temporal.getLong(INSTANT_SECONDS);
int nanoOfSecond = temporal.get(NANO_OF_SECOND);
return create(epochSecond, nanoOfSecond, zone);
} else {
LocalDate date = LocalDate.from(temporal);
LocalTime time = LocalTime.from(temporal);
return of(date, time, zone);
}
} catch (final DateTimeException exception) {
throw new DateTimeException(
"Unable to obtain ZonedDateTime from TemporalAccessor: " +
temporal + " of type " + temporal.getClass().getName(),
exception
);
}
}
Now, we come to your problem.
You are passing LocalDateTime.parse("2016-08-31T23:00:59")
to the from(TemporalAccessor temporal)
method. So, the temporal is not an instance of ZonedDateTime
, it comes ZoneId zone = ZoneId.from(temporal);
.
So, your are getting the error since the LocalDateTime
does not contain the
timezone.
How to solve the issue?
Use the Zone ID with the ZonedDateTime
:
LocalDateTime ldt = LocalDateTime.parse("2016-08-31T23:00:59");
ZoneId zoneId = ZoneId.of("Europe/Paris");
ZonedDateTime zdt = ldt.atZone(zoneId);
GregorianCalendar gc = GregorianCalendar.from(zdt);
with JUnit test
@Test
public void test() throws Exception {
ZoneId zoneId = ZoneId.of("Europe/Paris");
GregorianCalendar.from(LocalDateTime.parse("2016-08-31T23:00:59").atZone(zoneId));
}
You will have to provide the ZoneId
as well. I will have to look up on the behavior as to why this is. I will edit and post as soon as I see it.
LocalDateTime ldt = LocalDateTime.parse("2016-08-31T23:00:59");
GregorianCalendar gc = GregorianCalendar.from(ZonedDateTime.of(ldt, ZoneId.systemDefault()));
System.out.println(gc);
This results:
java.util.GregorianCalendar[time=1472664659000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Calcutta",offset=19800000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2016,MONTH=7,WEEK_OF_YEAR=35,WEEK_OF_MONTH=5,DAY_OF_MONTH=31,DAY_OF_YEAR=244,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=0,SECOND=59,MILLISECOND=0,ZONE_OFFSET=19800000,DST_OFFSET=0]
Edits: Just came across this interesting thread...
The Java 8 java.time.* package is a very strict package. It doesn't allow flexibility between types and inputs - if you want a ZonedDateTime object, you must construct it from an input that has a time zone, a date & a time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With