4

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();
Steve
  • 4,457
  • 12
  • 48
  • 89
  • 4
    Well 2017101800000700 doesn't fit yyyyMMddHHmmssSSS - you haven't got enough digits. (After the date, there's 00000700, giving HHmmss of 000007, leaving only 00 for SSS.) – Jon Skeet Oct 23 '17 at 21:59
  • 2
    But I suspect the 0700 is actually meant to be a time zone offset from UTC. – Jon Skeet Oct 23 '17 at 22:00
  • 1
    @JonSkeet The only strange thing (if 0700 is an offset) is the lack of a `+` or `-` sign, so it'd be an ambiguous offset. Or the absence of a sign means positive? –  Oct 23 '17 at 23:44
  • 1
    @Hugo: agreed, that's pretty odd. – Jon Skeet Oct 24 '17 at 04:56

1 Answers1

3

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.