Support parsing long millisecond timestamps in InstantFormatter

This commit adds support of parsing a simple long from a String and
turning it to an `Instant` by considering it represents a timestamp in
milliseconds (see `Instant.ofEpochMilli`). Failing to parse a long from
the String, the previous algorithm is used: first check for an RFC-1123
representation then an ISO_INSTANT representation.

See gh-30312
Closes gh-30546
This commit is contained in:
Remus Richard Dumitrache
2023-05-26 12:08:26 +02:00
committed by GitHub
parent 7150c23e93
commit 4d8f6c1b41
3 changed files with 49 additions and 6 deletions

View File

@@ -621,6 +621,19 @@ class DateTimeFormattingTests {
.hasMessageStartingWith("Text '210302'")
.hasNoCause();
}
@Test
void testBindInstantAsLongEpochMillis() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("instant", 1234L);
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getRawFieldValue("instant"))
.isInstanceOf(Instant.class)
.isEqualTo(Instant.ofEpochMilli(1234L));
assertThat(binder.getBindingResult().getFieldValue("instant"))
.hasToString("1970-01-01T00:00:01.234Z");
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.format.datetime.standard;
import java.text.ParseException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Random;
import java.util.stream.Stream;
@@ -79,6 +80,16 @@ class InstantFormatterTests {
assertThat(actual).isEqualTo(expected);
}
@ParameterizedTest
@ArgumentsSource(RandomEpochMillisProvider.class)
void should_parse_into_an_Instant_from_epoch_mili(Instant input) throws ParseException {
Instant expected = input;
Instant actual = instantFormatter.parse(Long.toString(input.toEpochMilli()), null);
assertThat(actual).isEqualTo(expected);
}
private static class RandomInstantProvider implements ArgumentsProvider {
private static final long DATA_SET_SIZE = 10;
@@ -121,5 +132,19 @@ class InstantFormatterTests {
.map(DateTimeFormatter.RFC_1123_DATE_TIME.withZone(systemDefault())::format);
}
}
private static final class RandomEpochMillisProvider implements ArgumentsProvider {
private static final long DATA_SET_SIZE = 10;
private static final Random random = new Random();
@Override
public Stream<Arguments> provideArguments(ExtensionContext context) {
return random.longs(DATA_SET_SIZE, Long.MIN_VALUE, Long.MAX_VALUE)
.mapToObj(Instant::ofEpochMilli)
.map(instant -> instant.truncatedTo(ChronoUnit.MILLIS))
.map(Arguments::of);
}
}
}