Im trying to parse a string that contain a date in an unknown format and the way I choose (not the best) is to try all the possible formats until parse correctlly. To do this Im using Vavr library and till now I've created something like this:
// My unknown date
String date = "2020-11-12T15:15:15.345";
// Date format that works for my unknown date (just for testing)
DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]")
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter();
OffsetDateTime value = OffsetDateTime.parse(date, FORMATTER); // PARSE CORRECTLY
// Try all possible formats until one works
Try<OffsetDateTime> myParsedDate = Try.of(()->date)
.map(x->OffsetDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd")))
.onFailure(x->System.out.println("NO yyyy-MM-dd"))
.map(x->OffsetDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm")))
.onFailure(x->System.out.println("NO yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm"))
.map(x->OffsetDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm")))
.onFailure(x->System.out.println("NO yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm"))
.map(x->OffsetDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")))
.onFailure(x->System.out.println("NO yyyy-MM-dd'T'HH:mm:ss.SSS"))
.map(x->OffsetDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")))
.onFailure(x->System.out.println("NO yyyy-MM-dd'T'HH:mm:ss.SSSZ"))
.map(x->OffsetDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")))
.onFailure(x->System.out.println("NO yyyy-MM-dd'T'HH:mm:ss"))
.map(x->OffsetDateTime.parse(date, FORMATTER)) // DOSENT WORK
.onFailure(x->System.out.println("NO yyyy-MM-dd'T'HH:mm:ss[.SSS]"));
if(myParsedDate.isSuccess()) {
System.out.println("OK");
}else {
System.out.println("KO");
}
Output:
NO yyyy-MM-dd
NO yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm
NO yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm
NO yyyy-MM-dd'T'HH:mm:ss.SSS
NO yyyy-MM-dd'T'HH:mm:ss.SSSZ
NO yyyy-MM-dd'T'HH:mm:ss
NO yyyy-MM-dd'T'HH:mm:ss[.SSS]
The question is: how to concatenate many try/catch or in this case using VAVR many actions that when one action fail try the next one and so one ? Thanks
Put your formatter into a Java Stream
and try on each of them until one successful:
import io.vavr.control.Try;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Optional;
import java.util.stream.Stream;
public class Test {
public static void main(String[] args) {
String date = "2020-11-12T15:15:15.345";
DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]")
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter();
OffsetDateTime value = OffsetDateTime.parse(date, FORMATTER);
Optional<OffsetDateTime> res = Stream.concat(Stream.of(
"yyyy-MM-dd",
"yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm",
"yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ss")
.map(p -> DateTimeFormatter.ofPattern(p)), Stream.of(FORMATTER))
.map(fmt -> Try.of(() -> OffsetDateTime.parse(date, fmt)))
.filter(Try::isSuccess)
.map(Try::get)
.findFirst();
System.out.println(res); //prints Optional[2020-11-12T15:15:15.345Z]
}
}
Stream.concat
is used for adding the FORMATTER with the rest of the Formatters.
In the end you will get an Optional<OffsetDateTime>
. It will be a None
if everything failed, or a Some
if one of them succeeded. The Java Stream is lazy so that once one match found, it will stop the rest from being executed.
If you want to print out all the failed cases too, you can add onFailure before filter
.
Edit: adding the case for special FORMATTER