8

I have two ZonedDateTime instances:

final ZonedDateTime a = ...;
final ZonedDateTime b = ...;

I want to get the maximum of those two values. I want to avoid having to write custom ad-hoc code.

What is the best way to do this in Java 8? I am currently doing it as following:

final ZonedDateTime c = Stream.of(a, b).max(ChronoZonedDateTime::compareTo).get();

Are there better approaches?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
thatsme
  • 83
  • 1
  • 4

2 Answers2

8

You can simply call isAfter:

ZonedDateTime max = a.isAfter(b) ? a : b;

or since the class implements Comparable:

a.compareTo(b);

As pointed out by OleV.V. in the comments it's a difference between the two methods for comparing times. So they might produce different results for the same values

DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
ZonedDateTime time1 = ZonedDateTime.from(formatter.parse("2019-10-31T02:00+01:00"));
ZonedDateTime time2 = ZonedDateTime.from(formatter.parse("2019-10-31T01:00Z"));

System.out.println(time1.isAfter(time2) + " - " + time1.isBefore(time1) + " - " + time1.isEqual(time2));
System.out.println(time1.compareTo(time2));

Generates

false - false - true
1

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
  • 1
    The two will not always give the same result in corner cases. `isAfter` compares the times, the instants, only. `compareTo` goes on to compare the local times if the instants are equal. So if `a` is `2019-10-31T02:00+01:00[Europe/Stockholm]` and `b` is `2019-10-31T01:00Z[Europe/London]`, then `a.isAfter(b)` is false, but `a.compareTo(b) > 0` is true. – Ole V.V. Oct 23 '19 at 08:03
  • @OleV.V. So what you are saying is that `isAfter` is more correct because it handles time zones while `compareTo` should be used if you want to compare two local times with each other independent of time zone. – Joakim Danielson Oct 23 '19 at 11:13
  • 1
    No, @JoakimDanielson, not exactly. `compareTo` compares the instants first, so if the two objects denote different points in time (even just 1 nanosecond apart), it works the way you expect. Only if they denote the same point in time, will it compare the local times instead. So both your snippets live up to the requirements as stated by the questioner. Only their way of breaking ties differs. (For comparison how `Collections.max` used in the other answer breaks ties is not specified at all.) – Ole V.V. Oct 23 '19 at 11:18
7

ZonedDateTime implements Comparable interface so you can simple use Collections.max

Collections.max(Arrays.asList(a,b));
Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • 3
    This is also what Guava suggests. See for example https://guava.dev/releases/23.0/api/docs/com/google/common/collect/Ordering.html#max-E-E-. – thatsme Oct 23 '19 at 10:16
  • 1
    @thatsme That link is a general recommendation to use the above method instead of their own similar method and is not specific to date/time comparisons. I can't see how it is more effective to first create a list object and then call compareTo rather than calling compareTo directly – Joakim Danielson Oct 23 '19 at 11:29
  • 1
    @JoakimDanielson To me the point is that calling a method named `max` is more readily readable. Either approach is valid and the choice probably a matter of taste (I upvoted both answers). – Ole V.V. Oct 23 '19 at 11:48