1

How to modify following Date Comparison using Java 8 which reduce whole these methods into less lines which includes Null Safety too?

//Format and convert to date
private static Date handleDate(String dateInString) {
    if(dateInString == null) {
        return null;
    }
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    try {
        return formatter.parse(dateInString);
    } catch (ParseException e) {
        e.printStackTrace();
        return null;
    }
}

//Checks if dates are equal
private static boolean dateEquals(Date date1, Date date2) {
    if (date1 == null && date2 != null) {
        return false;
    }
    if (date2 == null && date1 != null) {
        // We already have data, don't want null there then.
        return false;
    }

    if (date1 != null && date2 != null) {
        if(date1.equals(date2)) {
            return true;
        }
    }
    return false;
}

//Main
public static void main(String[] args) {

    Date current = handleDate("2019-09-02T00:00:00");
    Date existing = handleDate("2019-09-02T01:00:00");

    System.out.println(dateEquals(existing, current));

Also I do not want to have null check, so is there any utility doing so?

Naman
  • 27,789
  • 26
  • 218
  • 353
fatherazrael
  • 5,511
  • 16
  • 71
  • 155
  • 1
    One of the possible simplification `private static boolean dateEquals(Date date1, Date date2) { return date1 == null && date2 == null || date1 != null && date1.equals(date2); }` – Naman Aug 29 '19 at 07:04
  • 2
    Any reason for using legacy `Date` instead of java-8 `LocalDate` or other related ? – Benoit Aug 29 '19 at 07:52
  • Are you sure you want to represent an unparseable date string with `null`? I am trying, and I can’t find a situation where this makes sense. You’d rather want to discover the error and ask you user to correct it. – Ole V.V. Aug 29 '19 at 15:21
  • 1
    @Naman or [`return Objects.equals(date1, date2);`](https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#equals-java.lang.Object-java.lang.Object-)… – Holger Aug 29 '19 at 17:48

2 Answers2

4

For null-tolerant comparisons: java.util.Objects.equals(date1, date2) (since Java 7)

For formatting I'm not aware of any such utility.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
mtj
  • 3,381
  • 19
  • 30
3

Avoid Date class

You are using the wrong date-time class. Date was supplanted years ago by the modern java.time classes defined in JSR 310. And that class represents a moment in UTC, but your input lacks the context of an offset-from-UTC or time zone.

LocalDateTime

The LocalDateTime class is appropriate to represent a date and a time-of-day lacking any context of time zone or offset-from-UTC.

Your input is in standard ISO 8601 format. The java.time classes use these formats by default when parsing/generating text.

LocalDateTime x = LocalTimeDate.parse( "2019-09-02T00:00:00" ) ;

Compare as shown in the other Answer.

int result =  Objects.equals( x , y ) ;

Tolerating nulls

If you want to tolerate nulls in your input string, write your own wrapper around parsing. Return an Optional possibly containing a LocalDateTime. If your are new to Optional, do not get all excited and go sprinkling them throughout your codebase. That class is intended only for return values.

public Optional < LocalDateTime > parsePossibleNull ( String input )
{
    LocalDateTime ldt = null;
    if ( Objects.isNull( input ) )
    {
        return Optional.ofNullable( ldt );
    } else
    {
        try
        {
            ldt = LocalDateTime.parse( input );
            return Optional.of( ldt );
        } catch ( DateTimeParseException e )
        {
            return Optional.ofNullable( ldt );
        }
    }
}

⚠ Tolerating nulls is unwise

This much toleration of nulls is likely unwise. While your code should be defensive against invalid inputs, parsing/handling code should generally not take responsibility for such poor inputs, nor should it pass faulty inputs along the chain. Usually it is much wiser to reject bad input as early as possible, throwing an exception if need be.

Optional types are comparable

But if you insist, you can compare the Optional<LocalDateTime> objects.

To quote the Javadoc:

The other object is considered equal if:

• it is also an Optional and;

• both instances have no value present or;

• the present values are "equal to" each other via equals().

Try it.

String x = "2019-09-02T00:00:00";
String y = null; // "2019-09-02T01:00:00";

Optional < LocalDateTime > xOpt = this.parsePossibleNull( x );
Optional < LocalDateTime > yOpt = this.parsePossibleNull( y );

boolean same = Objects.equals( xOpt , yOpt );

System.out.println( "xOpt = " + xOpt );
System.out.println( "yOpt = " + yOpt );
System.out.println( "same: " + same );

xOpt = Optional[2019-09-02T00:00]

yOpt = Optional.empty

same: false

…and…

xOpt = Optional[2019-09-02T00:00]

yOpt = Optional[2019-09-02T01:00]

same: false

…and…

xOpt = Optional[2019-09-02T00:00]

yOpt = Optional[2019-09-02T00:00]

same: true

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 2
    @fatherazrael True. You could wrap the parsing to detect the null. From there you might want to return an `Optional`. Also, your parsing should trap any `DateTimeParseException` in case of unexpected input strings. – Basil Bourque Aug 29 '19 at 08:21
  • 1
    Do we actually need to parse ISO-8601 formatted strings, when all we want to do, is comparing them? – Holger Aug 29 '19 at 17:54
  • 1
    @Holger Yes, we must parse. "2019-09-02T00:00:00" vs "2019-09-02T00:00" and others. – Basil Bourque Aug 29 '19 at 20:08
  • @BasilBourque ok, I should not have just referred to the ISO-8601. The OP’s original code used the fixed pattern `"yyyy-MM-dd'T'HH:mm:ss"`, having all fields down to seconds mandatory and no units smaller than a second. So it looks like for this particular use case, just comparing the strings would work. It would be easy to formulate a general solution which only parses if both strings are non-null and have different lengths. By the way, you have it swapped one time, `LocalTimeDate` should be `LocalDateTime`. – Holger Aug 30 '19 at 07:46