问题
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?
回答1:
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()));
回答2:
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 ife1.compareTo(e2) == 0
has the same boolean value ase1.equals(e2)
for everye1
ande2
of classC
.
(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:
Use
isBefore
,isAfter
orisEqual
: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
- Use the answer by Yoav Gur: explicitly compare the instants.
来源:https://stackoverflow.com/questions/49686272/java-result-when-compare-two-zoneddatetime-is-not-as-expected