3rdRockSoftware
3rdRockSoftware

Reputation: 85

Is there a better way of comparing dates

I am trying to compare two dates. The dates are imported from a database as strings and may or may not be null

This is my code

private String compareDates(String date1, String date2)
    {
        String earliestDate = null;
//        date2 is null and date1 is null
//            earliest = “not seen yet”
        if (date2 == null && date1 == null)
        {
            earliestDate = "not seen yet";
        }
//        date2 is null and date1 is not null
//            earliest = date1
        if (date2 == null && date1 != null)
        {
            earliestDate = date1;
        }
//        date2 is not null and date1 is null
//            earliest = date2
        if (date2 != null && date1 == null)
        {
            earliestDate = date2;
        }
//        date2 is not null and date1 is not null
//            compare dates
        if (date2 != null && date1 != null)
        {
            LocalDate LDdate1 = LocalDate.parse(date1);
            LocalDate LDdate2 = LocalDate.parse(date2);
            if (LDdate1.isBefore(LDdate2) || LDdate1.isEqual(LDdate2))
            {
                earliestDate = LDdate1.toString();
            }
            else
            {
                earliestDate = LDdate2.toString();
            }
        }
        return earliestDate;
    }

This code gives me the correct output, which is the earliest date, or "not yet seen" if both dates are null, but I wondered if there is a better / more efficient way of doing things.

I imagine using a switch statement is an option but would that be better or just a different way of doing it?

The data from the database only consists of about 200 dates so it isn't dealing with a lot of data, it's just that I'm interested in writing better code

Upvotes: 1

Views: 2132

Answers (6)

Anonymous
Anonymous

Reputation: 86389

The dates are imported from a database as strings …

First point: don’t do that. Fetch your dates as LocalDate objects from your database and handle them as such in your program. For how to fetch a LocalDate from an SQL database, see the link at the bottom.

Next, I’d use a stream pipeline for finding the earlier one:

    // Some example dates
    LocalDate date1 = null;
    LocalDate date2 = LocalDate.of(2020, Month.OCTOBER, 24);
    
    String earlierDate = Stream.of(date1, date2)
            .filter(Objects::nonNull)
            .min(LocalDate::compareTo)
            .map(LocalDate::toString)
            .orElse("Not seen yet");
    System.out.println(earlierDate);

Output:

2020-10-24

Link: Question Insert & fetch java.time.LocalDate objects to/from an SQL database such as H2

Upvotes: 0

supernova
supernova

Reputation: 2100

If the dates are in standard local form and ordered YYYY MM DD like

"2018-01-24"

why bother parsing at all? Just do a String comparison. Approx 100 times faster and also easier.

    public static String compareDatesStr(String date1, String date2) {
        if (date1 == null)
            if (date2 == null)
                return "not seen yet";
            else
                return date2;
        else if (date2 == null)
            return date1;
        else
            return date1.compareTo(date2) > 0 ? date2 : date1;
    }

Some non-scientific performance comparison:

    private static String compareDatesParse(String date1, String date2) {
        String earliestDate;
        if (date1 == null) {
            if (date2 == null) {
                earliestDate = "not seen yet";
            }
            else {
                earliestDate = date2;
            }
        }
        else {
            if (date2 == null) {
                earliestDate = date1;
            }
            else {
                LocalDate LDdate1 = LocalDate.parse(date1);
                LocalDate LDdate2 = LocalDate.parse(date2);
                if (LDdate1.isAfter(LDdate2)) {
                    earliestDate = date2;
                }
                else {
                    earliestDate = date1;
                }
            }
        }
        return earliestDate;
    }

    public static void testDateComp(){
        String[] dates = {"2020-10-30", "2020-10-29", "1990-10-30", "2021-06-01", null};
        String result = null;
        int loops = 400000;
        long start, end = 0;
        // System.out.println("Runs: " + (dates.length*dates.length*loops));


        start = System.currentTimeMillis();
        for (int i = 0; i < loops; i++) {
            for (String date1:dates) {
                for (String date2:dates) {
                    result = compareDatesStr(date1, date2);
                }
            }
        }
        end = System.currentTimeMillis();
        System.out.println("Runtime String: " + (end - start)+ "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < loops; i++) {
            for (String date1:dates) {
                for (String date2:dates) {
                    result = compareDatesParse(date1, date2);
                }
            }
        }
        end = System.currentTimeMillis();
        System.out.println("Runtime Parsing: " + (end - start) + "ms");
    }

10MM method executions:

Runtime String: 61ms
Runtime Parsing: 7361ms

Upvotes: 1

MC Emperor
MC Emperor

Reputation: 23057

There are some other options than a bunch of similarly-looking if statements.

  1. Comparator

    if (date1 == null && date2 == null) {
        return "not seen yet";
    }
    Comparator<String> c = Comparator.nullsLast((String a, String b) -> LocalDate.parse(a).compareTo(LocalDate.parse(b)));
    return c.compare(date1, date2) <= 0 ? date1 : date2;
    
  2. Stream::reduce using a Comparator

    return Stream.of(date1, date2)
        .filter(Objects::nonNull)
        .reduce((a, b) -> LocalDate.parse(a).compareTo(LocalDate.parse(b)) <= 0 ? a : b)
        .orElse("not seen yet");
    

To me, both options are better readable than what you have now, but I would go for option 1. One may find using streams a little bit overkill, which, to some extend, is true.

Upvotes: 1

fps
fps

Reputation: 34470

You could use a custom Comparator, which allows to specify the comparison logic in a declarative way:

private String compareDates(String date1, String date2) {

    Comparator<String> c = Comparator.nullsLast(
                           Comparator.comparing(LocalDate::parse));

    int result = c.compare(date1, date2);

    return result == 0 && date1 == null ?
           "not seen yet"               :
           result <= 0                  ?
           date1                        :
           date2;
}

Upvotes: 1

Nandha Seshan
Nandha Seshan

Reputation: 69

You could reduce one if condition like this:

private String compareDates(String date1, String date2) {
    String earliestDate = "not seen yet";
    if (!StringUtils.isEmpty(date2) && !StringUtils.isEmpty(date1)) {
        LocalDate LDdate1 = LocalDate.parse(date1);
        LocalDate LDdate2 = LocalDate.parse(date2);
        if (LDdate1.isBefore(LDdate2) || LDdate1.isEqual(LDdate2)) {
            earliestDate = LDdate1.toString();
        }
        else {
            earliestDate = LDdate2.toString();
        }
    } else if (!StringUtils.isEmpty(date2) && StringUtils.isEmpty(date1)) {
        earliestDate = LDdate2;
    } else if (StringUtils.isEmpty(date2) && !StringUtils.isEmpty(date1)) {
        earliestDate = LDdate1;
    }
    return earliestDate;
} 

Upvotes: 1

Abra
Abra

Reputation: 20924

How about the following?

private String compareDates(String date1, String date2) {
    String earliestDate;
    if (date1 == null) {
        if (date2 == null) {
            earliestDate = "not seen yet";
        }
        else {
            earliestDate = date2;
        }
    }
    else {
        if (date2 == null) {
            earliestDate = date1;
        }
        else {
            LocalDate LDdate1 = LocalDate.parse(date1);
            LocalDate LDdate2 = LocalDate.parse(date2);
            if (LDdate1.isAfter(LDdate2)) {
                earliestDate = date2;
            }
            else {
                earliestDate = date1;
            }
        }
    }
    return earliestDate;
}

Upvotes: 0

Related Questions