Search code examples
javagsonjava-timedatetime-parsinglocaldate

java.time.format.DateTimeParseException could not be parsed at index 0


I am trying to tell Gson how to parse LocalDateTime and LocalDate, but I'm getting this error, which looks to me like it should match the format. I'm thinking there's either something I don't understand about parsing dates or something I don't understand about Gson.

java.time.format.DateTimeParseException: Text '2017101800000700' could not be parsed at index 0

Gson gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
    @Override
    public LocalDateTime deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        return LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
    }
  }).registerTypeAdapter(LocalDate.class, new JsonDeserializer<LocalDate>() {
    @Override
    public LocalDate deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        return LocalDate.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ofPattern("yyyyMMdd"));
    }
  }).create();

Solution

  • As @Jon Skeet said in the comments, your pattern has 1 extra digit when compared to the input string, so yyyyMMddHHmmssSSS won't work: the input 2017101800000700 has 16 digits, while the pattern yyyyMMddHHmmssSSS expects 17.


    Although the last part (0700) looks like an UTC offset, it's missing a + or - sign (so it should be +0700 or -0700). The offset represents the difference from UTC, and without a sign, it's ambiguous: you can't say if it's ahead or behind UTC.

    And even if it's really an offset, I couldn't find a way to parse without a sign: I tried with all the available options and none worked. A sign is always required, so parsing it as an offset is not possible, unless you make an arbitrary assumption (such as "it's positive") and change the input manually, like this:

    // assuming the offset "0700" is positive (7 hours ahead UTC)
    String dateStr = "2017101800000700";
    
    // insert the "+" manually, so input becomes 201710180000+0700
    dateStr = dateStr.substring(0, 12) + "+" + dateStr.substring(12, 16);
    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMddHHmmXX");
    System.out.println(LocalDateTime.parse(dateStr, fmt)); // 2017-10-18T00:00
    

    This will result in a LocalDateTime equals to:

    2017-10-18T00:00


    Another alternative is to treat 07 as seconds and the last 2 zeroes as fractions of second.

    In this case, a pattern such as yyyyMMddHHmmssSS won't work due to a bug in Java 8 API.

    The same link above also provides the workaround: use a java.time.format.DateTimeFormatterBuilder with a java.time.temporal.ChronoField for the fraction of seconds.

    String dateStr = "2017101800000700";
    DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        // date/time
        .appendPattern("yyyyMMddHHmmss")
        // milliseconds (with 2 digits)
        .appendValue(ChronoField.MILLI_OF_SECOND, 2)
        // create formatter
        .toFormatter();
    System.out.println(LocalDateTime.parse(dateStr, fmt)); // 2017-10-18T00:00:07
    

    This will parse the following LocalDateTime:

    2017-10-18T00:00:07

    Note that it's different from the previous one, because now we're considering 07 to be the seconds.