Find the earliest date among 3 dates
Asked Answered
B

15

27

I have three dates in Java: a, b, c. Any or all of these dates may be null. What's the most efficient way of determining the earliest date among a,b,c, without having a massive if-else block? I would want a the actual date that is earliest among a,b,c. Any date is better than a null date.

Biddick answered 4/4, 2013 at 20:20 Comment(6)
and what have you tried that is not so efficient?Vendee
Do you need to know which date is the earliest (i.e. tie it back to the variable), or just the value?Girlish
You could convert each Date to its millisecond representation and use a compound Math.min to determine the smallest valueSemiporcelain
Is null greater or smaller than any date?Farhi
I would want a the actual date that is earliest among a,b,c. Any date is better than a null date.Biddick
See the similar Question, Nice general way to sort nulls to the bottom, regardless?.Complex
P
35

There's no getting around null checking, but with some refactoring you can make it painless.

Create a method that safely compares two dates:

/**
 * Safely compare two dates, null being considered "greater" than a Date
 * @return the earliest of the two
 */
public static Date least(Date a, Date b) {
    return a == null ? b : (b == null ? a : (a.before(b) ? a : b));
}

then combine calls to that:

Date earliest = least(least(a, b), c);

Actually, you can make this a generic method for any Comparable:

public static <T extends Comparable<T>> T least(T a, T b) {
    return a == null ? b : (b == null ? a : (a.compareTo(b) < 0 ? a : b));
}
Pergola answered 4/4, 2013 at 20:31 Comment(0)
D
24

Java 8+ oneliner. To make it safe, null check is added. Pass any number of dates.

public static Date min(Date... dates) {
    return Arrays.stream(dates).filter(Objects::nonNull).min(Date::compareTo).orElse(null);
}

Not null safe, but much shorter:

public static Date min(Date... dates) {
    return Collections.min(Arrays.asList(dates));
}

Not null safe without a new method:

Collections.min(Arrays.asList(date1, date2));
Drafty answered 19/12, 2019 at 19:16 Comment(5)
If you are using Java or higher you should not use Date. That class is poorly designed and long outdated. Depending on exact requirements use Instant or another class from java.time, the modern Java date and time API. Even on Java 6 or 7 you may consider the same using ThreeTen Backport.Laurent
* using Java 8 or higherLaurent
@OleV.V. I prefer to use Date in most cases, because there is no issues to use it in DTO with Spring MVC (spring.jackson.serialization.write-dates-as-timestamps=true), no timezone problems between JavaScript (Angular, React, etc) and Java using REST. In JS just use new Date(timeFromServer) without any parsers. You don't ever think about a timezone when Date comes from frontend and goes with Hibernate (also Date) to database and vice versa. Also it perfectly works with Swagger (OpenAPI). It's very well supported by all new and old frameworks. New time API is perfect for time math.Drafty
Date has caused innumerable time zone errors over the years. Hibernate 5 supports java.time just fine. jackson-modules-java8 too. Tastes differ.Laurent
Java 8+ oneliner. Nit-picking, it needs to be wrapped in a varargs method as you show in order to fill the three values into an array. So I would not call it a one-liner.Laurent
C
13

When Apache Commons is available, you might use ObjectUtils.min:

Date earliest = ObjectUtils.min(a, b, c);
Ciapha answered 2/11, 2020 at 22:7 Comment(1)
Shortest solution +1Borne
W
8

Well, 'efficient' has some different meanings, but I don't think there will be an efficiency problem with comparing three dates. In fact, it's really cheap. You can try this approach:

SortedSet<Date> dates = new TreeSet<Date>();
dates.add(date1);
dates.add(date2);
// ...
dates.add(dateN);
Date earliest = dates.first();

Or, maybe more elegant:

for (Date date : someDates) {
   if (date != null) {
      dates.add(date);
   }
}
Date earliest = dates.first();
Waltraudwaltz answered 4/4, 2013 at 20:29 Comment(2)
but it's sorts all elements, and redundant in task of finding minRobenarobenia
The former crashes with a NullPointerException when a date is null. The latter crashes with a NoSuchElementException if all three are. The answer is also outdated since it uses the problematic and outmoded Date class.Laurent
O
8

java.time.LocalDate

The java.util.Date & java.sql.Date classes were supplanted by the java.time classes defined in JSR 310.

For Java 8 and later, use LocalDate for a date-only value.

To get the earliest of a list of dates:

import java.time.LocalDate;

public static LocalDate earliestDate(LocalDate... dates) {
    return
        Arrays
        .stream(dates)
        .filter(Objects::nonNull)
        .min(LocalDate::compareTo)
        .orElse(null);
}
Openair answered 27/12, 2020 at 20:27 Comment(2)
My answer specifically covers Java 8 LocalDate which none of the others does.Openair
I hadn’t noticed. Very well, I changed my down-vote to an up-vote, and removed my Comment. I suggest you add some prose explaining your use of LocalDate, the benefits, and how your Answer is different and better.Complex
T
6

Some Java 8 methods using streams. The first will filter nulls before comparing, the second will put them at the end of the list.

Date minDate = Arrays.asList(date1, date2, etc).stream()
     .filter(Objects::nonNull).min(Date::compareTo).get()

or

