Reputation: 1124
Input to my method will be a String
containing a date in UTC. I need to compare the input date with current date and time and check the difference between two dates. The result should be in days.
I tried the following with no success.
String dateString = "2019-06-18T16:23:41.575 UTC";
final DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS 'UTC'").withZone(ZoneId.of("UTC"));
OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1);
System.out.println("======================:"+parsedDate.format(formatter1));
OffsetDateTime currentUTC = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println("Until (with crono): " + parsedDate.until(currentUTC, ChronoUnit.DAYS));
I need the result in an int
(i.e., number of days).
The line OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1);
throws an exception with the following stack trace:
Exception in thread "main" java.time.format.DateTimeParseException: Text '2019-06-18T16:23:41.575 UTC' could not be parsed: Unable to obtain OffsetDateTime from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1959)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1894)
at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:402)
at thiagarajanramanathan.misc.App.main(App.java:86)
Caused by: java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:370)
at java.base/java.time.format.Parsed.query(Parsed.java:235)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1890)
... 3 more
Caused by: java.time.DateTimeException: Unable to obtain ZoneOffset from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.ZoneOffset.from(ZoneOffset.java:348)
at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:359)
... 5 more
Upvotes: 1
Views: 374
Reputation: 86379
You have got two good answers already. You are touching on an interesting and a bit tricky part of java.time, so I should like to make my contribution too. My key point is that a time zone and a UTC offset are not the same. To obtain an OffsetDateTime
you need an offset. You provide a time zone through the call .withZone(ZoneId.of("UTC"))
on the formatter, but it doesn’t help you. Yes, you and I know that UTC is the base of all offsets and therefore itself defines an offset of 0. But Java didn’t discover that from your code.
I admit I was surprised to discover that the following simple change was enough that your code runs on Java 9:
final DateTimeFormatter formatter1
= DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS 'UTC'")
.withZone(ZoneOffset.UTC);
However on Java 8 I still get the same exception as before. The output I got on Java 9.0.4 was:
======================:2019-06-18T16:23:41.575 UTC Until (with crono): 0
The only change is that I am now passing a ZoneOffset
rather than a ZoneId
object to withZone
(this is possible because ZoneOffset
is a subclass of ZoneId
).
A formatter that works on Java 8 too is one where we supply a default offset. For that we need a DateTimeFormatterBuilder
:
final DateTimeFormatter formatter1 = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendLiteral(" UTC")
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter();
Yet another and perhaps simpler option would be to parse into a LocalDateTime
first (which requires neither offset nor time zone) and then convert to OffsetDateTime
by calling .atOffset(ZoneOffset.UTC)
.
Upvotes: 1
Reputation: 340070
I would simplify the parsing if your input by getting it to comply with the ISO 8601 standard.
String input = "2019-06-18T16:23:41.575 UTC".replace( " UTC", "Z" ) ;
Instant instant = Instant.parse( input ) ;
If your definition of elapsed days is 24-hour chunks of time, use Duration
.
Duration d = Duration.between( instant , Instant.now() ;
long days = d.toDays() ;
If you want a count of days elapsed as seen on the calendar, meaning dates rather than 24-hour chunks of time, you must specify a time zone.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;
Extract the dates.
LocalDate start = zdt.toLocalDate() ;
LocalDate stop = now.toLocalDate() ;
long days = ChronoUnit.DAYS.between( start , stop ) ;
Upvotes: 1
Reputation: 717
As you can see from this thread: Unable to obtain OffsetDateTime from TemporalAccessor
I changed the following lines:
//OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1);
ZonedDateTime parsedDate = ZonedDateTime.parse(dateString, formatter1);
When your code is run with this modification, I could get the following results
for "2019-06-18T16:23:41.575 UTC" :
======================:2019-06-17T16:23:41.575 UTC
Until (with crono): 0
Since it's less than 24 hours, it returns 0
for "2019-06-17T16:23:41.575 UTC" :
======================:2019-06-17T16:23:41.575 UTC
Until (with crono): 1
Similarly, since it's over 24 hours but under 2 days, it returns 1.
I think this is what you want. Please try it and let me know if this works for you.
Upvotes: 2