Reputation: 85
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
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
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
Reputation: 23057
There are some other options than a bunch of similarly-looking if statements.
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;
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
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
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
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