Core: Remove parseDefaulting from DateFormatter (#36386)
This commit removes the parseDefaulting method from DateFormatter, bringing it more inline with the joda equivalent FormatDateTimeFormatter. This method was only needed for the java time implementation of DateMathParser. Instead, a DateFormatter now returns an implementation of DateMathParser for the given format, allowing the java time implementation to construct the appropriate date math parser internally.
This commit is contained in:
parent
9e8cfbb40d
commit
a998f4dec6
|
@ -30,7 +30,6 @@ import org.elasticsearch.common.regex.Regex;
|
||||||
import org.elasticsearch.common.time.DateFormatter;
|
import org.elasticsearch.common.time.DateFormatter;
|
||||||
import org.elasticsearch.common.time.DateFormatters;
|
import org.elasticsearch.common.time.DateFormatters;
|
||||||
import org.elasticsearch.common.time.DateMathParser;
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.time.JavaDateMathParser;
|
|
||||||
import org.elasticsearch.common.util.set.Sets;
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.index.IndexNotFoundException;
|
import org.elasticsearch.index.IndexNotFoundException;
|
||||||
|
@ -921,7 +920,7 @@ public class IndexNameExpressionResolver {
|
||||||
dateFormatter = DateFormatters.forPattern(dateFormatterPattern);
|
dateFormatter = DateFormatters.forPattern(dateFormatterPattern);
|
||||||
}
|
}
|
||||||
DateFormatter formatter = dateFormatter.withZone(timeZone);
|
DateFormatter formatter = dateFormatter.withZone(timeZone);
|
||||||
DateMathParser dateMathParser = new JavaDateMathParser(formatter);
|
DateMathParser dateMathParser = formatter.toDateMathParser();
|
||||||
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false, timeZone);
|
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false, timeZone);
|
||||||
|
|
||||||
String time = formatter.format(Instant.ofEpochMilli(millis));
|
String time = formatter.format(Instant.ofEpochMilli(millis));
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
|
|
||||||
package org.elasticsearch.common.time;
|
package org.elasticsearch.common.time;
|
||||||
|
|
||||||
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
|
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.temporal.TemporalAccessor;
|
import java.time.temporal.TemporalAccessor;
|
||||||
import java.time.temporal.TemporalField;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public interface DateFormatter {
|
public interface DateFormatter {
|
||||||
|
@ -86,13 +86,9 @@ public interface DateFormatter {
|
||||||
ZoneId getZone();
|
ZoneId getZone();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a formatter using default fields for a TemporalAccessor that should be used in case
|
* Return a {@link DateMathParser} built from this formatter.
|
||||||
* the supplied date is not having all of those fields
|
|
||||||
*
|
|
||||||
* @param fields A <code>Map<TemporalField, Long></code> of fields to be used as fallbacks
|
|
||||||
* @return A new date formatter instance, that will use those fields during parsing
|
|
||||||
*/
|
*/
|
||||||
DateFormatter parseDefaulting(Map<TemporalField, Long> fields);
|
DateMathParser toDateMathParser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge several date formatters into a single one. Useful if you need to have several formatters with
|
* Merge several date formatters into a single one. Useful if you need to have several formatters with
|
||||||
|
@ -102,7 +98,7 @@ public interface DateFormatter {
|
||||||
* @param formatters The list of date formatters to be merged together
|
* @param formatters The list of date formatters to be merged together
|
||||||
* @return The new date formtter containing the specified date formatters
|
* @return The new date formtter containing the specified date formatters
|
||||||
*/
|
*/
|
||||||
static DateFormatter merge(DateFormatter ... formatters) {
|
static DateFormatter merge(DateFormatter... formatters) {
|
||||||
return new MergedDateFormatter(formatters);
|
return new MergedDateFormatter(formatters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,10 +106,12 @@ public interface DateFormatter {
|
||||||
|
|
||||||
private final String format;
|
private final String format;
|
||||||
private final DateFormatter[] formatters;
|
private final DateFormatter[] formatters;
|
||||||
|
private final DateMathParser[] dateMathParsers;
|
||||||
|
|
||||||
MergedDateFormatter(DateFormatter ... formatters) {
|
MergedDateFormatter(DateFormatter... formatters) {
|
||||||
this.formatters = formatters;
|
this.formatters = formatters;
|
||||||
this.format = Arrays.stream(formatters).map(DateFormatter::pattern).collect(Collectors.joining("||"));
|
this.format = Arrays.stream(formatters).map(DateFormatter::pattern).collect(Collectors.joining("||"));
|
||||||
|
this.dateMathParsers = Arrays.stream(formatters).map(DateFormatter::toDateMathParser).toArray(DateMathParser[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -164,8 +162,22 @@ public interface DateFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
|
public DateMathParser toDateMathParser() {
|
||||||
return new MergedDateFormatter(Arrays.stream(formatters).map(f -> f.parseDefaulting(fields)).toArray(DateFormatter[]::new));
|
return (text, now, roundUp, tz) -> {
|
||||||
|
ElasticsearchParseException failure = null;
|
||||||
|
for (DateMathParser parser : dateMathParsers) {
|
||||||
|
try {
|
||||||
|
return parser.parse(text, now, roundUp, tz);
|
||||||
|
} catch (ElasticsearchParseException e) {
|
||||||
|
if (failure == null) {
|
||||||
|
failure = e;
|
||||||
|
} else {
|
||||||
|
failure.addSuppressed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw failure;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,7 @@ import java.time.ZoneId;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.temporal.TemporalAccessor;
|
import java.time.temporal.TemporalAccessor;
|
||||||
import java.time.temporal.TemporalField;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,7 +40,8 @@ import java.util.regex.Pattern;
|
||||||
class EpochMillisDateFormatter implements DateFormatter {
|
class EpochMillisDateFormatter implements DateFormatter {
|
||||||
|
|
||||||
private static final Pattern SPLIT_BY_DOT_PATTERN = Pattern.compile("\\.");
|
private static final Pattern SPLIT_BY_DOT_PATTERN = Pattern.compile("\\.");
|
||||||
static DateFormatter INSTANCE = new EpochMillisDateFormatter();
|
static final DateFormatter INSTANCE = new EpochMillisDateFormatter();
|
||||||
|
static final DateMathParser DATE_MATH_INSTANCE = new JavaDateMathParser(INSTANCE, INSTANCE);
|
||||||
|
|
||||||
private EpochMillisDateFormatter() {
|
private EpochMillisDateFormatter() {
|
||||||
}
|
}
|
||||||
|
@ -103,11 +102,6 @@ class EpochMillisDateFormatter implements DateFormatter {
|
||||||
return "epoch_millis";
|
return "epoch_millis";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Locale getLocale() {
|
public Locale getLocale() {
|
||||||
return Locale.ROOT;
|
return Locale.ROOT;
|
||||||
|
@ -117,4 +111,9 @@ class EpochMillisDateFormatter implements DateFormatter {
|
||||||
public ZoneId getZone() {
|
public ZoneId getZone() {
|
||||||
return ZoneOffset.UTC;
|
return ZoneOffset.UTC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateMathParser toDateMathParser() {
|
||||||
|
return DATE_MATH_INSTANCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,13 @@ import java.time.ZoneId;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.temporal.TemporalAccessor;
|
import java.time.temporal.TemporalAccessor;
|
||||||
import java.time.temporal.TemporalField;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class EpochSecondsDateFormatter implements DateFormatter {
|
public class EpochSecondsDateFormatter implements DateFormatter {
|
||||||
|
|
||||||
public static DateFormatter INSTANCE = new EpochSecondsDateFormatter();
|
public static DateFormatter INSTANCE = new EpochSecondsDateFormatter();
|
||||||
|
static final DateMathParser DATE_MATH_INSTANCE = new JavaDateMathParser(INSTANCE, INSTANCE);
|
||||||
private static final Pattern SPLIT_BY_DOT_PATTERN = Pattern.compile("\\.");
|
private static final Pattern SPLIT_BY_DOT_PATTERN = Pattern.compile("\\.");
|
||||||
|
|
||||||
private EpochSecondsDateFormatter() {}
|
private EpochSecondsDateFormatter() {}
|
||||||
|
@ -91,6 +90,11 @@ public class EpochSecondsDateFormatter implements DateFormatter {
|
||||||
return ZoneOffset.UTC;
|
return ZoneOffset.UTC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateMathParser toDateMathParser() {
|
||||||
|
return DATE_MATH_INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DateFormatter withZone(ZoneId zoneId) {
|
public DateFormatter withZone(ZoneId zoneId) {
|
||||||
if (zoneId.equals(ZoneOffset.UTC) == false) {
|
if (zoneId.equals(ZoneOffset.UTC) == false) {
|
||||||
|
@ -106,9 +110,4 @@ public class EpochSecondsDateFormatter implements DateFormatter {
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,28 @@ import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.time.temporal.ChronoField;
|
||||||
import java.time.temporal.TemporalAccessor;
|
import java.time.temporal.TemporalAccessor;
|
||||||
import java.time.temporal.TemporalField;
|
import java.time.temporal.TemporalField;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
class JavaDateFormatter implements DateFormatter {
|
class JavaDateFormatter implements DateFormatter {
|
||||||
|
|
||||||
|
// base fields which should be used for default parsing, when we round up for date math
|
||||||
|
private static final Map<TemporalField, Long> ROUND_UP_BASE_FIELDS = new HashMap<>(6);
|
||||||
|
{
|
||||||
|
ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L);
|
||||||
|
ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L);
|
||||||
|
ROUND_UP_BASE_FIELDS.put(ChronoField.HOUR_OF_DAY, 23L);
|
||||||
|
ROUND_UP_BASE_FIELDS.put(ChronoField.MINUTE_OF_HOUR, 59L);
|
||||||
|
ROUND_UP_BASE_FIELDS.put(ChronoField.SECOND_OF_MINUTE, 59L);
|
||||||
|
ROUND_UP_BASE_FIELDS.put(ChronoField.MILLI_OF_SECOND, 999L);
|
||||||
|
}
|
||||||
|
|
||||||
private final String format;
|
private final String format;
|
||||||
private final DateTimeFormatter printer;
|
private final DateTimeFormatter printer;
|
||||||
private final DateTimeFormatter[] parsers;
|
private final DateTimeFormatter[] parsers;
|
||||||
|
@ -116,8 +129,7 @@ class JavaDateFormatter implements DateFormatter {
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
JavaDateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
|
||||||
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
|
|
||||||
final DateTimeFormatterBuilder parseDefaultingBuilder = new DateTimeFormatterBuilder().append(printer);
|
final DateTimeFormatterBuilder parseDefaultingBuilder = new DateTimeFormatterBuilder().append(printer);
|
||||||
fields.forEach(parseDefaultingBuilder::parseDefaulting);
|
fields.forEach(parseDefaultingBuilder::parseDefaulting);
|
||||||
if (parsers.length == 1 && parsers[0].equals(printer)) {
|
if (parsers.length == 1 && parsers[0].equals(printer)) {
|
||||||
|
@ -143,6 +155,11 @@ class JavaDateFormatter implements DateFormatter {
|
||||||
return this.printer.getZone();
|
return this.printer.getZone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateMathParser toDateMathParser() {
|
||||||
|
return new JavaDateMathParser(this, this.parseDefaulting(ROUND_UP_BASE_FIELDS));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(getLocale(), printer.getZone(), format);
|
return Objects.hash(getLocale(), printer.getZone(), format);
|
||||||
|
|
|
@ -31,10 +31,7 @@ import java.time.ZonedDateTime;
|
||||||
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoField;
|
||||||
import java.time.temporal.TemporalAccessor;
|
import java.time.temporal.TemporalAccessor;
|
||||||
import java.time.temporal.TemporalAdjusters;
|
import java.time.temporal.TemporalAdjusters;
|
||||||
import java.time.temporal.TemporalField;
|
|
||||||
import java.time.temporal.TemporalQueries;
|
import java.time.temporal.TemporalQueries;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.LongSupplier;
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
|
@ -47,24 +44,15 @@ import java.util.function.LongSupplier;
|
||||||
*/
|
*/
|
||||||
public class JavaDateMathParser implements DateMathParser {
|
public class JavaDateMathParser implements DateMathParser {
|
||||||
|
|
||||||
// base fields which should be used for default parsing, when we round up
|
|
||||||
private static final Map<TemporalField, Long> ROUND_UP_BASE_FIELDS = new HashMap<>(6);
|
|
||||||
{
|
|
||||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L);
|
|
||||||
ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L);
|
|
||||||
ROUND_UP_BASE_FIELDS.put(ChronoField.HOUR_OF_DAY, 23L);
|
|
||||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MINUTE_OF_HOUR, 59L);
|
|
||||||
ROUND_UP_BASE_FIELDS.put(ChronoField.SECOND_OF_MINUTE, 59L);
|
|
||||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MILLI_OF_SECOND, 999L);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final DateFormatter formatter;
|
private final DateFormatter formatter;
|
||||||
private final DateFormatter roundUpFormatter;
|
private final DateFormatter roundUpFormatter;
|
||||||
|
|
||||||
public JavaDateMathParser(DateFormatter formatter) {
|
public JavaDateMathParser(DateFormatter formatter, DateFormatter roundUpFormatter) {
|
||||||
Objects.requireNonNull(formatter);
|
Objects.requireNonNull(formatter);
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
this.roundUpFormatter = formatter.parseDefaulting(ROUND_UP_BASE_FIELDS);
|
this.roundUpFormatter = roundUpFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -36,7 +36,7 @@ import static org.hamcrest.Matchers.is;
|
||||||
public class JavaDateMathParserTests extends ESTestCase {
|
public class JavaDateMathParserTests extends ESTestCase {
|
||||||
|
|
||||||
private final DateFormatter formatter = DateFormatters.forPattern("dateOptionalTime||epoch_millis");
|
private final DateFormatter formatter = DateFormatters.forPattern("dateOptionalTime||epoch_millis");
|
||||||
private final JavaDateMathParser parser = new JavaDateMathParser(formatter);
|
private final DateMathParser parser = formatter.toDateMathParser();
|
||||||
|
|
||||||
public void testBasicDates() {
|
public void testBasicDates() {
|
||||||
assertDateMathEquals("2014", "2014-01-01T00:00:00.000");
|
assertDateMathEquals("2014", "2014-01-01T00:00:00.000");
|
||||||
|
@ -139,7 +139,7 @@ public class JavaDateMathParserTests extends ESTestCase {
|
||||||
public void testRoundingPreservesEpochAsBaseDate() {
|
public void testRoundingPreservesEpochAsBaseDate() {
|
||||||
// If a user only specifies times, then the date needs to always be 1970-01-01 regardless of rounding
|
// If a user only specifies times, then the date needs to always be 1970-01-01 regardless of rounding
|
||||||
DateFormatter formatter = DateFormatters.forPattern("HH:mm:ss");
|
DateFormatter formatter = DateFormatters.forPattern("HH:mm:ss");
|
||||||
JavaDateMathParser parser = new JavaDateMathParser(formatter);
|
DateMathParser parser = formatter.toDateMathParser();
|
||||||
ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(formatter.parse("04:52:20"));
|
ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(formatter.parse("04:52:20"));
|
||||||
assertThat(zonedDateTime.getYear(), is(1970));
|
assertThat(zonedDateTime.getYear(), is(1970));
|
||||||
long millisStart = zonedDateTime.toInstant().toEpochMilli();
|
long millisStart = zonedDateTime.toInstant().toEpochMilli();
|
||||||
|
@ -165,7 +165,7 @@ public class JavaDateMathParserTests extends ESTestCase {
|
||||||
|
|
||||||
// implicit rounding with explicit timezone in the date format
|
// implicit rounding with explicit timezone in the date format
|
||||||
DateFormatter formatter = DateFormatters.forPattern("yyyy-MM-ddXXX");
|
DateFormatter formatter = DateFormatters.forPattern("yyyy-MM-ddXXX");
|
||||||
JavaDateMathParser parser = new JavaDateMathParser(formatter);
|
DateMathParser parser = formatter.toDateMathParser();
|
||||||
long time = parser.parse("2011-10-09+01:00", () -> 0, false, (ZoneId) null);
|
long time = parser.parse("2011-10-09+01:00", () -> 0, false, (ZoneId) null);
|
||||||
assertEquals(this.parser.parse("2011-10-09T00:00:00.000+01:00", () -> 0), time);
|
assertEquals(this.parser.parse("2011-10-09T00:00:00.000+01:00", () -> 0), time);
|
||||||
time = parser.parse("2011-10-09+01:00", () -> 0, true, (ZoneId) null);
|
time = parser.parse("2011-10-09+01:00", () -> 0, true, (ZoneId) null);
|
||||||
|
@ -239,7 +239,7 @@ public class JavaDateMathParserTests extends ESTestCase {
|
||||||
assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000");
|
assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000");
|
||||||
|
|
||||||
// also check other time units
|
// also check other time units
|
||||||
JavaDateMathParser parser = new JavaDateMathParser(DateFormatters.forPattern("epoch_second||dateOptionalTime"));
|
DateMathParser parser = DateFormatters.forPattern("epoch_second||dateOptionalTime").toDateMathParser();
|
||||||
long datetime = parser.parse("1418248078", () -> 0);
|
long datetime = parser.parse("1418248078", () -> 0);
|
||||||
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
|
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue