Search code examples
javadatebean-iorfc3339

Parse RFC3339 Date String 'ss.SSSXX' with BeanIO


I'm trying to parse a String Date like this: 2021-03-19T13:08:32.58600 with BeanIO in my template:

<field name="updatedAt" typeHandler="dateTypeHandler" format="yyyy-MM-dd'T'HH:mm:SSSXX"/>

And I'm getting Invalid date error.

I test some cases with SimpleDateFormat and for example, if I do something like this: new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(text) it works.

The problem is in DateTypeHandlerSupport class, the method parse validate the length:

    protected Date parseDate(String text) throws TypeConversionException {
        if ("".equals(text))
            return null;

        ParsePosition pp = new ParsePosition(0);
        Date date = getFormat().parse(text, pp);
        if (pp.getErrorIndex() >= 0 || pp.getIndex() != text.length()) {
            throw new TypeConversionException("Invalid date");
        }
        return date;
    }

Is there any way which I could parse the string without creating my own DateTypeHandler?


Solution

  • The trailing 00 of your string is not a valid UTC offset. I am pretty convinced that it was never meant to be one. Rather your string gives us seconds with a decimal fraction of 5 digits and does not include an offset.

    And I am sorry, you cannot parse that. If BeanIO internally uses SimpleDateFormat (a notorious troublemaker of a class, fortunately long outdated), then there is no way it can parse 2021-03-19T13:08:32.58600 correctly. SimpleDateFormat supports only exactly three decimals of fraction on the seconds and will parse .58600 as 58600 milliseconds, that is, 58.6 seconds (that’s correct). See the links to related questions at the bottom.

    Also your string does not conform to RFC-3339. According to RFC-3339 a timestamp must include a time-offset which must be either of "Z" / time-numoffset, where time-numoffset is ("+" / "-") time-hour ":" time-minute. So examples of valid offsets include Z, +00:00, +11:00, -00:00 and -11:00. If you have received that string as RFC-3339 from somewhere, it seems that you need to educate the publisher about that format standard.

    Parsing your string in Java is easy.

        String yourString = "2021-03-19T13:08:32.58600";
        LocalDateTime ldt = LocalDateTime.parse(yourString);
        System.out.println(ldt);
    

    Output:

    2021-03-19T13:08:32.586

    The classes of java.time parse the most common variants of ISO 8601 format as their default, that is, without any specification of the format. RFC-3339 is based on ISO 8601 and is simplified compared to it. Since your string did not include UTC offset, I parsed into a LocalDateTime, which the class for a date and time without time zone or UTC offset.

    Links