2

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?

orirab
  • 2,915
  • 1
  • 24
  • 48
Daniel Taub
  • 5,133
  • 7
  • 42
  • 72

3 Answers3

3

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;
}
manash
  • 6,985
  • 12
  • 65
  • 125
  • thank you very much! , I've tried your way, but what I need to do with the TemporalAccessor? – Daniel Taub Jan 11 '18 at 20:30
  • I modified my answer. You just need to check the return value of parse. If null, the string can't be parsed as ZonedDateTime. – manash Jan 11 '18 at 20:35
  • @DanielTaub Please consider accepting my answer if it answers your question. Thanks! – manash Jan 11 '18 at 20:37
  • Ok, im almost dont, but my last comment is that its not return a ZonedDateTime instance, when I'm trying to ZonedDateTime.from(temporal), there's an exception – Daniel Taub Jan 11 '18 at 20:42
  • 1
    In the meantime, I would parse again if the string is found as valid. Trying to get a ZonedDateTime from the Parsed instance is called resolving. This is tricky to do (if possible at all) because of the visibility of classes DateTimeParseContext and Parsed. – manash Jan 11 '18 at 20:56
  • 1
    I think you should return an `Optional` instead of null, or at least a `Boolean` - he asked for a validation method, not an actual parse. – orirab Jan 11 '18 at 21:19
  • Be careful! `DateTimeFormatter.ISO_ZONED_DATE_TIME` parse methods does not always return null. If you put an invalid offset, for example `"2012-04-23T18:25:43.511-25:00"`, this will throw a java.time.DateTimeException. – Tomas Lukac Sep 09 '21 at 06:29
1

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.

dimo414
  • 47,227
  • 18
  • 148
  • 244
0

I use in junit5 tests this code snippet

assertDoesNotThrow(() -> ZonedDateTime.parse("invalid zoned datetime"));

Huluvu424242
  • 756
  • 10
  • 25