|
@@ -9,7 +9,6 @@
|
|
|
package org.elasticsearch.common.time;
|
|
|
|
|
|
import org.elasticsearch.common.Strings;
|
|
|
-import org.elasticsearch.common.util.Maps;
|
|
|
|
|
|
import java.text.ParsePosition;
|
|
|
import java.time.ZoneId;
|
|
@@ -18,44 +17,25 @@ 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.ArrayList;
|
|
|
-import java.util.Arrays;
|
|
|
-import java.util.Collection;
|
|
|
import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
import java.util.Locale;
|
|
|
-import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
import java.util.function.Consumer;
|
|
|
+import java.util.function.UnaryOperator;
|
|
|
|
|
|
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 = Maps.newMapWithExpectedSize(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.NANO_OF_SECOND, 999_999_999L);
|
|
|
- }
|
|
|
-
|
|
|
private final String format;
|
|
|
private final DateTimeFormatter printer;
|
|
|
- private final List<DateTimeFormatter> parsers;
|
|
|
- private final JavaDateFormatter roundupParser;
|
|
|
+ private final DateTimeFormatter[] parsers;
|
|
|
+ private final RoundUpFormatter roundupParser;
|
|
|
|
|
|
- static class RoundUpFormatter extends JavaDateFormatter {
|
|
|
+ private static final class RoundUpFormatter extends JavaDateFormatter {
|
|
|
|
|
|
- RoundUpFormatter(String format, List<DateTimeFormatter> roundUpParsers) {
|
|
|
- super(format, firstFrom(roundUpParsers), null, roundUpParsers);
|
|
|
- }
|
|
|
-
|
|
|
- private static DateTimeFormatter firstFrom(List<DateTimeFormatter> roundUpParsers) {
|
|
|
- return roundUpParsers.get(0);
|
|
|
+ RoundUpFormatter(String format, DateTimeFormatter[] roundUpParsers) {
|
|
|
+ super(format, roundUpParsers[0], (RoundUpFormatter) null, roundUpParsers);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -66,7 +46,18 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
|
|
|
// named formatters use default roundUpParser
|
|
|
JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter... parsers) {
|
|
|
- this(format, printer, builder -> ROUND_UP_BASE_FIELDS.forEach(builder::parseDefaulting), parsers);
|
|
|
+ this(
|
|
|
+ format,
|
|
|
+ printer,
|
|
|
+ // set up base fields which should be used for default parsing, when we round up for date math
|
|
|
+ builder -> builder.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1L)
|
|
|
+ .parseDefaulting(ChronoField.DAY_OF_MONTH, 1L)
|
|
|
+ .parseDefaulting(ChronoField.HOUR_OF_DAY, 23L)
|
|
|
+ .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 59L)
|
|
|
+ .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 59L)
|
|
|
+ .parseDefaulting(ChronoField.NANO_OF_SECOND, 999_999_999L),
|
|
|
+ parsers
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
// subclasses override roundUpParser
|
|
@@ -79,24 +70,28 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
if (printer == null) {
|
|
|
throw new IllegalArgumentException("printer may not be null");
|
|
|
}
|
|
|
- long distinctZones = Arrays.stream(parsers).map(DateTimeFormatter::getZone).distinct().count();
|
|
|
- if (distinctZones > 1) {
|
|
|
- throw new IllegalArgumentException("formatters must have the same time zone");
|
|
|
- }
|
|
|
- long distinctLocales = Arrays.stream(parsers).map(DateTimeFormatter::getLocale).distinct().count();
|
|
|
- if (distinctLocales > 1) {
|
|
|
- throw new IllegalArgumentException("formatters must have the same locale");
|
|
|
- }
|
|
|
this.printer = printer;
|
|
|
this.format = format;
|
|
|
+ this.parsers = parsersArray(printer, parsers);
|
|
|
+ this.roundupParser = createRoundUpParser(format, roundupParserConsumer, locale(), this.parsers);
|
|
|
+ }
|
|
|
|
|
|
+ private static DateTimeFormatter[] parsersArray(DateTimeFormatter printer, DateTimeFormatter... parsers) {
|
|
|
if (parsers.length == 0) {
|
|
|
- this.parsers = Collections.singletonList(printer);
|
|
|
- } else {
|
|
|
- this.parsers = Arrays.asList(parsers);
|
|
|
+ return new DateTimeFormatter[] { printer };
|
|
|
+ }
|
|
|
+ final ZoneId zoneId = parsers[0].getZone();
|
|
|
+ final Locale locale = parsers[0].getLocale();
|
|
|
+ for (int i = 1; i < parsers.length; i++) {
|
|
|
+ final DateTimeFormatter parser = parsers[i];
|
|
|
+ if (Objects.equals(parser.getZone(), zoneId) == false) {
|
|
|
+ throw new IllegalArgumentException("formatters must have the same time zone");
|
|
|
+ }
|
|
|
+ if (Objects.equals(parser.getLocale(), locale) == false) {
|
|
|
+ throw new IllegalArgumentException("formatters must have the same locale");
|
|
|
+ }
|
|
|
}
|
|
|
- List<DateTimeFormatter> roundUp = createRoundUpParser(format, roundupParserConsumer);
|
|
|
- this.roundupParser = new RoundUpFormatter(format, roundUp);
|
|
|
+ return parsers;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -108,16 +103,19 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
* <code>DateFormatters</code>.
|
|
|
* This means that we need to also have multiple RoundUp parsers.
|
|
|
*/
|
|
|
- private List<DateTimeFormatter> createRoundUpParser(String format, Consumer<DateTimeFormatterBuilder> roundupParserConsumer) {
|
|
|
+ private static RoundUpFormatter createRoundUpParser(
|
|
|
+ String format,
|
|
|
+ Consumer<DateTimeFormatterBuilder> roundupParserConsumer,
|
|
|
+ Locale locale,
|
|
|
+ DateTimeFormatter[] parsers
|
|
|
+ ) {
|
|
|
if (format.contains("||") == false) {
|
|
|
- List<DateTimeFormatter> roundUpParsers = new ArrayList<>();
|
|
|
- for (DateTimeFormatter parser : this.parsers) {
|
|
|
+ return new RoundUpFormatter(format, mapParsers(parser -> {
|
|
|
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
|
|
|
builder.append(parser);
|
|
|
roundupParserConsumer.accept(builder);
|
|
|
- roundUpParsers.add(builder.toFormatter(locale()));
|
|
|
- }
|
|
|
- return roundUpParsers;
|
|
|
+ return builder.toFormatter(locale);
|
|
|
+ }, parsers));
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
@@ -135,22 +133,26 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
if (printer == null) {
|
|
|
printer = javaDateFormatter.getPrinter();
|
|
|
}
|
|
|
- parsers.addAll(javaDateFormatter.getParsers());
|
|
|
- roundUpParsers.addAll(javaDateFormatter.getRoundupParser().getParsers());
|
|
|
+ Collections.addAll(parsers, javaDateFormatter.parsers);
|
|
|
+ Collections.addAll(roundUpParsers, javaDateFormatter.getRoundupParser().parsers);
|
|
|
}
|
|
|
|
|
|
- return new JavaDateFormatter(input, printer, roundUpParsers, parsers);
|
|
|
+ return new JavaDateFormatter(
|
|
|
+ input,
|
|
|
+ printer,
|
|
|
+ roundUpParsers.toArray(DateTimeFormatter[]::new),
|
|
|
+ parsers.toArray(DateTimeFormatter[]::new)
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- private JavaDateFormatter(
|
|
|
- String format,
|
|
|
- DateTimeFormatter printer,
|
|
|
- List<DateTimeFormatter> roundUpParsers,
|
|
|
- List<DateTimeFormatter> parsers
|
|
|
- ) {
|
|
|
+ private JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter[] roundUpParsers, DateTimeFormatter[] parsers) {
|
|
|
+ this(format, printer, new RoundUpFormatter(format, roundUpParsers), parsers);
|
|
|
+ }
|
|
|
+
|
|
|
+ private JavaDateFormatter(String format, DateTimeFormatter printer, RoundUpFormatter roundupParser, DateTimeFormatter[] parsers) {
|
|
|
this.format = format;
|
|
|
this.printer = printer;
|
|
|
- this.roundupParser = roundUpParsers != null ? new RoundUpFormatter(format, roundUpParsers) : null;
|
|
|
+ this.roundupParser = roundupParser;
|
|
|
this.parsers = parsers;
|
|
|
}
|
|
|
|
|
@@ -190,7 +192,7 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
* @throws DateTimeParseException when unable to parse with any parsers
|
|
|
*/
|
|
|
private TemporalAccessor doParse(String input) {
|
|
|
- if (parsers.size() > 1) {
|
|
|
+ if (parsers.length > 1) {
|
|
|
for (DateTimeFormatter formatter : parsers) {
|
|
|
ParsePosition pos = new ParsePosition(0);
|
|
|
Object object = formatter.toFormat().parseObject(input, pos);
|
|
@@ -200,7 +202,7 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
}
|
|
|
throw new DateTimeParseException("Failed to parse with all enclosed parsers", input, 0);
|
|
|
}
|
|
|
- return this.parsers.get(0).parse(input);
|
|
|
+ return this.parsers[0].parse(input);
|
|
|
}
|
|
|
|
|
|
private boolean parsingSucceeded(Object object, String input, ParsePosition pos) {
|
|
@@ -213,9 +215,7 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
if (zoneId.equals(zone())) {
|
|
|
return this;
|
|
|
}
|
|
|
- List<DateTimeFormatter> parsers = this.parsers.stream().map(p -> p.withZone(zoneId)).toList();
|
|
|
- List<DateTimeFormatter> roundUpParsers = this.roundupParser.getParsers().stream().map(p -> p.withZone(zoneId)).toList();
|
|
|
- return new JavaDateFormatter(format, printer.withZone(zoneId), roundUpParsers, parsers);
|
|
|
+ return mapParsers(p -> p.withZone(zoneId));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -224,9 +224,24 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
if (locale.equals(locale())) {
|
|
|
return this;
|
|
|
}
|
|
|
- List<DateTimeFormatter> parsers = this.parsers.stream().map(p -> p.withLocale(locale)).toList();
|
|
|
- List<DateTimeFormatter> roundUpParsers = this.roundupParser.getParsers().stream().map(p -> p.withLocale(locale)).toList();
|
|
|
- return new JavaDateFormatter(format, printer.withLocale(locale), roundUpParsers, parsers);
|
|
|
+ return mapParsers(p -> p.withLocale(locale));
|
|
|
+ }
|
|
|
+
|
|
|
+ private JavaDateFormatter mapParsers(UnaryOperator<DateTimeFormatter> mapping) {
|
|
|
+ return new JavaDateFormatter(
|
|
|
+ format,
|
|
|
+ mapping.apply(printer),
|
|
|
+ mapParsers(mapping, ((JavaDateFormatter) this.roundupParser).parsers),
|
|
|
+ mapParsers(mapping, this.parsers)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ private static DateTimeFormatter[] mapParsers(UnaryOperator<DateTimeFormatter> mapping, DateTimeFormatter[] parsers) {
|
|
|
+ DateTimeFormatter[] res = new DateTimeFormatter[parsers.length];
|
|
|
+ for (int i = 0; i < parsers.length; i++) {
|
|
|
+ res[i] = mapping.apply(parsers[i]);
|
|
|
+ }
|
|
|
+ return res;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -276,7 +291,4 @@ class JavaDateFormatter implements DateFormatter {
|
|
|
return String.format(Locale.ROOT, "format[%s] locale[%s]", format, locale());
|
|
|
}
|
|
|
|
|
|
- Collection<DateTimeFormatter> getParsers() {
|
|
|
- return parsers;
|
|
|
- }
|
|
|
}
|