Check if datetime string is convertible to ZonedDateTime

I want to check if my String is convertible to ZonedDateTime

 String datetime = "2018-01-11T21:32:10.876+02:00"; // valid
 String badDateTime = "blah blah"; //not valid

I thought about checking it with this regex but this is not a relevant solution for me because of private reasons.

I also thought about use try..catch in that way

public void testing() {
    String datetime = "2018-01-11T21:32:10.876+02:00";

    System.out.println(isDateTime("no")); // false
    System.out.println(isDateTime(datetime)); // true
}


private boolean isDateTime(String str){
    boolean checkDateTimeString;

    try {
        ZonedDateTime.parse(str);
        checkDateTimeString = true;
    } catch (DateTimeParseException e){
        checkDateTimeString = false;
    }

    return checkDateTimeString;
}

But using exceptions as return values is bad practice and performance-costly.

Is there any other simple and good way to do that thing?

2 answers

  • answered 2018-01-11 19:49 dimo414

    using exceptions as return values is bad practice and performance-costly.

    It's a good point, and worth looking for alternatives, but in some cases that's simply the API that's provided. Often for parsing you really do want an exception because parse failures are the exceptional case - you expect the value to parse and just need to account for the possibility that it doesn't.

    In this case I'd just catch the exception as you're doing.

  • answered 2018-01-11 19:49 Mickael Marrache

    If you look at the ZonedDateTime.parse method implementation, you will find that the method is defined as follows:

    public static ZonedDateTime parse(CharSequence text) {
        return parse(text, DateTimeFormatter.ISO_ZONED_DATE_TIME);
    }
    
    public static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter) {
        Objects.requireNonNull(formatter, "formatter");
        return formatter.parse(text, ZonedDateTime::from);
    }
    

    If you also look at DateTimeFormatter, you will find a method named parseUnresolved. You can use it as follows:

    DateTimeFormatter.ISO_ZONED_DATE_TIME.parseUnresolved(str, new ParsePosition(0));
    

    The passed string will be parsed but not resolved. You can then look at parseResolved0 implementation and perform actual resolving without throwing exceptions. You will have to use method return value instead.

    The method you need would look like this:

    public TemporalAccessor parse(final CharSequence text) {
        ParsePosition pos = new ParsePosition(0);
        TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_ZONED_DATE_TIME.parseUnresolved(text, pos);
        if (temporalAccessor == null || pos.getErrorIndex() >= 0 || pos.getIndex() < text.length()) {
            return null;
        }
        return temporalAccessor;
    }
    

    Note that the returned TemporalAccessor will not be resolved. I need to look at the code more deeply to find exactly what it means.

    Finally, if you want to check if a string can be parsed as ZonedDateTime without exceptions, you just need to check the return value:

    parse("2018-01-11T21:32:10.876+02:00"); //return ZonedDateTime instance
    parse("blah blah"); //return null
    

    EDIT:

    Since the returned Parsed can't be used, it is "wrong" to write a parse method (unless the methods exposed by TemporalAccessor are useful to you). Therefore, a method that checks validity would be more correct:

    public boolean isParseableAsZonedDateTime(final CharSequence text) {
        ParsePosition pos = new ParsePosition(0);
        TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_ZONED_DATE_TIME.parseUnresolved(text, pos);
        if (temporalAccessor == null || pos.getErrorIndex() >= 0 || pos.getIndex() < text.length()) {
            return false;
        }
        return true;
    }