Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a date string be validated by DateTimeFormatter without catching exception?

Considering that flow control via exceptions is considered (by many) to be an anti-pattern, is it possible to validate that a string represents a valid date using the temporal library (java.time.*), without catching an exception?

Consider the following code, which relies upon an internal explosion:

public static boolean isValidDateFormat(String date, DateTimeFormatter formatter) {
    try {
        formatter.parse(date);
        return true;
    } catch (DateTimeParseException e) {
        return false;
    }
}

Can this be achieved without catching a parse explosion? Is there something akin to formatter.isValid(date) (even if that internally explodes - that would be up to the JDK impl which is "behind the curtain").

like image 213
Bohemian Avatar asked Jul 24 '15 06:07

Bohemian


People also ask

How do you verify if a string is a date in Java?

DateValidator validator = new DateValidatorUsingDateFormat("MM/dd/yyyy"); assertTrue(validator. isValid("02/28/2019")); assertFalse(validator. isValid("02/30/2019")); This was the most common solution before Java 8.

Is DateTimeFormatter thread-safe Java?

Yes, it is: DateTimeFormat is thread-safe and immutable, and the formatters it returns are as well. Implementation Requirements: This class is immutable and thread-safe.

How do you validate a date in YYYY-MM-DD format in Java?

Run it through SimpleDateFormat with yyyyMMdd ? input. matches("\\d{8}") tests if input consists of 8 digits.

What is the role of the DateTimeFormatter type in Java?

Formatter for printing and parsing date-time objects. This class provides the main application entry point for printing and parsing and provides common implementations of DateTimeFormatter : Using predefined constants, such as ISO_LOCAL_DATE. Using pattern letters, such as uuuu-MMM-dd.


1 Answers

The format engine of java.time.format always works with internal exceptions to control the flow. This is even true if you try to use a ParsePosition. An exception occurs, and the ParsePosition-object does not even report an error:

   pp = new ParsePosition(0);
   try {
       TemporalAccessor t = 
           DateTimeFormatter.ofPattern("uuuu-MM-dd")
             .withResolverStyle(ResolverStyle.STRICT)
             .parse("2015-02-29", pp);
   } catch (RuntimeException e) {
       e.printStackTrace();
       System.out.println("Error! " + pp);
       // Error! java.text.ParsePosition[index=10,errorIndex=-1]
   }

The javadoc explains:

The operation of this method is slightly different to similar methods using ParsePosition on java.text.Format. That class will return errors using the error index on the ParsePosition. By contrast, this method will throw a DateTimeParseException if an error occurs, with the exception containing the error index. This change in behavior is necessary due to the increased complexity of parsing and resolving dates/times in this API.

The following example tries to avoid an exception by using the method parseUnresolved:

   ParsePosition pp = new ParsePosition(0);
   try {
        TemporalAccessor t = 
            DateTimeFormatter.ofPattern("uuuu-MM-dd")
             .withResolverStyle(ResolverStyle.STRICT)
             .parseUnresolved("2015-02-29", pp);
        System.out.println("Info! " + t + "/" + pp); // note, no error in pp here!
        // Info! {DayOfMonth=29, MonthOfYear=2, Year=2015},null,null/java.text.ParsePosition[index=10,errorIndex=-1]
        boolean leapyear = Year.from(t).isLeap();
        MonthDay md = MonthDay.from(t);
        if (!leapyear && md.getDayOfMonth() == 29 && md.getMonth().getValue() == 2) {
            System.out.println("Error!"); // hand-made validation covering a special case
        }
   } catch (RuntimeException e) {
        e.printStackTrace(); // does not happen for given input
   }

This works without exception but you have to write the validation code yourself which asks for trouble.

I have always considered this approach of throwing exceptions for controlling the program flow as bad coding practice and therefore designed my own library Time4J in such a way that it strives for avoiding internal exceptions as good as possible (not in every case but in most cases without exception).

   ParseLog plog = new ParseLog();
   PlainDate date = ChronoFormatter.ofDatePattern("uuuu-MM-dd", PatternType.CLDR, Locale.ROOT).parse("2015-02-29", plog);
   System.out.println(date); // null
   System.out.println(plog.isError() + "/" + plog.getErrorMessage());
   // true/Validation failed => DAY_OF_MONTH out of range: 29 [parsed={YEAR=2015, MONTH_AS_NUMBER=2, DAY_OF_MONTH=29}]

This code clearly demonstrates the possibility of another design. I consider the choosen design of java.time as potential bottleneck if it comes to batch processing of mass bulk data with a lot of wrong data.

like image 150
Meno Hochschild Avatar answered Sep 30 '22 05:09

Meno Hochschild