I am having a hard time converting an ISO 8601 formatted String to a java.time.LocalDateTime which is in UTC.
More specific, I am trying to write an XMLAdapter for which you can enter the various ISO 8601 dataformats as a String (i.e. 2002-09-24
, 2011-03-22T13:30
, 2015-05-24T12:25:15Z
, 2015-07-28T11:11:15.321+05:30
) and which outputs a LocalDateTime in UTC and visa versa.
The system stores all it's Date and Time information internal in UTC times. When a user requests a Date or Time it is represented to the user based on their own ZoneId.
toISOString() method is used to convert the given date object's contents into a string in ISO format (ISO 8601) i.e, in the form of (YYYY-MM-DDTHH:mm:ss. sssZ or ±YYYYYY-MM-DDTHH:mm:ss. sssZ). The date object is created using date() constructor.
A ZonedDateTime represents a date-time with a time offset and/or a time zone in the ISO-8601 calendar system. On its own, ZonedDateTime only supports specifying time offsets such as UTC or UTC+02:00 , plus the SYSTEM time zone ID.
ZonedDateTime is an immutable representation of a date-time with a time-zone. This class stores all date and time fields, to a precision of nanoseconds, and a time-zone, with a zone offset used to handle ambiguous local date-times.
Instant.parse( "2015-05-24T12:25:15Z" )
.atZone(
ZoneId.of( "America/Montreal" )
)
.toString()
2015-05-24T08:25:15-04:00[America/Montreal]
ZonedDateTime
vs LocalDateTime
Your Question contradicts itself, with the title asking for a ZonedDateTime
and the body asking for a LocalDateTime
. Those are two very different beasts. One (ZonedDateTime
) is a specific moment on the timeline, the other (LocalDateTime
) is only a vague idea about possible moments but is not a specific moment.
For example, the LocalDateTime
of Christmas starting this year is 2017-12-25T00:00:00
but that has no meaning until you apply a time zone as Santa delivers first to the islands of Kiribati at their midnight (the first midnight on Earth), then on to New Zealand at their later midnight, then on to Australia at their later midnight, and so on moving westward toward successive midnights.
Also, you Question seems confused about exactly what is the input and what is the output.
a java.time.LocalDateTime which is in UTC.
This is a contradiction in terms. The LocalDateTime
class lacks any concept of time zone or offset-from-UTC. So this class cannot be used to represent a moment such as a UTC value. For a moment in UTC, use either Instant
or OffsetDateTime
classes.
Instant
For an input like 2015-05-24T12:25:15Z
, that represents a moment on the timeline in UTC (Z
is short for Zulu
and means UTC). To represent that, use the Instant
class. The Instant
class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.parse( "2015-05-24T12:25:15Z" );
Generally you should be working in UTC for most business logic, data storage, data exchange, and database. So for that Instant
is all you need.
ZonedDateTime
If you need to adjust into a time zone, such as for presentation to a user, apply a ZoneId
to get a ZonedDateTime
.
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
LocalDate
If you have a date-only value, parse as a LocalDate
object.
LocalDate localDate = LocalDate.parse( "2002-09-24" );
To get the first moment of the day for that date, specify a time zone. For any given moment, the date varies around the world by zone.
Also, do not assume the day starts at the time of 00:00:00
. Anomalies such as Daylight Saving Time (DST) may cause the first moment to be something like 01:00:00
. Let java.time determine the first moment’s time-of-day.
ZonedDateTime zdt = localDate.atStartOfDay( z );
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
Edit: Basils answer below should be marked correct. https://stackoverflow.com/a/43083698/348956
As the name suggests LocalDateTime
holds both Date and Time. The first example of a date string you have in your question for example only holds information about the date, therefore you cannot parse this directly into a LocalDateTime
. What you could do there is to first parse it into a LocalDate
and by setting the time on that object get a LocalDateTime
.
LocalDateTime localDateTime = LocalDate.parse("2002-09-24").atStartOfDay();
All Date and Time objects have a parse method like LocalDate
which can take a certain string format. These formats are different ISO standard formats specified in DateTimeFormatter
For formatting custom datetime strings into Temporal
objects use DateTimeFormatter and specify a custom pattern.
There are various formatters defined on DateTimeFormatter which do the job.
For example:
TemporalAccessor accessor= DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse("2020-07-14T21:56:00Z");
ZonedDateTime from = ZonedDateTime.from(accessor);
OR with a different offset format still in iso 8601 spec:
TemporalAccessor accessor= DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse("2020-07-14T21:56:00+00:00");
ZonedDateTime from = ZonedDateTime.from(accessor);
Note: This only works for the 'extended' iso 8601 formats. I didn't find support for the simplified one (without -'s for example 20200714T215600Z) but I don't think that's a big deal. I just wont support the simplified format in my API. If anyone does find a way to support the iso 8601 simplified date format (not just the extended) that is not a lot of work and is happy to leave a solution that would be great :)
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