Skip to content

Add JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_TIMESTAMPS to allow parsing quoted numbers when using a custom DateTimeFormatter #263

@mpkorstanje

Description

@mpkorstanje

I have a scenario where I would like to de-serialize epoch milis, and serialize with a @JsonFormat annotation. In essence:

  final ObjectMapper mapper = new ObjectMapper()
      .disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)
      .disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
      .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
      .setTimeZone(TimeZone.getTimeZone("UTC"))
      .findAndRegisterModules();
  final Instant instant = Instant.ofEpochMilli(123456);

  @Test
  public void test() throws JsonProcessingException {
    assertEquals("{\"time\":\"1970-01-01T00:02:03.456+0000\"}", mapper.writeValueAsString(new TimeHolderWithFormat(instant)));
    assertEquals(instant, mapper.readValue("{\"time\":123456}", TimeHolder.class).time);
    assertEquals(instant, mapper.readValue("{\"time\":\"123456\"}", TimeHolder.class).time);
    assertEquals(instant, mapper.readValue("{\"time\":123456}", TimeHolderWithFormat.class).time);
    assertEquals(instant, mapper.readValue("{\"time\":\"123456\"}", TimeHolderWithFormat.class).time);
  }

  
  static class TimeHolder {
    private Instant time;
    public void setTime(Instant time) {
      this.time = time;
    }
    // Getter ommited
  }

  static class TimeHolderWithFormat {
    private Instant time;
    // Constructors ommited
    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    public void setTime(Instant time) {
      this.time = time;
    }
    // Getter ommited
  }

This will fail on the last assertion with:

com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.Instant` from String "123456": Failed to deserialize java.time.Instant: (java.time.format.DateTimeParseException) Text '123456' could not be parsed at index 0
 at [Source: (String)"{"time":"123456"}"; line: 1, column: 9] (through reference chain: com.example.app.AppTest$TimeHolderWithFormat["time"])

There is an apparent inconsistency in the way Jackson de-serializes numbers that are shaped as a string into an instant.

  • When not providing a custom date format, quoted numbers are treated as epoch milis/nanos
  • When not providing a custom date, quoted numbers are assumed to be handled by the pattern.

// only check for other parsing modes if we are using default formatter
if (_formatter == DateTimeFormatter.ISO_INSTANT ||
_formatter == DateTimeFormatter.ISO_OFFSET_DATE_TIME ||
_formatter == DateTimeFormatter.ISO_ZONED_DATE_TIME) {
// 22-Jan-2016, [datatype-jsr310#16]: Allow quoted numbers too
int dots = _countPeriods(string);
if (dots >= 0) { // negative if not simple number
try {
if (dots == 0) {
return _fromLong(ctxt, NumberInput.parseLong(string));
}

There is however no way to construct a pattern that handles both ISO dates and epoch milis/nanos. Would it be possible to add a feature toggle here?

Details:

Metadata

Metadata

Assignees

No one assigned

    Labels

    pr-welcomeIssue for which progress most likely if someone submits a Pull Request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions