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:
parent
7150c23e93
commit
4d8f6c1b41
|
@ -41,13 +41,18 @@ public class InstantFormatter implements Formatter<Instant> {
|
|||
|
||||
@Override
|
||||
public Instant parse(String text, Locale locale) throws ParseException {
|
||||
if (text.length() > 0 && Character.isAlphabetic(text.charAt(0))) {
|
||||
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
|
||||
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(text));
|
||||
try {
|
||||
return Instant.ofEpochMilli(Long.parseLong(text));
|
||||
}
|
||||
else {
|
||||
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
|
||||
return Instant.parse(text);
|
||||
catch (NumberFormatException ex) {
|
||||
if (text.length() > 0 && Character.isAlphabetic(text.charAt(0))) {
|
||||
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
|
||||
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(text));
|
||||
}
|
||||
else {
|
||||
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
|
||||
return Instant.parse(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue