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:
Ryan Ernst 2018-12-07 12:40:14 -08:00 committed by GitHub
parent 9e8cfbb40d
commit a998f4dec6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 50 deletions

View File

@ -30,7 +30,6 @@ import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.time.DateMathParser;
import org.elasticsearch.common.time.JavaDateMathParser;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
@ -921,7 +920,7 @@ public class IndexNameExpressionResolver {
dateFormatter = DateFormatters.forPattern(dateFormatterPattern);
}
DateFormatter formatter = dateFormatter.withZone(timeZone);
DateMathParser dateMathParser = new JavaDateMathParser(formatter);
DateMathParser dateMathParser = formatter.toDateMathParser();
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false, timeZone);
String time = formatter.format(Instant.ofEpochMilli(millis));

View File

@ -19,13 +19,13 @@
package org.elasticsearch.common.time;
import org.elasticsearch.ElasticsearchParseException;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
public interface DateFormatter {
@ -86,13 +86,9 @@ public interface DateFormatter {
ZoneId getZone();
/**
* Configure a formatter using default fields for a TemporalAccessor that should be used in case
* the supplied date is not having all of those fields
*
* @param fields A <code>Map&lt;TemporalField, Long&gt;</code> of fields to be used as fallbacks
* @return A new date formatter instance, that will use those fields during parsing
* Return a {@link DateMathParser} built from this formatter.
*/
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
@ -102,7 +98,7 @@ public interface DateFormatter {
* @param formatters The list of date formatters to be merged together
* @return The new date formtter containing the specified date formatters
*/
static DateFormatter merge(DateFormatter ... formatters) {
static DateFormatter merge(DateFormatter... formatters) {
return new MergedDateFormatter(formatters);
}
@ -110,10 +106,12 @@ public interface DateFormatter {
private final String format;
private final DateFormatter[] formatters;
private final DateMathParser[] dateMathParsers;
MergedDateFormatter(DateFormatter ... formatters) {
MergedDateFormatter(DateFormatter... formatters) {
this.formatters = formatters;
this.format = Arrays.stream(formatters).map(DateFormatter::pattern).collect(Collectors.joining("||"));
this.dateMathParsers = Arrays.stream(formatters).map(DateFormatter::toDateMathParser).toArray(DateMathParser[]::new);
}
@Override
@ -164,8 +162,22 @@ public interface DateFormatter {
}
@Override
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
return new MergedDateFormatter(Arrays.stream(formatters).map(f -> f.parseDefaulting(fields)).toArray(DateFormatter[]::new));
public DateMathParser toDateMathParser() {
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;
};
}
}
}

View File

@ -25,9 +25,7 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
/**
@ -42,7 +40,8 @@ import java.util.regex.Pattern;
class EpochMillisDateFormatter implements DateFormatter {
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() {
}
@ -103,11 +102,6 @@ class EpochMillisDateFormatter implements DateFormatter {
return "epoch_millis";
}
@Override
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
return this;
}
@Override
public Locale getLocale() {
return Locale.ROOT;
@ -117,4 +111,9 @@ class EpochMillisDateFormatter implements DateFormatter {
public ZoneId getZone() {
return ZoneOffset.UTC;
}
@Override
public DateMathParser toDateMathParser() {
return DATE_MATH_INSTANCE;
}
}

View File

@ -25,14 +25,13 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
public class EpochSecondsDateFormatter implements DateFormatter {
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 EpochSecondsDateFormatter() {}
@ -91,6 +90,11 @@ public class EpochSecondsDateFormatter implements DateFormatter {
return ZoneOffset.UTC;
}
@Override
public DateMathParser toDateMathParser() {
return DATE_MATH_INSTANCE;
}
@Override
public DateFormatter withZone(ZoneId zoneId) {
if (zoneId.equals(ZoneOffset.UTC) == false) {
@ -106,9 +110,4 @@ public class EpochSecondsDateFormatter implements DateFormatter {
}
return this;
}
@Override
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
return this;
}
}

View File

@ -23,15 +23,28 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
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 DateTimeFormatter printer;
private final DateTimeFormatter[] parsers;
@ -116,8 +129,7 @@ class JavaDateFormatter implements DateFormatter {
return format;
}
@Override
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
JavaDateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
final DateTimeFormatterBuilder parseDefaultingBuilder = new DateTimeFormatterBuilder().append(printer);
fields.forEach(parseDefaultingBuilder::parseDefaulting);
if (parsers.length == 1 && parsers[0].equals(printer)) {
@ -143,6 +155,11 @@ class JavaDateFormatter implements DateFormatter {
return this.printer.getZone();
}
@Override
public DateMathParser toDateMathParser() {
return new JavaDateMathParser(this, this.parseDefaulting(ROUND_UP_BASE_FIELDS));
}
@Override
public int hashCode() {
return Objects.hash(getLocale(), printer.getZone(), format);

View File

@ -31,10 +31,7 @@ import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQueries;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.LongSupplier;
@ -47,24 +44,15 @@ import java.util.function.LongSupplier;
*/
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 roundUpFormatter;
public JavaDateMathParser(DateFormatter formatter) {
public JavaDateMathParser(DateFormatter formatter, DateFormatter roundUpFormatter) {
Objects.requireNonNull(formatter);
this.formatter = formatter;
this.roundUpFormatter = formatter.parseDefaulting(ROUND_UP_BASE_FIELDS);
this.roundUpFormatter = roundUpFormatter;
}
@Override

View File

@ -36,7 +36,7 @@ import static org.hamcrest.Matchers.is;
public class JavaDateMathParserTests extends ESTestCase {
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() {
assertDateMathEquals("2014", "2014-01-01T00:00:00.000");
@ -139,7 +139,7 @@ public class JavaDateMathParserTests extends ESTestCase {
public void testRoundingPreservesEpochAsBaseDate() {
// 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");
JavaDateMathParser parser = new JavaDateMathParser(formatter);
DateMathParser parser = formatter.toDateMathParser();
ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(formatter.parse("04:52:20"));
assertThat(zonedDateTime.getYear(), is(1970));
long millisStart = zonedDateTime.toInstant().toEpochMilli();
@ -165,7 +165,7 @@ public class JavaDateMathParserTests extends ESTestCase {
// implicit rounding with explicit timezone in the date format
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);
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);
@ -239,7 +239,7 @@ public class JavaDateMathParserTests extends ESTestCase {
assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000");
// 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);
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");