Skip to content

Commit

Permalink
[7.17] Fix date rounding for date math parsing backport(#90458) (#90635)
Browse files Browse the repository at this point in the history
* Fix date rounding for date math parsing (#90458)

in #89693 the rounding logic was only applied when a field was present on a pattern. This is incorrect as for dates like "2020" we want to default to "2020-01-01T23:59:59.999..." when rounding is enabled.

This commit always applies monthOfYear or dayofMonth defaulting (when rounding enabled) except when the dayOfYear is set
closes #90187
backport(#90458)
(cherry picked from commit 3f3a95e)
  • Loading branch information
pgomulka committed Oct 4, 2022
1 parent 974669e commit 75df484
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 3 deletions.
6 changes: 6 additions & 0 deletions docs/changelog/90458.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 90458
summary: Fix date rounding for date math parsing
area: Infra/Core
type: bug
issues:
- 90187
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
setup:
- skip:
version: " - 8.5.99"
reason: awaits backports
- do:
indices.create:
index: dates_year_only
body:
mappings:
properties:
date:
type: date
format: uuuu

- do:
bulk:
refresh: true
body:
- '{ "index" : { "_index" : "dates_year_only", "_id" : "first" } }'
- '{"date" : "1900", "field" : 1 }'
- '{ "index" : { "_index" : "dates_year_only", "_id" : "second" } }'
- '{"date" : "2022", "field" : 1 }'
- '{ "index" : { "_index" : "dates_year_only", "_id" : "third" } }'
- '{"date" : "2022", "field" : 2 }'
- '{ "index" : { "_index" : "dates_year_only", "_id" : "fourth" } }'
- '{"date" : "1500", "field" : 2 }'

- do:
indices.create:
index: dates
body:
mappings:
properties:
date:
type: date

- do:
bulk:
refresh: true
body:
- '{ "index" : { "_index" : "dates", "_id" : "first" } }'
- '{"date" : "1900-01-01T12:12:12.123456789Z", "field" : 1 }'
- '{ "index" : { "_index" : "dates", "_id" : "second" } }'
- '{"date" : "2022-01-01T12:12:12.123456789Z", "field" : 1 }'
- '{ "index" : { "_index" : "dates", "_id" : "third" } }'
- '{"date" : "2022-01-03T12:12:12.123456789Z", "field" : 2 }'
- '{ "index" : { "_index" : "dates", "_id" : "fourth" } }'
- '{"date" : "1500-01-01T12:12:12.123456789Z", "field" : 2 }'
- '{ "index" : { "_index" : "dates", "_id" : "fifth" } }'
- '{"date" : "1500-01-05T12:12:12.123456789Z", "field" : 2 }'

---
"test range query for all docs with year uuuu":
- do:
search:
rest_total_hits_as_int: true
index: dates
body:
query:
range:
date:
gte: 1000
lte: 2023
format: uuuu

- match: { hits.total: 5 }
- length: { hits.hits: 5 }

---
"test match query gte and lt for single result with year uuuu":
- do:
search:
rest_total_hits_as_int: true
index: dates
body:
query:
range:
date:
gte: 1500 #1500-01-01T00:00:00
lte: 1500 #1500-01-01T23:59:59
format: uuuu

- match: { hits.total: 1 }
- length: { hits.hits: 1 }
- match: { hits.hits.0._id: "fourth" }

---
"test match query gte and lte with year uuuu":
- do:
search:
rest_total_hits_as_int: true
index: dates
body:
query:
range:
date:
gte: 1500
lte: 2000
format: uuuu

- match: { hits.total: 3 }
- length: { hits.hits: 3 }
- match: { hits.hits.0._id: "first" }
- match: { hits.hits.1._id: "fourth" }
- match: { hits.hits.2._id: "fifth" }

---
"test match query with year uuuu":
- do:
search:
rest_total_hits_as_int: true
index: dates_year_only
body:
query:
match:
date:
query: "1500"

- match: { hits.total: 1 }
- length: { hits.hits: 1 }
- match: { hits.hits.0._id: "fourth" }
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -41,12 +42,17 @@ class JavaDateFormatter implements DateFormatter {
*/
private static final BiConsumer<DateTimeFormatterBuilder, DateTimeFormatter> DEFAULT_ROUND_UP = (builder, parser) -> {
String parserAsString = parser.toString();
if (parserAsString.contains(ChronoField.MONTH_OF_YEAR.toString())) {
if (parserAsString.contains(ChronoField.DAY_OF_YEAR.toString())) {
builder.parseDefaulting(ChronoField.DAY_OF_YEAR, 1L);
// TODO ideally we should make defaulting for weekbased year here too,
// but this will not work when locale is changed
// weekbased rounding relies on DateFormatters#localDateFromWeekBasedDate
// Applying month of year or dayOfMonth when weekbased fields are used will result in a conflict
} else if (parserAsString.contains(IsoFields.WEEK_BASED_YEAR.toString()) == false) {
builder.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1L);
}
if (parserAsString.contains(ChronoField.DAY_OF_MONTH.toString())) {
builder.parseDefaulting(ChronoField.DAY_OF_MONTH, 1L);
}

if (parserAsString.contains(ChronoField.CLOCK_HOUR_OF_AMPM.toString())) {
builder.parseDefaulting(ChronoField.CLOCK_HOUR_OF_AMPM, 11L);
builder.parseDefaulting(ChronoField.AMPM_OF_DAY, 1L);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,26 @@ public void testRoundupFormatterWithEpochDates() {
assertRoundupFormatter("uuuu-MM-dd'T'HH:mm:ss.SSS||epoch_second", "1234567890", 1234567890999L);
}

public void testYearWithoutMonthRoundUp() {
assertRoundupFormatter("1500", "1500-01-01T23:59:59.999", "uuuu");
assertRoundupFormatter("2022", "2022-01-01T23:59:59.999", "uuuu");
assertRoundupFormatter("2022", "2022-01-01T23:59:59.999", "yyyy");
assumeFalse(
"won't work in jdk8 " + "because SPI mechanism is not looking at classpath - needs ISOCalendarDataProvider in jre's ext/libs",
JavaVersion.current().equals(JavaVersion.parse("8"))
);
// cannot reliably default week based years due to locale changing. This is always using the same locale anyway
// See JavaDateFormatter javadocs
assertRoundupFormatter("2022", "2022-01-03T23:59:59.999", "YYYY");
}

private void assertRoundupFormatter(String input, String expectedDate, String format) {
long expectedMillis = DateFormatters.from(DateFormatter.forPattern("strict_date_optional_time").parse(expectedDate))
.toInstant()
.toEpochMilli();
assertRoundupFormatter(format, input, expectedMillis);
}

private void assertRoundupFormatter(String format, String input, long expectedMilliSeconds) {
JavaDateFormatter dateFormatter = (JavaDateFormatter) DateFormatter.forPattern(format);
dateFormatter.parse(input);
Expand Down

0 comments on commit 75df484

Please sign in to comment.