Skip to content

Commit 798fb54

Browse files
authored
Core: Create java time based DateMathParser (#32131)
This adds a java time based date math parser class in order, which will replace the joda date based one in the future. For now the class also returns the date in milliseconds since the epoch.
1 parent 93d4f84 commit 798fb54

File tree

6 files changed

+653
-4
lines changed

6 files changed

+653
-4
lines changed

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,36 @@
2020

2121
import java.time.ZoneId;
2222
import java.time.format.DateTimeFormatter;
23+
import java.time.format.DateTimeFormatterBuilder;
2324
import java.time.format.DateTimeParseException;
2425
import java.time.temporal.TemporalAccessor;
26+
import java.time.temporal.TemporalField;
27+
import java.util.Arrays;
28+
import java.util.Locale;
29+
import java.util.Map;
30+
import java.util.function.Consumer;
2531

2632
/**
2733
* wrapper class around java.time.DateTimeFormatter that supports multiple formats for easier parsing,
2834
* and one specific format for printing
2935
*/
3036
public class CompoundDateTimeFormatter {
3137

38+
private static final Consumer<DateTimeFormatter[]> SAME_TIME_ZONE_VALIDATOR = (parsers) -> {
39+
long distinctZones = Arrays.stream(parsers).map(DateTimeFormatter::getZone).distinct().count();
40+
if (distinctZones > 1) {
41+
throw new IllegalArgumentException("formatters must have the same time zone");
42+
}
43+
};
44+
3245
final DateTimeFormatter printer;
3346
final DateTimeFormatter[] parsers;
3447

3548
CompoundDateTimeFormatter(DateTimeFormatter ... parsers) {
3649
if (parsers.length == 0) {
3750
throw new IllegalArgumentException("at least one date time formatter is required");
3851
}
52+
SAME_TIME_ZONE_VALIDATOR.accept(parsers);
3953
this.printer = parsers[0];
4054
this.parsers = parsers;
4155
}
@@ -58,7 +72,18 @@ public TemporalAccessor parse(String input) {
5872
throw failure;
5973
}
6074

75+
/**
76+
* Configure a specific time zone for a date formatter
77+
*
78+
* @param zoneId The zoneId this formatter shoulduse
79+
* @return The new formatter with all parsers switched to the specified timezone
80+
*/
6181
public CompoundDateTimeFormatter withZone(ZoneId zoneId) {
82+
// shortcurt to not create new objects unnecessarily
83+
if (zoneId.equals(parsers[0].getZone())) {
84+
return this;
85+
}
86+
6287
final DateTimeFormatter[] parsersWithZone = new DateTimeFormatter[parsers.length];
6388
for (int i = 0; i < parsers.length; i++) {
6489
parsersWithZone[i] = parsers[i].withZone(zoneId);
@@ -67,6 +92,20 @@ public CompoundDateTimeFormatter withZone(ZoneId zoneId) {
6792
return new CompoundDateTimeFormatter(parsersWithZone);
6893
}
6994

95+
/**
96+
* Configure defaults for missing values in a parser, then return a new compound date formatter
97+
*/
98+
CompoundDateTimeFormatter parseDefaulting(Map<TemporalField, Long> fields) {
99+
final DateTimeFormatter[] parsersWithDefaulting = new DateTimeFormatter[parsers.length];
100+
for (int i = 0; i < parsers.length; i++) {
101+
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().append(parsers[i]);
102+
fields.forEach(builder::parseDefaulting);
103+
parsersWithDefaulting[i] = builder.toFormatter(Locale.ROOT);
104+
}
105+
106+
return new CompoundDateTimeFormatter(parsersWithDefaulting);
107+
}
108+
70109
public String format(TemporalAccessor accessor) {
71110
return printer.format(accessor);
72111
}

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,11 +701,15 @@ public class DateFormatters {
701701
/////////////////////////////////////////
702702

703703
private static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
704-
.appendValue(ChronoField.YEAR, 1, 4, SignStyle.NORMAL)
704+
.appendValue(ChronoField.YEAR, 1, 5, SignStyle.NORMAL)
705+
.optionalStart()
705706
.appendLiteral('-')
706707
.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NOT_NEGATIVE)
708+
.optionalStart()
707709
.appendLiteral('-')
708710
.appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE)
711+
.optionalEnd()
712+
.optionalEnd()
709713
.toFormatter(Locale.ROOT);
710714

711715
private static final DateTimeFormatter HOUR_MINUTE_FORMATTER = new DateTimeFormatterBuilder()
@@ -723,7 +727,11 @@ public class DateFormatters {
723727
.append(DATE_FORMATTER)
724728
.optionalStart()
725729
.appendLiteral('T')
726-
.append(HOUR_MINUTE_FORMATTER)
730+
.optionalStart()
731+
.appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE)
732+
.optionalStart()
733+
.appendLiteral(':')
734+
.appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE)
727735
.optionalStart()
728736
.appendLiteral(':')
729737
.appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE)
@@ -733,12 +741,18 @@ public class DateFormatters {
733741
.optionalEnd()
734742
.optionalStart().appendZoneOrOffsetId().optionalEnd()
735743
.optionalEnd()
744+
.optionalEnd()
745+
.optionalEnd()
736746
.toFormatter(Locale.ROOT),
737747
new DateTimeFormatterBuilder()
738748
.append(DATE_FORMATTER)
739749
.optionalStart()
740750
.appendLiteral('T')
741-
.append(HOUR_MINUTE_FORMATTER)
751+
.optionalStart()
752+
.appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE)
753+
.optionalStart()
754+
.appendLiteral(':')
755+
.appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE)
742756
.optionalStart()
743757
.appendLiteral(':')
744758
.appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE)
@@ -748,6 +762,8 @@ public class DateFormatters {
748762
.optionalEnd()
749763
.optionalStart().appendOffset("+HHmm", "Z").optionalEnd()
750764
.optionalEnd()
765+
.optionalEnd()
766+
.optionalEnd()
751767
.toFormatter(Locale.ROOT));
752768

753769
private static final DateTimeFormatter HOUR_MINUTE_SECOND_FORMATTER = new DateTimeFormatterBuilder()

0 commit comments

Comments
 (0)