Skip to content

Commit

Permalink
Enable ResolverStyle.STRICT for java formatters (#46675)
Browse files Browse the repository at this point in the history
Joda was using ResolverStyle.STRICT when parsing. This means that date will be validated to be a correct year, year-of-month, day-of-month
However, we also want to make it works with Year-Of-Era as Joda used to, hence custom temporalquery.localdate in DateFormatters.from
Within DateFormatters we use the correct uuuu year instead of yyyy year of era

worth noting: if yyyy(without an era) is used in code, the parsing result will be a TemporalAccessor which will fail to be converted into LocalDate. We mostly use DateFormatters.from so this takes care of this. If possible the uuuu format should be used.
  • Loading branch information
pgomulka authored Oct 11, 2019
1 parent f68b036 commit da44943
Show file tree
Hide file tree
Showing 7 changed files with 449 additions and 223 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ Function<String, ZonedDateTime> getFunction(String format, ZoneId zoneId, Locale
final DateFormatter formatter = dateFormatter;
return text -> {
TemporalAccessor accessor = formatter.parse(text);
// if there is no year, we fall back to the current one and
// if there is no year nor year-of-era, we fall back to the current one and
// fill the rest of the date up with the parsed date
if (accessor.isSupported(ChronoField.YEAR) == false) {
if (accessor.isSupported(ChronoField.YEAR) == false && accessor.isSupported(ChronoField.YEAR_OF_ERA) == false ) {
int year = LocalDate.now(ZoneOffset.UTC).getYear();
ZonedDateTime newTime = Instant.EPOCH.atZone(ZoneOffset.UTC).withYear(year);
for (ChronoField field : FIELDS) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,22 @@ public void testParseJava() {
equalTo("11 24 01:29:01"));
}

public void testParseJavaWithTimeZone() {
public void testParseYearOfEraJavaWithTimeZone() {
Function<String, ZonedDateTime> javaFunction = DateFormat.Java.getFunction("yyyy-MM-dd'T'HH:mm:ss.SSSZZ",
ZoneOffset.UTC, Locale.ROOT);
ZonedDateTime datetime = javaFunction.apply("2018-02-05T13:44:56.657+0100");
String expectedDateTime = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withZone(ZoneOffset.UTC).format(datetime);
assertThat(expectedDateTime, is("2018-02-05T12:44:56.657Z"));
}

public void testParseYearJavaWithTimeZone() {
Function<String, ZonedDateTime> javaFunction = DateFormat.Java.getFunction("uuuu-MM-dd'T'HH:mm:ss.SSSZZ",
ZoneOffset.UTC, Locale.ROOT);
ZonedDateTime datetime = javaFunction.apply("2018-02-05T13:44:56.657+0100");
String expectedDateTime = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withZone(ZoneOffset.UTC).format(datetime);
assertThat(expectedDateTime, is("2018-02-05T12:44:56.657Z"));
}

public void testParseJavaDefaultYear() {
String format = randomFrom("8dd/MM", "dd/MM");
ZoneId timezone = DateUtils.of("Europe/Amsterdam");
Expand Down
638 changes: 426 additions & 212 deletions server/src/main/java/org/elasticsearch/common/time/DateFormatters.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,10 @@ public void testIso8601Parsers() {
assertSameDate("2018-10-10T10:11:12,123+05:30", format, jodaFormatter, javaFormatter);
}

public void testParsingLocalDateFromYearOfEra(){
//with strict resolving, YearOfEra expect an era, otherwise it won't resolve to a date
assertSameDate("2018363","yyyyDDD",Joda.forPattern("YYYYDDD"),DateFormatter.forPattern("uuuuDDD"));
}
public void testParsingMissingTimezone() {
long millisJava = DateFormatter.forPattern("8yyyy-MM-dd HH:mm:ss").parseMillis("2018-02-18 17:47:17");
long millisJoda = DateFormatter.forPattern("yyyy-MM-dd HH:mm:ss").parseMillis("2018-02-18 17:47:17");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstStart() throws Excep
List<IndexRequestBuilder> builders = new ArrayList<>();

ZoneId timezone = ZoneId.of("CET");
DateFormatter formatter = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(timezone);
DateFormatter formatter = DateFormatter.forPattern("uuuu-MM-dd'T'HH:mm:ss").withZone(timezone);
// epoch millis: 1332547200000
addNTimes(1, IDX_DST_START, DateFormatters.from(formatter.parse("2012-03-24T01:00:00")), builders);
// day with dst shift, only 23h long
Expand All @@ -223,7 +223,7 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstStart() throws Excep
List<? extends Bucket> buckets = deriv.getBuckets();
assertThat(buckets.size(), equalTo(4));

DateFormatter dateFormatter = DateFormatter.forPattern("yyyy-MM-dd");
DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd");
ZonedDateTime expectedKeyFirstBucket =
LocalDate.from(dateFormatter.parse("2012-03-24")).atStartOfDay(timezone).withZoneSameInstant(ZoneOffset.UTC);
assertBucket(buckets.get(0), expectedKeyFirstBucket, 1L, nullValue(), null, null);
Expand All @@ -250,7 +250,7 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstEnd() throws Excepti
ZoneId timezone = ZoneId.of("CET");
List<IndexRequestBuilder> builders = new ArrayList<>();

DateFormatter formatter = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(timezone);
DateFormatter formatter = DateFormatter.forPattern("uuuu-MM-dd'T'HH:mm:ss").withZone(timezone);
addNTimes(1, IDX_DST_END, DateFormatters.from(formatter.parse("2012-10-27T01:00:00")), builders);
// day with dst shift -1h, 25h long
addNTimes(2, IDX_DST_END, DateFormatters.from(formatter.parse("2012-10-28T01:00:00")), builders);
Expand All @@ -274,7 +274,7 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstEnd() throws Excepti
List<? extends Bucket> buckets = deriv.getBuckets();
assertThat(buckets.size(), equalTo(4));

DateFormatter dateFormatter = DateFormatter.forPattern("yyyy-MM-dd").withZone(ZoneOffset.UTC);
DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd").withZone(ZoneOffset.UTC);

ZonedDateTime expectedKeyFirstBucket =
LocalDate.from(dateFormatter.parse("2012-10-27")).atStartOfDay(timezone).withZoneSameInstant(ZoneOffset.UTC);
Expand Down Expand Up @@ -303,7 +303,7 @@ public void testSingleValuedFieldNormalised_timeZone_AsiaKathmandu() throws Exce
ZoneId timezone = ZoneId.of("Asia/Kathmandu");
List<IndexRequestBuilder> builders = new ArrayList<>();

DateFormatter formatter = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(timezone);
DateFormatter formatter = DateFormatter.forPattern("uuuu-MM-dd'T'HH:mm:ss").withZone(timezone);
addNTimes(1, IDX_DST_KATHMANDU, DateFormatters.from(formatter.parse("1985-12-31T22:30:00")), builders);
// the shift happens during the next bucket, which includes the 45min that do not start on the full hour
addNTimes(2, IDX_DST_KATHMANDU, DateFormatters.from(formatter.parse("1985-12-31T23:30:00")), builders);
Expand All @@ -327,7 +327,7 @@ public void testSingleValuedFieldNormalised_timeZone_AsiaKathmandu() throws Exce
List<? extends Bucket> buckets = deriv.getBuckets();
assertThat(buckets.size(), equalTo(4));

DateFormatter dateFormatter = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC);
DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC);

ZonedDateTime expectedKeyFirstBucket =
LocalDateTime.from(dateFormatter.parse("1985-12-31T22:00:00")).atZone(timezone).withZoneSameInstant(ZoneOffset.UTC);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

public class IndexLifecycleOriginationDateParser {

private static final DateFormatter DATE_FORMATTER = DateFormatter.forPattern("yyyy.MM.dd");
private static final DateFormatter DATE_FORMATTER = DateFormatter.forPattern("uuuu.MM.dd");
private static final String INDEX_NAME_REGEX = "^.*-(\\d{4}.\\d{2}.\\d{2})(-[\\d]+)?$";
private static final Pattern INDEX_NAME_PATTERN = Pattern.compile(INDEX_NAME_REGEX);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

public class IndexLifecycleOriginationDateParserTests extends ESTestCase {

private static final DateFormatter dateFormatter = DateFormatter.forPattern("yyyy.MM.dd");
private static final DateFormatter dateFormatter = DateFormatter.forPattern("uuuu.MM.dd");

public void testShouldParseIndexNameReturnsFalseWhenOriginationDateIsSet() {
Settings settings = Settings.builder()
Expand Down

0 comments on commit da44943

Please sign in to comment.