1

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
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Thiagarajan Ramanathan
  • 1,035
  • 5
  • 24
  • 32
  • Have you tried java.time package? – BugsForBreakfast Jun 18 '19 at 22:30
  • 1
    Actually DateTimeFormatter and OffsetDateTime both are from java.time package. – Thiagarajan Ramanathan Jun 18 '19 at 22:58
  • 2
    Does your code give you what you want? If not, how does observed result differ? Please quote any error message or stack trace verbatim. Thank you. – Ole V.V. Jun 18 '19 at 23:26
  • 1
    The result of running your code depends entirely on the instant it's run, and where it runs. Please provide a [MCVE](https://stackoverflow.com/help/minimal-reproducible-example), replacing the use of `OffsetDateTime.now()` with a constant value so the question has meaning and value for other visitors (especially ones in the more distant future) – Bohemian Jun 19 '19 at 00:21

3 Answers3

2

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.

AntiqTech
  • 717
  • 1
  • 6
  • 10
1

Parsing

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

Days as 24-hour chunks

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

Days according to calendar

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 ) ;
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

The difference between an time zone and an offset

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 1
    final DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS 'UTC'") .withZone(ZoneOffset.UTC); The above code snipped (ZoneOffset instead of ZoneId) is working perfectly on Java 8. – Thiagarajan Ramanathan Jun 19 '19 at 14:33