Search code examples
javadatetimejava-8java.time.instant

Parsing date with differents possible format


I have to parse a String to a timestamp in UTC. The String can have those format :

  • YYYY-MM-DDThh:mm:ss.sssZ
  • YYYY-MM-DDThh:mm:ss.sss+/-hh:mm
  • YYYY-MM-DDThh:mm:ss.sss (considered at UTC, so add a Z at the end)

What's the best way to do this and avoid :

    try {
        firstDateTimeFormatter.parse(string, Instant::from).toEpochMilli();
    } catch (DateTimeParseException e) {
        try {
            secondDateTimeFormatter.parse(string, Instant::from).toEpochMilli();
        } catch (DateTimeParseException e2) {
                thirdDateTimeFormatter.parse(string, Instant::from).toEpochMilli();
        }
    }

Solution

  • There are various options. Here’s a simple one:

    private static DateTimeFormatter formatter 
            = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS[XXX]")
                    .withZone(ZoneOffset.UTC);
    
    public static Instant parse(String offsetDateTimeString) {
        return OffsetDateTime.parse(offsetDateTimeString, formatter).toInstant();
    }
    

    Let’s try it out:

        System.out.println(parse("2018-08-04T21:41:55.987Z"));
        System.out.println(parse("2018-08-04T19:41:55.987-02:00"));
        System.out.println(parse("2018-08-04T21:41:55.987"));
    

    This prints:

    2018-08-04T21:41:55.987Z
    2018-08-04T21:41:55.987Z
    2018-08-04T21:41:55.987Z
    

    The square brackets in the format pattern string surround an optional part, so the offset may be present or not. Offset X uses Z for offset zero so matches the first two of your three formats. To specify the offset to use if there isn’t any in the string I have set a default time zone of UTC on the formatter.

    Variations include:

    • You may use predefined ISO formats rather than writing the entire format patterns string yourself.
    • Since we are after an offset, not a time zone, it may be more correct to specify a default offset rather than a default time zone.

    DateTimeFormatterBuilder allows us to do both. So here’s a different formatter you may use in the above code instead:

    private static DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            .appendPattern("[XXX]")
            .parseDefaulting(ChronoField.OFFSET_SECONDS, ZoneOffset.UTC.getTotalSeconds())
            .toFormatter();
    

    Results are identical.