Date minDate = Arrays.asList(date1, date2, etc).stream()
    .sorted((a, b) -> {
        //some kind of custom sort.
        if(a == null && b == null) return 0;
        if(a == null) return 1;
        if(b == null) return -1;
        return a.compareTo(b);
    }).findFirst().get()
Titanesque answered 1/5, 2018 at 13:35 Comment(1)
Won’t the second one fail with a NoSuchElementException if all the dates are null? PS Don’t use Date.Laurent
L
5

Arrays.asList(), Collections.min() and a null-safe comparator

    LocalDate a = LocalDate.of(2025, Month.JANUARY, 6);
    LocalDate b = null;
    LocalDate c = LocalDate.of(2023, Month.NOVEMBER, 22);

    List<LocalDate> dates = Arrays.asList(a, b, c);
    LocalDate earliest = 
        Collections.min(
            dates,
            Comparator.nullsLast(Comparator.naturalOrder())
        );

    System.out.println("Earliest date: " + earliest);

I consider this both clear and short and simple, also compared to the stream solutions in a couple of other answers. Output is:

Earliest date: 2023-11-22

If all three dates happen to be null, the output is:

Earliest date: null

Often we prefer List.of() over Arrays.asList() when passing each element; but since List.of() does not accepts nulls, we can’t use it here.

Comparators are often used with sorting, but they are also very practical for just finding the minimum or maximum. My code does not perform any complete sort, it only finds the min according to the comparator.

Do use LocalDate from java.time, the modern Java date and time API, for a date. The Date class used in some of the answers had severe design problems and has been outdated the last 10 years (and counting).

The idea can obviously be used with any class that implements Comparable and any number of objects of that class.

Documentation links

Laurent answered 7/3, 2024 at 18:6 Comment(1)
Simple and elegant solution! Comparator.nullsLast() fits perfectly with the requirements. I liked this solution over the previous ones on this page. Thanks for this quality contribution!Hugh
J
2

Use the java Date object http://docs.oracle.com/javase/6/docs/api/java/util/Date.html

You can use the before() and after() functions of these objects then

Jitterbug answered 4/4, 2013 at 20:21 Comment(0)
A
2

using before and after :

  /**
     * find Min Dates
     * @param date1
     * @param date2
     * @return
     */
    public static Date minDate(Date date1, Date date2) {
        // if date1 before date2 then return date1 else return date2
        return date1.before(date2) ? date1 : date2;
    }
     /**
     * find Max Dates
     * @param date1
     * @param date2
     * @return
     */
    public static Date maxDate(Date date1, Date date2) {
        // if date1 after date2 then return date1 else return date2
        return date1.after(date2) ? date1 : date2;
    }
Akela answered 5/6, 2017 at 11:18 Comment(0)
O
2

Using stream:

Date min = Stream.of(date1, date2, etc)
  .filter(Objects::nonNull)
  .min(Date::compareTo)
  .orElse(null);

If your array does not contain NULL values you can use Ordering

Ordering.natural().min(date1, date2, etc);
Ophiuchus answered 28/4, 2020 at 6:38 Comment(1)
Which Ordering type is that?? Neither my IDE nor my documentation knows of it. PS Don’t use Date.Laurent
L
1

Another way is to use java.util.Collections.min(collection):

Returns: the minimum element of the given collection, according to the natural ordering of its elements.

public static Date getEarliestDate(List<Date> dates) {

    if (dates == null || dates.isEmpty())
        return null;

    dates.removeIf(Objects::isNull);

    return dates.isEmpty() ? null : Collections.min(dates);
}
Longer answered 26/6, 2017 at 7:58 Comment(1)
Since the OP had three dates a, b and c, the natural thing to pass to your method is Arrays.asList(a, b, c). When I do that, I get an UnsupportedOperationException from the call to removeIf() if one of the dates is indeed null. PS Don’t use Date.Laurent
C
0

You can use

date1.compareTo(anotherDate)

Returns:

the value 0 if the argument Date is equal to this Date; a value less than 0 if this Date is before the Date argument; and a value greater than 0 if this Date is after the Date argument.

Throws:

NullPointerException - 

if anotherDate is null.

Ca answered 4/4, 2013 at 20:29 Comment(0)
C
0

just another version as an idea and for fun

new Date(Math.min(a != null ? a.getTime() : Long.MAX_VALUE
, Math.min(b != null ? b.getTime() : Long.MAX_VALUE
, c != null ? c.getTime() : Long.MAX_VALUE)))
Caballero answered 4/4, 2013 at 20:53 Comment(0)
M
0

If you're also using kotlin and if entries are not null, it becomes as simple as minOf(a, b, c). If they can be null, listOfNotNull(a, b, c).minOrNull().
For a collection, it's dates.filterNotNull().minOrNull().

Mundane answered 7/3, 2024 at 14:51 Comment(0)
S
-1
List.of(date1, date2)
                .stream()
                .min(Date::compareTo)
                .get();

It is simple solution which I wanted to share that could be used in a private method when you are sure all dates are not null.

Sorgo answered 20/1, 2022 at 8:3 Comment(1)
sorry for my fault, really did not read a question. However, it is simple solution, which I wanted to share that could be used in private method when you sure all dates are not nullSorgo

© 2022 - 2025 — McMap. All rights reserved.