Skip to content

Commit 61d6186

Browse files
committed
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.
1 parent 915cc1b commit 61d6186

File tree

7 files changed

+63
-49
lines changed

7 files changed

+63
-49
lines changed

server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,6 @@ String resolveExpression(String expression, final Context context) {
940940
DateMathParser dateMathParser = formatter.toDateMathParser();
941941
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false,
942942
DateUtils.dateTimeZoneToZoneId(timeZone));
943-
944943
String time = formatter.printer().print(millis);
945944
beforePlaceHolderSb.append(time);
946945
inPlaceHolderSb = new StringBuilder();

server/src/main/java/org/elasticsearch/common/time/DateFormatter.java

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919

2020
package org.elasticsearch.common.time;
2121

22+
import org.elasticsearch.ElasticsearchParseException;
23+
2224
import java.time.ZoneId;
2325
import java.time.format.DateTimeParseException;
2426
import java.time.temporal.TemporalAccessor;
25-
import java.time.temporal.TemporalField;
2627
import java.util.Arrays;
2728
import java.util.Locale;
28-
import java.util.Map;
2929
import java.util.stream.Collectors;
3030

3131
public interface DateFormatter {
@@ -86,13 +86,9 @@ public interface DateFormatter {
8686
ZoneId getZone();
8787

8888
/**
89-
* Configure a formatter using default fields for a TemporalAccessor that should be used in case
90-
* the supplied date is not having all of those fields
91-
*
92-
* @param fields A <code>Map&lt;TemporalField, Long&gt;</code> of fields to be used as fallbacks
93-
* @return A new date formatter instance, that will use those fields during parsing
89+
* Return a {@link DateMathParser} built from this formatter.
9490
*/
95-
DateFormatter parseDefaulting(Map<TemporalField, Long> fields);
91+
DateMathParser toDateMathParser();
9692

9793
/**
9894
* Merge several date formatters into a single one. Useful if you need to have several formatters with
@@ -102,18 +98,20 @@ public interface DateFormatter {
10298
* @param formatters The list of date formatters to be merged together
10399
* @return The new date formtter containing the specified date formatters
104100
*/
105-
static DateFormatter merge(DateFormatter ... formatters) {
101+
static DateFormatter merge(DateFormatter... formatters) {
106102
return new MergedDateFormatter(formatters);
107103
}
108104

109105
class MergedDateFormatter implements DateFormatter {
110106

111107
private final String format;
112108
private final DateFormatter[] formatters;
109+
private final DateMathParser[] dateMathParsers;
113110

114-
MergedDateFormatter(DateFormatter ... formatters) {
111+
MergedDateFormatter(DateFormatter... formatters) {
115112
this.formatters = formatters;
116113
this.format = Arrays.stream(formatters).map(DateFormatter::pattern).collect(Collectors.joining("||"));
114+
this.dateMathParsers = Arrays.stream(formatters).map(DateFormatter::toDateMathParser).toArray(DateMathParser[]::new);
117115
}
118116

119117
@Override
@@ -164,8 +162,22 @@ public ZoneId getZone() {
164162
}
165163

166164
@Override
167-
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
168-
return new MergedDateFormatter(Arrays.stream(formatters).map(f -> f.parseDefaulting(fields)).toArray(DateFormatter[]::new));
165+
public DateMathParser toDateMathParser() {
166+
return (text, now, roundUp, tz) -> {
167+
ElasticsearchParseException failure = null;
168+
for (DateMathParser parser : dateMathParsers) {
169+
try {
170+
return parser.parse(text, now, roundUp, tz);
171+
} catch (ElasticsearchParseException e) {
172+
if (failure == null) {
173+
failure = e;
174+
} else {
175+
failure.addSuppressed(e);
176+
}
177+
}
178+
}
179+
throw failure;
180+
};
169181
}
170182
}
171183
}

server/src/main/java/org/elasticsearch/common/time/EpochMillisDateFormatter.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
import java.time.ZoneOffset;
2626
import java.time.format.DateTimeParseException;
2727
import java.time.temporal.TemporalAccessor;
28-
import java.time.temporal.TemporalField;
2928
import java.util.Locale;
30-
import java.util.Map;
3129
import java.util.regex.Pattern;
3230

3331
/**
@@ -42,7 +40,8 @@
4240
class EpochMillisDateFormatter implements DateFormatter {
4341

4442
private static final Pattern SPLIT_BY_DOT_PATTERN = Pattern.compile("\\.");
45-
static DateFormatter INSTANCE = new EpochMillisDateFormatter();
43+
static final DateFormatter INSTANCE = new EpochMillisDateFormatter();
44+
static final DateMathParser DATE_MATH_INSTANCE = new JavaDateMathParser(INSTANCE, INSTANCE);
4645

4746
private EpochMillisDateFormatter() {
4847
}
@@ -103,11 +102,6 @@ public String pattern() {
103102
return "epoch_millis";
104103
}
105104

106-
@Override
107-
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
108-
return this;
109-
}
110-
111105
@Override
112106
public Locale getLocale() {
113107
return Locale.ROOT;
@@ -117,4 +111,9 @@ public Locale getLocale() {
117111
public ZoneId getZone() {
118112
return ZoneOffset.UTC;
119113
}
114+
115+
@Override
116+
public DateMathParser toDateMathParser() {
117+
return DATE_MATH_INSTANCE;
118+
}
120119
}

server/src/main/java/org/elasticsearch/common/time/EpochSecondsDateFormatter.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@
2525
import java.time.ZoneOffset;
2626
import java.time.format.DateTimeParseException;
2727
import java.time.temporal.TemporalAccessor;
28-
import java.time.temporal.TemporalField;
2928
import java.util.Locale;
30-
import java.util.Map;
3129
import java.util.regex.Pattern;
3230

3331
public class EpochSecondsDateFormatter implements DateFormatter {
3432

3533
public static DateFormatter INSTANCE = new EpochSecondsDateFormatter();
34+
static final DateMathParser DATE_MATH_INSTANCE = new JavaDateMathParser(INSTANCE, INSTANCE);
3635
private static final Pattern SPLIT_BY_DOT_PATTERN = Pattern.compile("\\.");
3736

3837
private EpochSecondsDateFormatter() {}
@@ -91,6 +90,11 @@ public ZoneId getZone() {
9190
return ZoneOffset.UTC;
9291
}
9392

93+
@Override
94+
public DateMathParser toDateMathParser() {
95+
return DATE_MATH_INSTANCE;
96+
}
97+
9498
@Override
9599
public DateFormatter withZone(ZoneId zoneId) {
96100
if (zoneId.equals(ZoneOffset.UTC) == false) {
@@ -106,9 +110,4 @@ public DateFormatter withLocale(Locale locale) {
106110
}
107111
return this;
108112
}
109-
110-
@Override
111-
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
112-
return this;
113-
}
114113
}

server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,28 @@
2323
import java.time.format.DateTimeFormatter;
2424
import java.time.format.DateTimeFormatterBuilder;
2525
import java.time.format.DateTimeParseException;
26+
import java.time.temporal.ChronoField;
2627
import java.time.temporal.TemporalAccessor;
2728
import java.time.temporal.TemporalField;
2829
import java.util.Arrays;
30+
import java.util.HashMap;
2931
import java.util.Locale;
3032
import java.util.Map;
3133
import java.util.Objects;
3234

3335
class JavaDateFormatter implements DateFormatter {
3436

37+
// base fields which should be used for default parsing, when we round up for date math
38+
private static final Map<TemporalField, Long> ROUND_UP_BASE_FIELDS = new HashMap<>(6);
39+
{
40+
ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L);
41+
ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L);
42+
ROUND_UP_BASE_FIELDS.put(ChronoField.HOUR_OF_DAY, 23L);
43+
ROUND_UP_BASE_FIELDS.put(ChronoField.MINUTE_OF_HOUR, 59L);
44+
ROUND_UP_BASE_FIELDS.put(ChronoField.SECOND_OF_MINUTE, 59L);
45+
ROUND_UP_BASE_FIELDS.put(ChronoField.MILLI_OF_SECOND, 999L);
46+
}
47+
3548
private final String format;
3649
private final DateTimeFormatter printer;
3750
private final DateTimeFormatter[] parsers;
@@ -116,8 +129,7 @@ public String pattern() {
116129
return format;
117130
}
118131

119-
@Override
120-
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
132+
JavaDateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
121133
final DateTimeFormatterBuilder parseDefaultingBuilder = new DateTimeFormatterBuilder().append(printer);
122134
fields.forEach(parseDefaultingBuilder::parseDefaulting);
123135
if (parsers.length == 1 && parsers[0].equals(printer)) {
@@ -143,6 +155,11 @@ public ZoneId getZone() {
143155
return this.printer.getZone();
144156
}
145157

158+
@Override
159+
public DateMathParser toDateMathParser() {
160+
return new JavaDateMathParser(this, this.parseDefaulting(ROUND_UP_BASE_FIELDS));
161+
}
162+
146163
@Override
147164
public int hashCode() {
148165
return Objects.hash(getLocale(), printer.getZone(), format);

server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@
3131
import java.time.temporal.ChronoField;
3232
import java.time.temporal.TemporalAccessor;
3333
import java.time.temporal.TemporalAdjusters;
34-
import java.time.temporal.TemporalField;
3534
import java.time.temporal.TemporalQueries;
36-
import java.util.HashMap;
37-
import java.util.Map;
3835
import java.util.Objects;
3936
import java.util.function.LongSupplier;
4037

@@ -47,24 +44,15 @@
4744
*/
4845
public class JavaDateMathParser implements DateMathParser {
4946

50-
// base fields which should be used for default parsing, when we round up
51-
private static final Map<TemporalField, Long> ROUND_UP_BASE_FIELDS = new HashMap<>(6);
52-
{
53-
ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L);
54-
ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L);
55-
ROUND_UP_BASE_FIELDS.put(ChronoField.HOUR_OF_DAY, 23L);
56-
ROUND_UP_BASE_FIELDS.put(ChronoField.MINUTE_OF_HOUR, 59L);
57-
ROUND_UP_BASE_FIELDS.put(ChronoField.SECOND_OF_MINUTE, 59L);
58-
ROUND_UP_BASE_FIELDS.put(ChronoField.MILLI_OF_SECOND, 999L);
59-
}
47+
6048

6149
private final DateFormatter formatter;
6250
private final DateFormatter roundUpFormatter;
6351

64-
public JavaDateMathParser(DateFormatter formatter) {
52+
public JavaDateMathParser(DateFormatter formatter, DateFormatter roundUpFormatter) {
6553
Objects.requireNonNull(formatter);
6654
this.formatter = formatter;
67-
this.roundUpFormatter = formatter.parseDefaulting(ROUND_UP_BASE_FIELDS);
55+
this.roundUpFormatter = roundUpFormatter;
6856
}
6957

7058
@Override

server/src/test/java/org/elasticsearch/common/time/JavaDateMathParserTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
public class JavaDateMathParserTests extends ESTestCase {
3737

3838
private final DateFormatter formatter = DateFormatters.forPattern("dateOptionalTime||epoch_millis");
39-
private final JavaDateMathParser parser = new JavaDateMathParser(formatter);
39+
private final DateMathParser parser = formatter.toDateMathParser();
4040

4141
public void testBasicDates() {
4242
assertDateMathEquals("2014", "2014-01-01T00:00:00.000");
@@ -139,7 +139,7 @@ public void testNow() {
139139
public void testRoundingPreservesEpochAsBaseDate() {
140140
// If a user only specifies times, then the date needs to always be 1970-01-01 regardless of rounding
141141
DateFormatter formatter = DateFormatters.forPattern("HH:mm:ss");
142-
JavaDateMathParser parser = new JavaDateMathParser(formatter);
142+
DateMathParser parser = formatter.toDateMathParser();
143143
ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(formatter.parse("04:52:20"));
144144
assertThat(zonedDateTime.getYear(), is(1970));
145145
long millisStart = zonedDateTime.toInstant().toEpochMilli();
@@ -165,7 +165,7 @@ public void testImplicitRounding() {
165165

166166
// implicit rounding with explicit timezone in the date format
167167
DateFormatter formatter = DateFormatters.forPattern("yyyy-MM-ddXXX");
168-
JavaDateMathParser parser = new JavaDateMathParser(formatter);
168+
DateMathParser parser = formatter.toDateMathParser();
169169
long time = parser.parse("2011-10-09+01:00", () -> 0, false, (ZoneId) null);
170170
assertEquals(this.parser.parse("2011-10-09T00:00:00.000+01:00", () -> 0), time);
171171
time = parser.parse("2011-10-09+01:00", () -> 0, true, (ZoneId) null);
@@ -239,7 +239,7 @@ public void testTimestamps() {
239239
assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000");
240240

241241
// also check other time units
242-
JavaDateMathParser parser = new JavaDateMathParser(DateFormatters.forPattern("epoch_second||dateOptionalTime"));
242+
DateMathParser parser = DateFormatters.forPattern("epoch_second||dateOptionalTime").toDateMathParser();
243243
long datetime = parser.parse("1418248078", () -> 0);
244244
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
245245

0 commit comments

Comments
 (0)