Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Result when compare two ZonedDateTime is not as expected

I have this code :

ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T10:01:00.000+03:00");
ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
System.out.println(t1.compareTo(t2));

The result is -1. I guess it will convert both t1 and t2 to the same timezone then compare it. E.g in the first case it will (maybe) compare

t1 = 2018-04-06T07:01:00.000+00:00 to t2 = 2018-04-06T13:01:00.000+00:00

This case return 1 as I expected

ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T17:01:00.000+03:00");
ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
System.out.println(t1.compareTo(t2));

But this one (*) doesn't return 0, it return 1 :

ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T16:01:00.000+03:00");
ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
System.out.println(t1.compareTo(t2));

I even try to use truncatedTo(ChronoUnit.MINUTES) after parse the string to ignore the seconds part but nothing change.

The most interested thing is if I decrease / increase t1 / t2 by one second, the result will be -1

e.g :

ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T16:01:00.000+03:00");
ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:01.000-03:00");
System.out.println(t1.compareTo(t2));

In my use case, all user input time will be saved in the above format in many ZoneOffset and I have some query where I need to compare it with server time (ignore the seconds of the time). How can I get t1 equals t2 in case (*) above?

like image 754
Hai Hoang Avatar asked Apr 06 '18 05:04

Hai Hoang


People also ask

How to compare two ZonedDateTime in Java?

ZonedDateTime equals() method in Java with ExamplesThe equals() method of ZonedDateTime class in Java is used to compare this ZonedDateTime to the another date-time object passed as parameter. The comparison is based on the offset date-time and the zone.

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.


2 Answers

The comparison is based first on the instant, then on the local date-time, then on the zone ID, then on the chronology. It is "consistent with equals", as defined by Comparable.

compareTo is implemented in the superclass ChronoZonedDateTime, a class that we usually don’t care about, but where we need to look for the documentation of some of the methods. So the above quote comes from there.

So in the case where both ZonedDateTime objects denote the same instant, t1’s local time is 16:01, which is greater than the t2’s 10:01. So 1 is the correct and expected result. On the other hand, if the instants are just one second apart — or just 1 nanosecond — that takes precedence, and the local times are not compared.

I believe this explains the behaviour you observed.

The quote furthermore gives a hint as to why this must be so: Suppose that comparing two ZonedDateTime objects with same instant but different time zones returned 0. Then the comparison would no longer be consistent with equals. Which is considered a pretty nice property (though not required).

The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C.

(Quoted from Comparable interface documentation)

So if you wanted to compare just the instants, there are two options depending on what type you prefer for your result:

  1. Use isBefore, isAfter or isEqual:

    ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T16:01:00.000+03:00");
    ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
    System.out.println(t1.isBefore(t2));
    System.out.println(t1.isAfter(t2));
    System.out.println(t1.isEqual(t2));
    

    Output:

false
false
true
  1. Use the answer by Yoav Gur: explicitly compare the instants.
like image 95
Ole V.V. Avatar answered Nov 15 '22 18:11

Ole V.V.


Use toInstant(), then compare the results, like so:

    ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T16:01:00.000+03:00");
    ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
    System.out.println(t1.toInstant().compareTo(t2.toInstant())); 
like image 42
Yoav Gur Avatar answered Nov 15 '22 19:11

Yoav Gur