Search code examples
javasimpledateformatdate-formattingdateformatterisodate

I haven't been able to parse '2017-04-04T08:04+0000' into an OffsetDateTime


I have created this deserializer to be managed in a centralized custom object mapper bean, and it works fine, but I'm not able to deserialize the dates that are coming in this format '2017-04-04T08:04+0000' into the correct OffsetDateTime format - getting the following exception:

Text '2017-04-04T08:04+0000' could not be parsed, unparsed text found at index 19 (through reference chain: earth.green.integration_models.sh.Prediction$PredictionBuilder["created"])

I have searched lots of formats on the internet and none of them have worked, so if any of you know which format can I use to deserialize this date, will be very helpful.


public class OffsetDateTimeDeserializer
      extends JsonDeserializer<OffsetDateTime>
{
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm[XXX][XXXX]" );


    @Override
    public OffsetDateTime deserialize( final JsonParser parser,
                                       final DeserializationContext context )
          throws IOException
    {
        formatter.withZone( ZoneId.of( "GMT") );
        return OffsetDateTime.parse( parser.readValueAs( String.class ), formatter );
    }
}

Solution

  • Just two lower case xx for offset without colon

    private final DateTimeFormatter formatter
            = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mmxx" );    
    

    When offset zero is given as +0000 rather than Z, use lower case xx.

    Demonstration:

        String valueAsString = "2017-04-04T08:04+0000";
        OffsetDateTime deserialized = OffsetDateTime.parse( valueAsString, formatter );
        System.out.println( deserialized );
    

    This outputs:

    2017-04-04T08:04Z

    What went wrong in your code? From the documentation:

    Offset X and x: This formats the offset based on the number of pattern letters. One letter outputs just the hour, such as '+01', unless the minute is non-zero in which case the minute is also output, such as '+0130'. Two letters outputs the hour and minute, without a colon, such as '+0130'. Three letters outputs the hour and minute, with a colon, such as '+01:30'. Four letters outputs the hour and minute and optional second, without a colon, such as '+013015'. Five letters outputs the hour and minute and optional second, with a colon, such as '+01:30:15'. …

    According to this XXX should try to parse an offset like +00:00, with a colon, and fail since there is no colon in the offset in the parsed string. Next XXXX should try either +0000 or +000000 since the seconds are optional, so should succeed. Which it also did on my computer. So explanations I can think of include:

    • The code you were running was not eaxctly the same code as you posted in the question. I believe that maybe the first version of your question contained the code that produced the exception, and you haven’t updated the exception message after updating the code.
    • (Possibly your Java version had a bug where three XXX parsed just +00 (the hours) and left the final 00 unparsed, after which XXXX couldn’t do anything. It’s not a bug I’ve heard of, so pure speculation and not really likely. If this were the problem, just swapping [XXX] and [XXXX] should remedy it.)

    Edit: Do you know what can I want to get it deserialized in the same format as the string, just to serialize back and compare the two strings and look that they are equal?

    1. Do that test the other way around: Serialize an OffsetDateTime, deserialize it back and check that the two OffsetDateTime objects are equal.
    2. If you insist, use lower case xx in the format pattern string. This prevents offset zero being rendered as Z. Meaning that 2017-04-04T08:04+0000 will be printed back, the same string as you started out from.