Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Time API converting ISO 8601 to ZonedDateTime

Tags:

java-time

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.

like image 307
Martijn Burger Avatar asked Jun 10 '15 15:06

Martijn Burger


People also ask

How do I convert iso8601 to date?

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.

Is ZonedDateTime a UTC?

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.

What is ZonedDateTime in Java?

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.


3 Answers

tl;dr

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 );

About java.time

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?

  • Java SE 8, Java SE 9, Java SE 10, and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

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.

like image 165
Basil Bourque Avatar answered Sep 30 '22 12:09

Basil Bourque


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.

like image 37
softarn Avatar answered Sep 30 '22 13:09

softarn


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 :)

like image 29
John Deverall Avatar answered Sep 30 '22 11:09

John Deverall