Core: Add DateFormatter interface for java time parsing (#33467)

The existing approach used date formatters when a format based string
like `date_time||epoch_millis` was used, instead of the custom code.

In order to properly solve this, a new interface called
`DateFormatter` has been added, which now can be implemented for custom
formatters. Currently there are two implementations, one using java time
and one doing the epoch_millis formatter, which simply parses a number
and then converts it to a date in UTC timezone.

The DateFormatter interface now also has a method to retrieve the name
of the formatter pattern, which is needed for mapping changes anyway.

The existing `CompoundDateTimeFormatter` class has been removed, the
name was not really nice anyway.

One more minor change is the fact, that the new java time using
FormatDateFormatter does not try to parse the date with its printer
implementation first (which might be a strict one and fail), but a
printer can now be specified in addition. This saves one potential
failure/exception when parsing less strict dates.

If only a printer is specified, the printer will also be used as a
parser.
This commit is contained in:
Alexander Reelsen 2018-09-14 13:55:16 +02:00 committed by GitHub
parent 222f42274e
commit faa3c16241
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 456 additions and 249 deletions

View File

@ -28,7 +28,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.xcontent.ContextParser; import org.elasticsearch.common.xcontent.ContextParser;
import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser;
@ -368,8 +368,7 @@ public final class IndexGraveyard implements MetaData.Custom {
TOMBSTONE_PARSER.declareString((b, s) -> {}, new ParseField(DELETE_DATE_KEY)); TOMBSTONE_PARSER.declareString((b, s) -> {}, new ParseField(DELETE_DATE_KEY));
} }
static final CompoundDateTimeFormatter FORMATTER = static final DateFormatter FORMATTER = DateFormatters.forPattern("strict_date_optional_time").withZone(ZoneOffset.UTC);
DateFormatters.forPattern("strict_date_optional_time").withZone(ZoneOffset.UTC);
static ContextParser<Void, Tombstone> getParser() { static ContextParser<Void, Tombstone> getParser() {
return (parser, context) -> TOMBSTONE_PARSER.apply(parser, null).build(); return (parser, context) -> TOMBSTONE_PARSER.apply(parser, null).build();

View File

@ -31,7 +31,7 @@ import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.ToXContentFragment;
@ -48,8 +48,7 @@ import java.util.Objects;
*/ */
public final class UnassignedInfo implements ToXContentFragment, Writeable { public final class UnassignedInfo implements ToXContentFragment, Writeable {
public static final CompoundDateTimeFormatter DATE_TIME_FORMATTER = public static final DateFormatter DATE_TIME_FORMATTER = DateFormatters.forPattern("dateOptionalTime").withZone(ZoneOffset.UTC);
DateFormatters.forPattern("dateOptionalTime").withZone(ZoneOffset.UTC);
public static final Setting<TimeValue> INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING = public static final Setting<TimeValue> INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING =
Setting.positiveTimeSetting("index.unassigned.node_left.delayed_timeout", TimeValue.timeValueMinutes(1), Property.Dynamic, Setting.positiveTimeSetting("index.unassigned.node_left.delayed_timeout", TimeValue.timeValueMinutes(1), Property.Dynamic,

View File

@ -19,7 +19,7 @@
package org.elasticsearch.common; package org.elasticsearch.common;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import java.time.Instant; import java.time.Instant;
@ -85,7 +85,7 @@ public class Table {
return this; return this;
} }
private static final CompoundDateTimeFormatter FORMATTER = DateFormatters.forPattern("HH:mm:ss").withZone(ZoneOffset.UTC); private static final DateFormatter FORMATTER = DateFormatters.forPattern("HH:mm:ss").withZone(ZoneOffset.UTC);
public Table startRow() { public Table startRow() {
if (headers.isEmpty()) { if (headers.isEmpty()) {

View File

@ -0,0 +1,133 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.time;
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.Map;
import java.util.stream.Collectors;
public interface DateFormatter {
/**
* Try to parse input to a java time TemporalAccessor
* @param input An arbitrary string resembling the string representation of a date or time
* @throws DateTimeParseException If parsing fails, this exception will be thrown.
* Note that it can contained suppressed exceptions when several formatters failed parse this value
* @return The java time object containing the parsed input
*/
TemporalAccessor parse(String input);
/**
* Create a copy of this formatter that is configured to parse dates in the specified time zone
*
* @param zoneId The time zone to act on
* @return A copy of the date formatter this has been called on
*/
DateFormatter withZone(ZoneId zoneId);
/**
* Print the supplied java time accessor in a string based representation according to this formatter
*
* @param accessor The temporal accessor used to format
* @return The string result for the formatting
*/
String format(TemporalAccessor accessor);
/**
* A name based format for this formatter. Can be one of the registered formatters like <code>epoch_millis</code> or
* a configured format like <code>HH:mm:ss</code>
*
* @return The name of this formatter
*/
String pattern();
/**
* 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
*/
DateFormatter parseDefaulting(Map<TemporalField, Long> fields);
/**
* Merge several date formatters into a single one. Useful if you need to have several formatters with
* different formats act as one, for example when you specify a
* format like <code>date_hour||epoch_millis</code>
*
* @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) {
return new MergedDateFormatter(formatters);
}
class MergedDateFormatter implements DateFormatter {
private final String format;
private final DateFormatter[] formatters;
MergedDateFormatter(DateFormatter ... formatters) {
this.formatters = formatters;
this.format = Arrays.stream(formatters).map(DateFormatter::pattern).collect(Collectors.joining("||"));
}
@Override
public TemporalAccessor parse(String input) {
DateTimeParseException failure = null;
for (DateFormatter formatter : formatters) {
try {
return formatter.parse(input);
} catch (DateTimeParseException e) {
if (failure == null) {
failure = e;
} else {
failure.addSuppressed(e);
}
}
}
throw failure;
}
@Override
public DateFormatter withZone(ZoneId zoneId) {
return new MergedDateFormatter(Arrays.stream(formatters).map(f -> f.withZone(zoneId)).toArray(DateFormatter[]::new));
}
@Override
public String format(TemporalAccessor accessor) {
return formatters[0].format(accessor);
}
@Override
public String pattern() {
return format;
}
@Override
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
return new MergedDateFormatter(Arrays.stream(formatters).map(f -> f.parseDefaulting(fields)).toArray(DateFormatter[]::new));
}
}
}

View File

@ -25,12 +25,10 @@ import java.time.DateTimeException;
import java.time.DayOfWeek; import java.time.DayOfWeek;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
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.ResolverStyle; import java.time.format.ResolverStyle;
import java.time.format.SignStyle; import java.time.format.SignStyle;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
@ -38,9 +36,6 @@ import java.time.temporal.IsoFields;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields; import java.time.temporal.WeekFields;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Locale; import java.util.Locale;
import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_MONTH;
@ -106,8 +101,9 @@ public class DateFormatters {
/** /**
* Returns a generic ISO datetime parser where the date is mandatory and the time is optional. * Returns a generic ISO datetime parser where the date is mandatory and the time is optional.
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE_OPTIONAL_TIME = private static final DateFormatter STRICT_DATE_OPTIONAL_TIME =
new CompoundDateTimeFormatter(STRICT_DATE_OPTIONAL_TIME_FORMATTER_1, STRICT_DATE_OPTIONAL_TIME_FORMATTER_2); new JavaDateFormatter("strict_date_optional_time", STRICT_DATE_OPTIONAL_TIME_FORMATTER_1,
STRICT_DATE_OPTIONAL_TIME_FORMATTER_1, STRICT_DATE_OPTIONAL_TIME_FORMATTER_2);
private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_1 = new DateTimeFormatterBuilder() private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_1 = new DateTimeFormatterBuilder()
.append(STRICT_YEAR_MONTH_DAY_FORMATTER) .append(STRICT_YEAR_MONTH_DAY_FORMATTER)
@ -140,8 +136,9 @@ public class DateFormatters {
/** /**
* Returns a generic ISO datetime parser where the date is mandatory and the time is optional with nanosecond resolution. * Returns a generic ISO datetime parser where the date is mandatory and the time is optional with nanosecond resolution.
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE_OPTIONAL_TIME_NANOS = private static final DateFormatter STRICT_DATE_OPTIONAL_TIME_NANOS = new JavaDateFormatter("strict_date_optional_time_nanos",
new CompoundDateTimeFormatter(STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_1, STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_2); STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_1,
STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_1, STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_2);
///////////////////////////////////////// /////////////////////////////////////////
// //
@ -162,7 +159,8 @@ public class DateFormatters {
* Returns a basic formatter for a two digit hour of day, two digit minute * Returns a basic formatter for a two digit hour of day, two digit minute
* of hour, two digit second of minute, and time zone offset (HHmmssZ). * of hour, two digit second of minute, and time zone offset (HHmmssZ).
*/ */
private static final CompoundDateTimeFormatter BASIC_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_TIME_NO_MILLIS = new JavaDateFormatter("basic_time_no_millis",
new DateTimeFormatterBuilder().append(BASIC_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(BASIC_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_TIME_NO_MILLIS_BASE).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(BASIC_TIME_NO_MILLIS_BASE).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
); );
@ -186,7 +184,7 @@ public class DateFormatters {
* of hour, two digit second of minute, three digit millis, and time zone * of hour, two digit second of minute, three digit millis, and time zone
* offset (HHmmss.SSSZ). * offset (HHmmss.SSSZ).
*/ */
private static final CompoundDateTimeFormatter BASIC_TIME = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_TIME = new JavaDateFormatter("basic_time",
new DateTimeFormatterBuilder().append(BASIC_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(BASIC_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(BASIC_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(BASIC_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
@ -203,7 +201,7 @@ public class DateFormatters {
* of hour, two digit second of minute, three digit millis, and time zone * of hour, two digit second of minute, three digit millis, and time zone
* offset prefixed by 'T' ('T'HHmmss.SSSZ). * offset prefixed by 'T' ('T'HHmmss.SSSZ).
*/ */
private static final CompoundDateTimeFormatter BASIC_T_TIME = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_T_TIME = new JavaDateFormatter("basic_t_time",
new DateTimeFormatterBuilder().append(BASIC_T_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(BASIC_T_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_T_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(BASIC_T_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_T_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(BASIC_T_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
@ -214,11 +212,11 @@ public class DateFormatters {
* of hour, two digit second of minute, and time zone offset prefixed by 'T' * of hour, two digit second of minute, and time zone offset prefixed by 'T'
* ('T'HHmmssZ). * ('T'HHmmssZ).
*/ */
private static final CompoundDateTimeFormatter BASIC_T_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_T_TIME_NO_MILLIS = new JavaDateFormatter("basic_t_time_no_millis",
new DateTimeFormatterBuilder().appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE).append(TIME_ZONE_FORMATTER_NO_COLON)
.append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) .toFormatter(Locale.ROOT)
); );
private static final DateTimeFormatter BASIC_YEAR_MONTH_DAY_FORMATTER = new DateTimeFormatterBuilder() private static final DateTimeFormatter BASIC_YEAR_MONTH_DAY_FORMATTER = new DateTimeFormatterBuilder()
@ -241,7 +239,7 @@ public class DateFormatters {
* Returns a basic formatter that combines a basic date and time, separated * Returns a basic formatter that combines a basic date and time, separated
* by a 'T' (yyyyMMdd'T'HHmmss.SSSZ). * by a 'T' (yyyyMMdd'T'HHmmss.SSSZ).
*/ */
private static final CompoundDateTimeFormatter BASIC_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_DATE_TIME = new JavaDateFormatter("basic_date_time",
new DateTimeFormatterBuilder().append(BASIC_DATE_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(BASIC_DATE_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_DATE_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(BASIC_DATE_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_DATE_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(BASIC_DATE_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
@ -254,7 +252,9 @@ public class DateFormatters {
* Returns a basic formatter that combines a basic date and time without millis, * Returns a basic formatter that combines a basic date and time without millis,
* separated by a 'T' (yyyyMMdd'T'HHmmssZ). * separated by a 'T' (yyyyMMdd'T'HHmmssZ).
*/ */
private static final CompoundDateTimeFormatter BASIC_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_DATE_TIME_NO_MILLIS = new JavaDateFormatter("basic_t_time_no_millis",
new DateTimeFormatterBuilder().append(BASIC_DATE_T).append(BASIC_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_DATE_T).append(BASIC_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().append(BASIC_DATE_T).append(BASIC_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_DATE_T).append(BASIC_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().append(BASIC_DATE_T).append(BASIC_TIME_NO_MILLIS_BASE)
@ -265,14 +265,14 @@ public class DateFormatters {
* Returns a formatter for a full ordinal date, using a four * Returns a formatter for a full ordinal date, using a four
* digit year and three digit dayOfYear (yyyyDDD). * digit year and three digit dayOfYear (yyyyDDD).
*/ */
private static final CompoundDateTimeFormatter BASIC_ORDINAL_DATE = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_ORDINAL_DATE = new JavaDateFormatter("basic_ordinal_date",
DateTimeFormatter.ofPattern("yyyyDDD", Locale.ROOT)); DateTimeFormatter.ofPattern("yyyyDDD", Locale.ROOT));
/* /*
* Returns a formatter for a full ordinal date and time, using a four * Returns a formatter for a full ordinal date and time, using a four
* digit year and three digit dayOfYear (yyyyDDD'T'HHmmss.SSSZ). * digit year and three digit dayOfYear (yyyyDDD'T'HHmmss.SSSZ).
*/ */
private static final CompoundDateTimeFormatter BASIC_ORDINAL_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_ORDINAL_DATE_TIME = new JavaDateFormatter("basic_ordinal_date_time",
new DateTimeFormatterBuilder().appendPattern("yyyyDDD").append(BASIC_T_TIME_PRINTER) new DateTimeFormatterBuilder().appendPattern("yyyyDDD").append(BASIC_T_TIME_PRINTER)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendPattern("yyyyDDD").append(BASIC_T_TIME_FORMATTER) new DateTimeFormatterBuilder().appendPattern("yyyyDDD").append(BASIC_T_TIME_FORMATTER)
@ -284,7 +284,9 @@ public class DateFormatters {
* Returns a formatter for a full ordinal date and time without millis, * Returns a formatter for a full ordinal date and time without millis,
* using a four digit year and three digit dayOfYear (yyyyDDD'T'HHmmssZ). * using a four digit year and three digit dayOfYear (yyyyDDD'T'HHmmssZ).
*/ */
private static final CompoundDateTimeFormatter BASIC_ORDINAL_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_ORDINAL_DATE_TIME_NO_MILLIS = new JavaDateFormatter("basic_ordinal_date_time_no_millis",
new DateTimeFormatterBuilder().appendPattern("yyyyDDD").appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendPattern("yyyyDDD").appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().appendPattern("yyyyDDD").appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendPattern("yyyyDDD").appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().appendPattern("yyyyDDD").appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE)
@ -329,14 +331,14 @@ public class DateFormatters {
* Returns a basic formatter for a full date as four digit weekyear, two * Returns a basic formatter for a full date as four digit weekyear, two
* digit week of weekyear, and one digit day of week (xxxx'W'wwe). * digit week of weekyear, and one digit day of week (xxxx'W'wwe).
*/ */
private static final CompoundDateTimeFormatter STRICT_BASIC_WEEK_DATE = private static final DateFormatter STRICT_BASIC_WEEK_DATE =
new CompoundDateTimeFormatter(STRICT_BASIC_WEEK_DATE_PRINTER, STRICT_BASIC_WEEK_DATE_FORMATTER); new JavaDateFormatter("strict_basic_week_date", STRICT_BASIC_WEEK_DATE_PRINTER, STRICT_BASIC_WEEK_DATE_FORMATTER);
/* /*
* Returns a basic formatter that combines a basic weekyear date and time * Returns a basic formatter that combines a basic weekyear date and time
* without millis, separated by a 'T' (xxxx'W'wwe'T'HHmmssX). * without millis, separated by a 'T' (xxxx'W'wwe'T'HHmmssX).
*/ */
private static final CompoundDateTimeFormatter STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS = new JavaDateFormatter("strict_basic_week_date_no_millis",
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(STRICT_BASIC_WEEK_DATE_PRINTER).append(DateTimeFormatter.ofPattern("'T'HHmmssX", Locale.ROOT)) .append(STRICT_BASIC_WEEK_DATE_PRINTER).append(DateTimeFormatter.ofPattern("'T'HHmmssX", Locale.ROOT))
.toFormatter(Locale.ROOT), .toFormatter(Locale.ROOT),
@ -349,7 +351,7 @@ public class DateFormatters {
* Returns a basic formatter that combines a basic weekyear date and time, * Returns a basic formatter that combines a basic weekyear date and time,
* separated by a 'T' (xxxx'W'wwe'T'HHmmss.SSSX). * separated by a 'T' (xxxx'W'wwe'T'HHmmss.SSSX).
*/ */
private static final CompoundDateTimeFormatter STRICT_BASIC_WEEK_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_BASIC_WEEK_DATE_TIME = new JavaDateFormatter("strict_basic_week_date_time",
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(STRICT_BASIC_WEEK_DATE_PRINTER) .append(STRICT_BASIC_WEEK_DATE_PRINTER)
.append(DateTimeFormatter.ofPattern("'T'HHmmss.SSSX", Locale.ROOT)) .append(DateTimeFormatter.ofPattern("'T'HHmmss.SSSX", Locale.ROOT))
@ -363,30 +365,32 @@ public class DateFormatters {
/* /*
* An ISO date formatter that formats or parses a date without an offset, such as '2011-12-03'. * An ISO date formatter that formats or parses a date without an offset, such as '2011-12-03'.
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_DATE = new JavaDateFormatter("strict_date",
DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT)); DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT));
/* /*
* A date formatter that formats or parses a date plus an hour without an offset, such as '2011-12-03T01'. * A date formatter that formats or parses a date plus an hour without an offset, such as '2011-12-03T01'.
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE_HOUR = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_DATE_HOUR = new JavaDateFormatter("strict_date_hour",
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH", Locale.ROOT)); DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH", Locale.ROOT));
/* /*
* A date formatter that formats or parses a date plus an hour/minute without an offset, such as '2011-12-03T01:10'. * A date formatter that formats or parses a date plus an hour/minute without an offset, such as '2011-12-03T01:10'.
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE_HOUR_MINUTE = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_DATE_HOUR_MINUTE = new JavaDateFormatter("strict_date_hour_minute",
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm", Locale.ROOT)); DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm", Locale.ROOT));
/* /*
* A strict date formatter that formats or parses a date without an offset, such as '2011-12-03'. * A strict date formatter that formats or parses a date without an offset, such as '2011-12-03'.
*/ */
private static final CompoundDateTimeFormatter STRICT_YEAR_MONTH_DAY = new CompoundDateTimeFormatter(STRICT_YEAR_MONTH_DAY_FORMATTER); private static final DateFormatter STRICT_YEAR_MONTH_DAY =
new JavaDateFormatter("strict_year_month_day", STRICT_YEAR_MONTH_DAY_FORMATTER);
/* /*
* A strict formatter that formats or parses a year and a month, such as '2011-12'. * A strict formatter that formats or parses a year and a month, such as '2011-12'.
*/ */
private static final CompoundDateTimeFormatter STRICT_YEAR_MONTH = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() private static final DateFormatter STRICT_YEAR_MONTH = new JavaDateFormatter("strict_year_month",
new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendLiteral("-") .appendLiteral("-")
.appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE) .appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE)
@ -395,15 +399,15 @@ public class DateFormatters {
/* /*
* A strict formatter that formats or parses a year, such as '2011'. * A strict formatter that formats or parses a year, such as '2011'.
*/ */
private static final CompoundDateTimeFormatter STRICT_YEAR = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() private static final DateFormatter STRICT_YEAR = new JavaDateFormatter("strict_year", new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.toFormatter(Locale.ROOT)); .toFormatter(Locale.ROOT));
/* /*
* A strict formatter that formats or parses a hour, minute and second, such as '09:43:25'. * A strict formatter that formats or parses a hour, minute and second, such as '09:43:25'.
*/ */
private static final CompoundDateTimeFormatter STRICT_HOUR_MINUTE_SECOND = private static final DateFormatter STRICT_HOUR_MINUTE_SECOND =
new CompoundDateTimeFormatter(STRICT_HOUR_MINUTE_SECOND_FORMATTER); new JavaDateFormatter("strict_hour_minute_second", STRICT_HOUR_MINUTE_SECOND_FORMATTER);
private static final DateTimeFormatter STRICT_DATE_FORMATTER = new DateTimeFormatterBuilder() private static final DateTimeFormatter STRICT_DATE_FORMATTER = new DateTimeFormatterBuilder()
.append(STRICT_YEAR_MONTH_DAY_FORMATTER) .append(STRICT_YEAR_MONTH_DAY_FORMATTER)
@ -418,7 +422,8 @@ public class DateFormatters {
* Returns a formatter that combines a full date and time, separated by a 'T' * Returns a formatter that combines a full date and time, separated by a 'T'
* (yyyy-MM-dd'T'HH:mm:ss.SSSZZ). * (yyyy-MM-dd'T'HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_DATE_TIME = new JavaDateFormatter("strict_date_time",
new DateTimeFormatterBuilder().append(STRICT_DATE_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_DATE_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(STRICT_DATE_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_DATE_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(STRICT_DATE_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
); );
@ -435,7 +440,7 @@ public class DateFormatters {
* Returns a formatter for a full ordinal date and time without millis, * Returns a formatter for a full ordinal date and time without millis,
* using a four digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ssZZ). * using a four digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_ORDINAL_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_ORDINAL_DATE_TIME_NO_MILLIS = new JavaDateFormatter("strict_ordinal_date_time_no_millis",
new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_NO_MILLIS_BASE)
@ -452,7 +457,9 @@ public class DateFormatters {
* Returns a formatter that combines a full date and time without millis, * Returns a formatter that combines a full date and time without millis,
* separated by a 'T' (yyyy-MM-dd'T'HH:mm:ssZZ). * separated by a 'T' (yyyy-MM-dd'T'HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_DATE_TIME_NO_MILLIS = new JavaDateFormatter("strict_date_time_no_millis",
new DateTimeFormatterBuilder().append(STRICT_DATE_TIME_NO_MILLIS_FORMATTER)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_DATE_TIME_NO_MILLIS_FORMATTER) new DateTimeFormatterBuilder().append(STRICT_DATE_TIME_NO_MILLIS_FORMATTER)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_DATE_TIME_NO_MILLIS_FORMATTER) new DateTimeFormatterBuilder().append(STRICT_DATE_TIME_NO_MILLIS_FORMATTER)
@ -478,17 +485,19 @@ public class DateFormatters {
* NOTE: this is not a strict formatter to retain the joda time based behaviour, * NOTE: this is not a strict formatter to retain the joda time based behaviour,
* even though it's named like this * even though it's named like this
*/ */
private static final CompoundDateTimeFormatter STRICT_HOUR_MINUTE_SECOND_MILLIS = private static final DateFormatter STRICT_HOUR_MINUTE_SECOND_MILLIS =
new CompoundDateTimeFormatter(STRICT_HOUR_MINUTE_SECOND_MILLIS_PRINTER, STRICT_HOUR_MINUTE_SECOND_MILLIS_FORMATTER); new JavaDateFormatter("strict_hour_minute_second_millis",
STRICT_HOUR_MINUTE_SECOND_MILLIS_PRINTER, STRICT_HOUR_MINUTE_SECOND_MILLIS_FORMATTER);
private static final CompoundDateTimeFormatter STRICT_HOUR_MINUTE_SECOND_FRACTION = STRICT_HOUR_MINUTE_SECOND_MILLIS; private static final DateFormatter STRICT_HOUR_MINUTE_SECOND_FRACTION = STRICT_HOUR_MINUTE_SECOND_MILLIS;
/* /*
* Returns a formatter that combines a full date, two digit hour of day, * Returns a formatter that combines a full date, two digit hour of day,
* two digit minute of hour, two digit second of minute, and three digit * two digit minute of hour, two digit second of minute, and three digit
* fraction of second (yyyy-MM-dd'T'HH:mm:ss.SSS). * fraction of second (yyyy-MM-dd'T'HH:mm:ss.SSS).
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION = new JavaDateFormatter(
"strict_date_hour_minute_second_fraction",
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(STRICT_YEAR_MONTH_DAY_FORMATTER) .append(STRICT_YEAR_MONTH_DAY_FORMATTER)
.appendLiteral("T") .appendLiteral("T")
@ -503,20 +512,20 @@ public class DateFormatters {
.toFormatter(Locale.ROOT) .toFormatter(Locale.ROOT)
); );
private static final CompoundDateTimeFormatter STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS = STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION; private static final DateFormatter STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS = STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION;
/* /*
* Returns a formatter for a two digit hour of day. (HH) * Returns a formatter for a two digit hour of day. (HH)
*/ */
private static final CompoundDateTimeFormatter STRICT_HOUR = private static final DateFormatter STRICT_HOUR =
new CompoundDateTimeFormatter(DateTimeFormatter.ofPattern("HH", Locale.ROOT)); new JavaDateFormatter("strict_hour", DateTimeFormatter.ofPattern("HH", Locale.ROOT));
/* /*
* Returns a formatter for a two digit hour of day and two digit minute of * Returns a formatter for a two digit hour of day and two digit minute of
* hour. (HH:mm) * hour. (HH:mm)
*/ */
private static final CompoundDateTimeFormatter STRICT_HOUR_MINUTE = private static final DateFormatter STRICT_HOUR_MINUTE =
new CompoundDateTimeFormatter(DateTimeFormatter.ofPattern("HH:mm", Locale.ROOT)); new JavaDateFormatter("strict_hour_minute", DateTimeFormatter.ofPattern("HH:mm", Locale.ROOT));
private static final DateTimeFormatter STRICT_ORDINAL_DATE_TIME_FORMATTER_BASE = new DateTimeFormatterBuilder() private static final DateTimeFormatter STRICT_ORDINAL_DATE_TIME_FORMATTER_BASE = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
@ -535,7 +544,7 @@ public class DateFormatters {
* Returns a formatter for a full ordinal date and time, using a four * Returns a formatter for a full ordinal date and time, using a four
* digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ss.SSSZZ). * digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_ORDINAL_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_ORDINAL_DATE_TIME = new JavaDateFormatter("strict_ordinal_date_time",
new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_FORMATTER_BASE) new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_FORMATTER_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_FORMATTER_BASE) new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_FORMATTER_BASE)
@ -566,7 +575,7 @@ public class DateFormatters {
* hour, two digit second of minute, three digit fraction of second, and * hour, two digit second of minute, three digit fraction of second, and
* time zone offset (HH:mm:ss.SSSZZ). * time zone offset (HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_TIME = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_TIME = new JavaDateFormatter("strict_time",
new DateTimeFormatterBuilder().append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_TIME_FORMATTER_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(STRICT_TIME_FORMATTER_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_TIME_FORMATTER_BASE).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(STRICT_TIME_FORMATTER_BASE).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
@ -577,7 +586,7 @@ public class DateFormatters {
* hour, two digit second of minute, three digit fraction of second, and * hour, two digit second of minute, three digit fraction of second, and
* time zone offset prefixed by 'T' ('T'HH:mm:ss.SSSZZ). * time zone offset prefixed by 'T' ('T'HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_T_TIME = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_T_TIME = new JavaDateFormatter("strict_t_time",
new DateTimeFormatterBuilder().appendLiteral('T').append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().appendLiteral('T').append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendLiteral('T').append(STRICT_TIME_FORMATTER_BASE) new DateTimeFormatterBuilder().appendLiteral('T').append(STRICT_TIME_FORMATTER_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
@ -597,7 +606,8 @@ public class DateFormatters {
* Returns a formatter for a two digit hour of day, two digit minute of * Returns a formatter for a two digit hour of day, two digit minute of
* hour, two digit second of minute, and time zone offset (HH:mm:ssZZ). * hour, two digit second of minute, and time zone offset (HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_TIME_NO_MILLIS = new JavaDateFormatter("strict_time_no_millis",
new DateTimeFormatterBuilder().append(STRICT_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(STRICT_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(STRICT_TIME_NO_MILLIS_BASE).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(STRICT_TIME_NO_MILLIS_BASE).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
); );
@ -607,7 +617,9 @@ public class DateFormatters {
* hour, two digit second of minute, and time zone offset prefixed * hour, two digit second of minute, and time zone offset prefixed
* by 'T' ('T'HH:mm:ssZZ). * by 'T' ('T'HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_T_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_T_TIME_NO_MILLIS = new JavaDateFormatter("strict_t_time_no_millis",
new DateTimeFormatterBuilder().appendLiteral("T").append(STRICT_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendLiteral("T").append(STRICT_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().appendLiteral("T").append(STRICT_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendLiteral("T").append(STRICT_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().appendLiteral("T").append(STRICT_TIME_NO_MILLIS_BASE)
@ -632,13 +644,15 @@ public class DateFormatters {
* Returns a formatter for a full date as four digit weekyear, two digit * Returns a formatter for a full date as four digit weekyear, two digit
* week of weekyear, and one digit day of week (xxxx-'W'ww-e). * week of weekyear, and one digit day of week (xxxx-'W'ww-e).
*/ */
private static final CompoundDateTimeFormatter STRICT_WEEK_DATE = new CompoundDateTimeFormatter(ISO_WEEK_DATE); private static final DateFormatter STRICT_WEEK_DATE = new JavaDateFormatter("strict_week_date", ISO_WEEK_DATE);
/* /*
* Returns a formatter that combines a full weekyear date and time without millis, * Returns a formatter that combines a full weekyear date and time without millis,
* separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ssZZ). * separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_WEEK_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_WEEK_DATE_TIME_NO_MILLIS = new JavaDateFormatter("strict_week_date_time_no_millis",
new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T)
.append(STRICT_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T) new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T)
.append(STRICT_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT), .append(STRICT_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T) new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T)
@ -649,7 +663,7 @@ public class DateFormatters {
* Returns a formatter that combines a full weekyear date and time, * Returns a formatter that combines a full weekyear date and time,
* separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ). * separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter STRICT_WEEK_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter STRICT_WEEK_DATE_TIME = new JavaDateFormatter("strict_week_date_time",
new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T).append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T).append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T).append(STRICT_TIME_FORMATTER_BASE) new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T).append(STRICT_TIME_FORMATTER_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
@ -660,7 +674,7 @@ public class DateFormatters {
/* /*
* Returns a formatter for a four digit weekyear * Returns a formatter for a four digit weekyear
*/ */
private static final CompoundDateTimeFormatter STRICT_WEEKYEAR = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() private static final DateFormatter STRICT_WEEKYEAR = new JavaDateFormatter("strict_weekyear", new DateTimeFormatterBuilder()
.appendValue(WeekFields.ISO.weekBasedYear(), 4, 10, SignStyle.EXCEEDS_PAD) .appendValue(WeekFields.ISO.weekBasedYear(), 4, 10, SignStyle.EXCEEDS_PAD)
.toFormatter(Locale.ROOT)); .toFormatter(Locale.ROOT));
@ -674,13 +688,15 @@ public class DateFormatters {
* Returns a formatter for a four digit weekyear and two digit week of * Returns a formatter for a four digit weekyear and two digit week of
* weekyear. (xxxx-'W'ww) * weekyear. (xxxx-'W'ww)
*/ */
private static final CompoundDateTimeFormatter STRICT_WEEKYEAR_WEEK = new CompoundDateTimeFormatter(STRICT_WEEKYEAR_WEEK_FORMATTER); private static final DateFormatter STRICT_WEEKYEAR_WEEK =
new JavaDateFormatter("strict_weekyear_week", STRICT_WEEKYEAR_WEEK_FORMATTER);
/* /*
* Returns a formatter for a four digit weekyear, two digit week of * Returns a formatter for a four digit weekyear, two digit week of
* weekyear, and one digit day of week. (xxxx-'W'ww-e) * weekyear, and one digit day of week. (xxxx-'W'ww-e)
*/ */
private static final CompoundDateTimeFormatter STRICT_WEEKYEAR_WEEK_DAY = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() private static final DateFormatter STRICT_WEEKYEAR_WEEK_DAY = new JavaDateFormatter("strict_weekyear_week_day",
new DateTimeFormatterBuilder()
.append(STRICT_WEEKYEAR_WEEK_FORMATTER) .append(STRICT_WEEKYEAR_WEEK_FORMATTER)
.appendLiteral("-") .appendLiteral("-")
.appendValue(WeekFields.ISO.dayOfWeek()) .appendValue(WeekFields.ISO.dayOfWeek())
@ -691,14 +707,14 @@ public class DateFormatters {
* two digit minute of hour, and two digit second of * two digit minute of hour, and two digit second of
* minute. (yyyy-MM-dd'T'HH:mm:ss) * minute. (yyyy-MM-dd'T'HH:mm:ss)
*/ */
private static final CompoundDateTimeFormatter STRICT_DATE_HOUR_MINUTE_SECOND = private static final DateFormatter STRICT_DATE_HOUR_MINUTE_SECOND = new JavaDateFormatter("strict_date_hour_minute_second",
new CompoundDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT)); DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT));
/* /*
* A basic formatter for a full date as four digit year, two digit * A basic formatter for a full date as four digit year, two digit
* month of year, and two digit day of month (yyyyMMdd). * month of year, and two digit day of month (yyyyMMdd).
*/ */
private static final CompoundDateTimeFormatter BASIC_DATE = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_DATE = new JavaDateFormatter("basic_date",
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.NORMAL) .appendValue(ChronoField.YEAR, 4, 4, SignStyle.NORMAL)
.appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE) .appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE)
@ -723,7 +739,7 @@ public class DateFormatters {
* Returns a formatter for a full ordinal date, using a four * Returns a formatter for a full ordinal date, using a four
* digit year and three digit dayOfYear (yyyy-DDD). * digit year and three digit dayOfYear (yyyy-DDD).
*/ */
private static final CompoundDateTimeFormatter STRICT_ORDINAL_DATE = new CompoundDateTimeFormatter(STRICT_ORDINAL_DATE_FORMATTER); private static final DateFormatter STRICT_ORDINAL_DATE = new JavaDateFormatter("strict_ordinal_date", STRICT_ORDINAL_DATE_FORMATTER);
///////////////////////////////////////// /////////////////////////////////////////
// //
@ -759,7 +775,8 @@ public class DateFormatters {
* a date formatter with optional time, being very lenient, format is * a date formatter with optional time, being very lenient, format is
* yyyy-MM-dd'T'HH:mm:ss.SSSZ * yyyy-MM-dd'T'HH:mm:ss.SSSZ
*/ */
private static final CompoundDateTimeFormatter DATE_OPTIONAL_TIME = new CompoundDateTimeFormatter(STRICT_DATE_OPTIONAL_TIME.printer, private static final DateFormatter DATE_OPTIONAL_TIME = new JavaDateFormatter("date_optional_time",
STRICT_DATE_OPTIONAL_TIME_FORMATTER_1,
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(DATE_FORMATTER) .append(DATE_FORMATTER)
.optionalStart() .optionalStart()
@ -834,8 +851,8 @@ public class DateFormatters {
* Returns a formatter for a full ordinal date, using a four * Returns a formatter for a full ordinal date, using a four
* digit year and three digit dayOfYear (yyyy-DDD). * digit year and three digit dayOfYear (yyyy-DDD).
*/ */
private static final CompoundDateTimeFormatter ORDINAL_DATE = private static final DateFormatter ORDINAL_DATE =
new CompoundDateTimeFormatter(ORDINAL_DATE_PRINTER, ORDINAL_DATE_FORMATTER); new JavaDateFormatter("ordinal_date", ORDINAL_DATE_PRINTER, ORDINAL_DATE_FORMATTER);
private static final DateTimeFormatter TIME_NO_MILLIS_FORMATTER = new DateTimeFormatterBuilder() private static final DateTimeFormatter TIME_NO_MILLIS_FORMATTER = new DateTimeFormatterBuilder()
.appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE)
@ -864,70 +881,32 @@ public class DateFormatters {
/* /*
* Returns a formatter for a four digit weekyear. (YYYY) * Returns a formatter for a four digit weekyear. (YYYY)
*/ */
private static final CompoundDateTimeFormatter WEEK_YEAR = new CompoundDateTimeFormatter( private static final DateFormatter WEEK_YEAR = new JavaDateFormatter("week_year",
new DateTimeFormatterBuilder().appendValue(WeekFields.ISO.weekBasedYear()).toFormatter(Locale.ROOT)); new DateTimeFormatterBuilder().appendValue(WeekFields.ISO.weekBasedYear()).toFormatter(Locale.ROOT));
/* /*
* Returns a formatter for a four digit weekyear. (uuuu) * Returns a formatter for a four digit weekyear. (uuuu)
*/ */
private static final CompoundDateTimeFormatter YEAR = new CompoundDateTimeFormatter( private static final DateFormatter YEAR = new JavaDateFormatter("year",
new DateTimeFormatterBuilder().appendValue(ChronoField.YEAR).toFormatter(Locale.ROOT)); new DateTimeFormatterBuilder().appendValue(ChronoField.YEAR).toFormatter(Locale.ROOT));
/* /*
* Returns a formatter for parsing the seconds since the epoch * Returns a formatter for parsing the seconds since the epoch
*/ */
private static final CompoundDateTimeFormatter EPOCH_SECOND = new CompoundDateTimeFormatter( private static final DateFormatter EPOCH_SECOND = new JavaDateFormatter("epoch_second",
new DateTimeFormatterBuilder().appendValue(ChronoField.INSTANT_SECONDS).toFormatter(Locale.ROOT)); new DateTimeFormatterBuilder().appendValue(ChronoField.INSTANT_SECONDS).toFormatter(Locale.ROOT));
/* /*
* Returns a formatter for parsing the milliseconds since the epoch * Parses the milliseconds since/before the epoch
* This one needs a custom implementation, because the standard date formatter can not parse negative values
* or anything +- 999 milliseconds around the epoch
*
* This implementation just resorts to parsing the input directly to an Instant by trying to parse a number.
*/ */
private static final DateTimeFormatter EPOCH_MILLIS_FORMATTER = new DateTimeFormatterBuilder() private static final DateFormatter EPOCH_MILLIS = EpochMillisDateFormatter.INSTANCE;
.appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter(Locale.ROOT);
private static final class EpochDateTimeFormatter extends CompoundDateTimeFormatter {
private EpochDateTimeFormatter() {
super(EPOCH_MILLIS_FORMATTER);
}
private EpochDateTimeFormatter(ZoneId zoneId) {
super(EPOCH_MILLIS_FORMATTER.withZone(zoneId));
}
@Override
public TemporalAccessor parse(String input) {
try {
return Instant.ofEpochMilli(Long.valueOf(input)).atZone(ZoneOffset.UTC);
} catch (NumberFormatException e) {
throw new DateTimeParseException("invalid number", input, 0, e);
}
}
@Override
public CompoundDateTimeFormatter withZone(ZoneId zoneId) {
return new EpochDateTimeFormatter(zoneId);
}
@Override
public String format(TemporalAccessor accessor) {
return String.valueOf(Instant.from(accessor).toEpochMilli());
}
}
private static final CompoundDateTimeFormatter EPOCH_MILLIS = new EpochDateTimeFormatter();
/* /*
* Returns a formatter that combines a full date and two digit hour of * Returns a formatter that combines a full date and two digit hour of
* day. (yyyy-MM-dd'T'HH) * day. (yyyy-MM-dd'T'HH)
*/ */
private static final CompoundDateTimeFormatter DATE_HOUR = new CompoundDateTimeFormatter(STRICT_DATE_HOUR.printer, private static final DateFormatter DATE_HOUR = new JavaDateFormatter("date_hour",
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH", Locale.ROOT),
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(DATE_FORMATTER) .append(DATE_FORMATTER)
.appendLiteral("T") .appendLiteral("T")
@ -940,8 +919,8 @@ public class DateFormatters {
* fraction of second (yyyy-MM-dd'T'HH:mm:ss.SSS). Parsing will parse up * fraction of second (yyyy-MM-dd'T'HH:mm:ss.SSS). Parsing will parse up
* to 3 fractional second digits. * to 3 fractional second digits.
*/ */
private static final CompoundDateTimeFormatter DATE_HOUR_MINUTE_SECOND_MILLIS = private static final DateFormatter DATE_HOUR_MINUTE_SECOND_MILLIS =
new CompoundDateTimeFormatter( new JavaDateFormatter("date_hour_minute_second_millis",
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(STRICT_YEAR_MONTH_DAY_FORMATTER) .append(STRICT_YEAR_MONTH_DAY_FORMATTER)
.appendLiteral("T") .appendLiteral("T")
@ -953,13 +932,14 @@ public class DateFormatters {
.append(HOUR_MINUTE_SECOND_MILLIS_FORMATTER) .append(HOUR_MINUTE_SECOND_MILLIS_FORMATTER)
.toFormatter(Locale.ROOT)); .toFormatter(Locale.ROOT));
private static final CompoundDateTimeFormatter DATE_HOUR_MINUTE_SECOND_FRACTION = DATE_HOUR_MINUTE_SECOND_MILLIS; private static final DateFormatter DATE_HOUR_MINUTE_SECOND_FRACTION = DATE_HOUR_MINUTE_SECOND_MILLIS;
/* /*
* Returns a formatter that combines a full date, two digit hour of day, * Returns a formatter that combines a full date, two digit hour of day,
* and two digit minute of hour. (yyyy-MM-dd'T'HH:mm) * and two digit minute of hour. (yyyy-MM-dd'T'HH:mm)
*/ */
private static final CompoundDateTimeFormatter DATE_HOUR_MINUTE = new CompoundDateTimeFormatter(STRICT_DATE_HOUR_MINUTE.printer, private static final DateFormatter DATE_HOUR_MINUTE = new JavaDateFormatter("date_hour_minute",
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm", Locale.ROOT),
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(DATE_FORMATTER) .append(DATE_FORMATTER)
.appendLiteral("T") .appendLiteral("T")
@ -971,8 +951,8 @@ public class DateFormatters {
* two digit minute of hour, and two digit second of * two digit minute of hour, and two digit second of
* minute. (yyyy-MM-dd'T'HH:mm:ss) * minute. (yyyy-MM-dd'T'HH:mm:ss)
*/ */
private static final CompoundDateTimeFormatter DATE_HOUR_MINUTE_SECOND = new CompoundDateTimeFormatter( private static final DateFormatter DATE_HOUR_MINUTE_SECOND = new JavaDateFormatter("date_hour_minute_second",
STRICT_DATE_HOUR_MINUTE_SECOND.printer, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT),
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(DATE_FORMATTER) .append(DATE_FORMATTER)
.appendLiteral("T") .appendLiteral("T")
@ -994,8 +974,8 @@ public class DateFormatters {
* Returns a formatter that combines a full date and time, separated by a 'T' * Returns a formatter that combines a full date and time, separated by a 'T'
* (yyyy-MM-dd'T'HH:mm:ss.SSSZZ). * (yyyy-MM-dd'T'HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter DATE_TIME = new JavaDateFormatter("date_time",
STRICT_DATE_TIME.printer, STRICT_DATE_OPTIONAL_TIME_FORMATTER_1,
new DateTimeFormatterBuilder().append(DATE_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(DATE_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(DATE_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(DATE_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
); );
@ -1004,20 +984,22 @@ public class DateFormatters {
* Returns a basic formatter for a full date as four digit weekyear, two * Returns a basic formatter for a full date as four digit weekyear, two
* digit week of weekyear, and one digit day of week (YYYY'W'wwe). * digit week of weekyear, and one digit day of week (YYYY'W'wwe).
*/ */
private static final CompoundDateTimeFormatter BASIC_WEEK_DATE = private static final DateFormatter BASIC_WEEK_DATE =
new CompoundDateTimeFormatter(STRICT_BASIC_WEEK_DATE.printer, BASIC_WEEK_DATE_FORMATTER); new JavaDateFormatter("basic_week_date", STRICT_BASIC_WEEK_DATE_PRINTER, BASIC_WEEK_DATE_FORMATTER);
/* /*
* Returns a formatter for a full date as four digit year, two digit month * Returns a formatter for a full date as four digit year, two digit month
* of year, and two digit day of month (yyyy-MM-dd). * of year, and two digit day of month (yyyy-MM-dd).
*/ */
private static final CompoundDateTimeFormatter DATE = new CompoundDateTimeFormatter(STRICT_DATE.printer, DATE_FORMATTER); private static final DateFormatter DATE = new JavaDateFormatter("date",
DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT),
DATE_FORMATTER);
// only the formatter, nothing optional here // only the formatter, nothing optional here
private static final DateTimeFormatter DATE_TIME_NO_MILLIS_PRINTER = new DateTimeFormatterBuilder() private static final DateTimeFormatter DATE_TIME_NO_MILLIS_PRINTER = new DateTimeFormatterBuilder()
.append(STRICT_DATE.printer) .append(DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT))
.appendLiteral('T') .appendLiteral('T')
.append(STRICT_HOUR_MINUTE.printer) .appendPattern("HH:mm")
.appendLiteral(':') .appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE)
.appendZoneId() .appendZoneId()
@ -1037,7 +1019,8 @@ public class DateFormatters {
* Returns a formatter that combines a full date and time without millis, but with a timezone that can be optional * Returns a formatter that combines a full date and time without millis, but with a timezone that can be optional
* separated by a 'T' (yyyy-MM-dd'T'HH:mm:ssZ). * separated by a 'T' (yyyy-MM-dd'T'HH:mm:ssZ).
*/ */
private static final CompoundDateTimeFormatter DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter(DATE_TIME_NO_MILLIS_PRINTER, private static final DateFormatter DATE_TIME_NO_MILLIS = new JavaDateFormatter("date_time_no_millis",
DATE_TIME_NO_MILLIS_PRINTER,
new DateTimeFormatterBuilder().append(DATE_TIME_PREFIX).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(DATE_TIME_PREFIX).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(DATE_TIME_PREFIX).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(DATE_TIME_PREFIX).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(DATE_TIME_PREFIX) new DateTimeFormatterBuilder().append(DATE_TIME_PREFIX)
@ -1051,21 +1034,21 @@ public class DateFormatters {
* hour, two digit second of minute, and three digit fraction of * hour, two digit second of minute, and three digit fraction of
* second (HH:mm:ss.SSS). * second (HH:mm:ss.SSS).
*/ */
private static final CompoundDateTimeFormatter HOUR_MINUTE_SECOND_MILLIS = private static final DateFormatter HOUR_MINUTE_SECOND_MILLIS = new JavaDateFormatter("hour_minute_second_millis",
new CompoundDateTimeFormatter(STRICT_HOUR_MINUTE_SECOND_FRACTION.printer, HOUR_MINUTE_SECOND_MILLIS_FORMATTER); STRICT_HOUR_MINUTE_SECOND_MILLIS_PRINTER, HOUR_MINUTE_SECOND_MILLIS_FORMATTER);
/* /*
* Returns a formatter for a two digit hour of day and two digit minute of * Returns a formatter for a two digit hour of day and two digit minute of
* hour. (HH:mm) * hour. (HH:mm)
*/ */
private static final CompoundDateTimeFormatter HOUR_MINUTE = private static final DateFormatter HOUR_MINUTE =
new CompoundDateTimeFormatter(STRICT_HOUR_MINUTE.printer, HOUR_MINUTE_FORMATTER); new JavaDateFormatter("hour_minute", DateTimeFormatter.ofPattern("HH:mm", Locale.ROOT), HOUR_MINUTE_FORMATTER);
/* /*
* A strict formatter that formats or parses a hour, minute and second, such as '09:43:25'. * A strict formatter that formats or parses a hour, minute and second, such as '09:43:25'.
*/ */
private static final CompoundDateTimeFormatter HOUR_MINUTE_SECOND = new CompoundDateTimeFormatter( private static final DateFormatter HOUR_MINUTE_SECOND = new JavaDateFormatter("hour_minute_second",
STRICT_HOUR_MINUTE_SECOND.printer, STRICT_HOUR_MINUTE_SECOND_FORMATTER,
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.append(HOUR_MINUTE_FORMATTER) .append(HOUR_MINUTE_FORMATTER)
.appendLiteral(":") .appendLiteral(":")
@ -1076,8 +1059,8 @@ public class DateFormatters {
/* /*
* Returns a formatter for a two digit hour of day. (HH) * Returns a formatter for a two digit hour of day. (HH)
*/ */
private static final CompoundDateTimeFormatter HOUR = new CompoundDateTimeFormatter( private static final DateFormatter HOUR = new JavaDateFormatter("hour",
STRICT_HOUR.printer, DateTimeFormatter.ofPattern("HH", Locale.ROOT),
new DateTimeFormatterBuilder().appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE).toFormatter(Locale.ROOT)
); );
@ -1096,8 +1079,9 @@ public class DateFormatters {
* Returns a formatter for a full ordinal date and time, using a four * Returns a formatter for a full ordinal date and time, using a four
* digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ss.SSSZZ). * digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter ORDINAL_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter ORDINAL_DATE_TIME = new JavaDateFormatter("ordinal_date_time",
STRICT_ORDINAL_DATE_TIME.printer, new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_FORMATTER_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(ORDINAL_DATE_TIME_FORMATTER_BASE) new DateTimeFormatterBuilder().append(ORDINAL_DATE_TIME_FORMATTER_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(ORDINAL_DATE_TIME_FORMATTER_BASE) new DateTimeFormatterBuilder().append(ORDINAL_DATE_TIME_FORMATTER_BASE)
@ -1114,8 +1098,9 @@ public class DateFormatters {
* Returns a formatter for a full ordinal date and time without millis, * Returns a formatter for a full ordinal date and time without millis,
* using a four digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ssZZ). * using a four digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter ORDINAL_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter ORDINAL_DATE_TIME_NO_MILLIS = new JavaDateFormatter("ordinal_date_time_no_millis",
STRICT_ORDINAL_DATE_TIME_NO_MILLIS.printer, new DateTimeFormatterBuilder().append(STRICT_ORDINAL_DATE_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(ORDINAL_DATE_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().append(ORDINAL_DATE_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(ORDINAL_DATE_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().append(ORDINAL_DATE_TIME_NO_MILLIS_BASE)
@ -1126,8 +1111,8 @@ public class DateFormatters {
* Returns a formatter that combines a full weekyear date and time, * Returns a formatter that combines a full weekyear date and time,
* separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ). * separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter WEEK_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter WEEK_DATE_TIME = new JavaDateFormatter("week_date_time",
STRICT_WEEK_DATE_TIME.printer, new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T).append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).appendLiteral("T").append(TIME_PREFIX) new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).appendLiteral("T").append(TIME_PREFIX)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).appendLiteral("T").append(TIME_PREFIX) new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).appendLiteral("T").append(TIME_PREFIX)
@ -1138,8 +1123,9 @@ public class DateFormatters {
* Returns a formatter that combines a full weekyear date and time, * Returns a formatter that combines a full weekyear date and time,
* separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ssZZ). * separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter WEEK_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter WEEK_DATE_TIME_NO_MILLIS = new JavaDateFormatter("week_date_time_no_millis",
STRICT_WEEK_DATE_TIME_NO_MILLIS.printer, new DateTimeFormatterBuilder().append(ISO_WEEK_DATE_T)
.append(STRICT_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).append(T_TIME_NO_MILLIS_FORMATTER) new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).append(T_TIME_NO_MILLIS_FORMATTER)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).append(T_TIME_NO_MILLIS_FORMATTER) new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).append(T_TIME_NO_MILLIS_FORMATTER)
@ -1150,8 +1136,11 @@ public class DateFormatters {
* Returns a basic formatter that combines a basic weekyear date and time, * Returns a basic formatter that combines a basic weekyear date and time,
* separated by a 'T' (xxxx'W'wwe'T'HHmmss.SSSX). * separated by a 'T' (xxxx'W'wwe'T'HHmmss.SSSX).
*/ */
private static final CompoundDateTimeFormatter BASIC_WEEK_DATE_TIME = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_WEEK_DATE_TIME = new JavaDateFormatter("basic_week_date_time",
STRICT_BASIC_WEEK_DATE_TIME.printer, new DateTimeFormatterBuilder()
.append(STRICT_BASIC_WEEK_DATE_PRINTER)
.append(DateTimeFormatter.ofPattern("'T'HHmmss.SSSX", Locale.ROOT))
.toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_WEEK_DATE_FORMATTER).append(BASIC_T_TIME_FORMATTER) new DateTimeFormatterBuilder().append(BASIC_WEEK_DATE_FORMATTER).append(BASIC_T_TIME_FORMATTER)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_WEEK_DATE_FORMATTER).append(BASIC_T_TIME_FORMATTER) new DateTimeFormatterBuilder().append(BASIC_WEEK_DATE_FORMATTER).append(BASIC_T_TIME_FORMATTER)
@ -1162,8 +1151,10 @@ public class DateFormatters {
* Returns a basic formatter that combines a basic weekyear date and time, * Returns a basic formatter that combines a basic weekyear date and time,
* separated by a 'T' (xxxx'W'wwe'T'HHmmssX). * separated by a 'T' (xxxx'W'wwe'T'HHmmssX).
*/ */
private static final CompoundDateTimeFormatter BASIC_WEEK_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter BASIC_WEEK_DATE_TIME_NO_MILLIS = new JavaDateFormatter("basic_week_date_time_no_millis",
STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS.printer, new DateTimeFormatterBuilder()
.append(STRICT_BASIC_WEEK_DATE_PRINTER).append(DateTimeFormatter.ofPattern("'T'HHmmssX", Locale.ROOT))
.toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_WEEK_DATE_FORMATTER).appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().append(BASIC_WEEK_DATE_FORMATTER).appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(BASIC_WEEK_DATE_FORMATTER).appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE) new DateTimeFormatterBuilder().append(BASIC_WEEK_DATE_FORMATTER).appendLiteral("T").append(BASIC_TIME_NO_MILLIS_BASE)
@ -1175,8 +1166,8 @@ public class DateFormatters {
* hour, two digit second of minute, three digit fraction of second, and * hour, two digit second of minute, three digit fraction of second, and
* time zone offset (HH:mm:ss.SSSZZ). * time zone offset (HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter TIME = new CompoundDateTimeFormatter( private static final DateFormatter TIME = new JavaDateFormatter("time",
STRICT_TIME.printer, new DateTimeFormatterBuilder().append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(TIME_PREFIX).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(TIME_PREFIX).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(TIME_PREFIX).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(TIME_PREFIX).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
); );
@ -1185,8 +1176,8 @@ public class DateFormatters {
* Returns a formatter for a two digit hour of day, two digit minute of * Returns a formatter for a two digit hour of day, two digit minute of
* hour, two digit second of minute, andtime zone offset (HH:mm:ssZZ). * hour, two digit second of minute, andtime zone offset (HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter TIME_NO_MILLIS = new JavaDateFormatter("time_no_millis",
STRICT_TIME_NO_MILLIS.printer, new DateTimeFormatterBuilder().append(STRICT_TIME_NO_MILLIS_BASE).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(TIME_NO_MILLIS_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(TIME_NO_MILLIS_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(TIME_NO_MILLIS_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(TIME_NO_MILLIS_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
); );
@ -1196,8 +1187,8 @@ public class DateFormatters {
* hour, two digit second of minute, three digit fraction of second, and * hour, two digit second of minute, three digit fraction of second, and
* time zone offset prefixed by 'T' ('T'HH:mm:ss.SSSZZ). * time zone offset prefixed by 'T' ('T'HH:mm:ss.SSSZZ).
*/ */
private static final CompoundDateTimeFormatter T_TIME = new CompoundDateTimeFormatter( private static final DateFormatter T_TIME = new JavaDateFormatter("t_time",
STRICT_T_TIME.printer, new DateTimeFormatterBuilder().appendLiteral('T').append(STRICT_TIME_PRINTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendLiteral("T").append(TIME_PREFIX) new DateTimeFormatterBuilder().appendLiteral("T").append(TIME_PREFIX)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT), .appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendLiteral("T").append(TIME_PREFIX) new DateTimeFormatterBuilder().appendLiteral("T").append(TIME_PREFIX)
@ -1209,8 +1200,9 @@ public class DateFormatters {
* hour, two digit second of minute, and time zone offset prefixed * hour, two digit second of minute, and time zone offset prefixed
* by 'T' ('T'HH:mm:ssZZ). * by 'T' ('T'HH:mm:ssZZ).
*/ */
private static final CompoundDateTimeFormatter T_TIME_NO_MILLIS = new CompoundDateTimeFormatter( private static final DateFormatter T_TIME_NO_MILLIS = new JavaDateFormatter("t_time_no_millis",
STRICT_T_TIME_NO_MILLIS.printer, new DateTimeFormatterBuilder().appendLiteral("T").append(STRICT_TIME_NO_MILLIS_BASE)
.appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(T_TIME_NO_MILLIS_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(T_TIME_NO_MILLIS_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().append(T_TIME_NO_MILLIS_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().append(T_TIME_NO_MILLIS_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT)
); );
@ -1218,16 +1210,20 @@ public class DateFormatters {
/* /*
* A strict formatter that formats or parses a year and a month, such as '2011-12'. * A strict formatter that formats or parses a year and a month, such as '2011-12'.
*/ */
private static final CompoundDateTimeFormatter YEAR_MONTH = new CompoundDateTimeFormatter( private static final DateFormatter YEAR_MONTH = new JavaDateFormatter("year_month",
STRICT_YEAR_MONTH.printer, new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendLiteral("-")
.appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE)
.toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder().appendValue(ChronoField.YEAR).appendLiteral("-").appendValue(MONTH_OF_YEAR).toFormatter(Locale.ROOT) new DateTimeFormatterBuilder().appendValue(ChronoField.YEAR).appendLiteral("-").appendValue(MONTH_OF_YEAR).toFormatter(Locale.ROOT)
); );
/* /*
* A strict date formatter that formats or parses a date without an offset, such as '2011-12-03'. * A strict date formatter that formats or parses a date without an offset, such as '2011-12-03'.
*/ */
private static final CompoundDateTimeFormatter YEAR_MONTH_DAY = new CompoundDateTimeFormatter( private static final DateFormatter YEAR_MONTH_DAY = new JavaDateFormatter("year_month_day",
STRICT_YEAR_MONTH_DAY.printer, STRICT_YEAR_MONTH_DAY_FORMATTER,
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR) .appendValue(ChronoField.YEAR)
.appendLiteral("-") .appendLiteral("-")
@ -1241,13 +1237,13 @@ public class DateFormatters {
* Returns a formatter for a full date as four digit weekyear, two digit * Returns a formatter for a full date as four digit weekyear, two digit
* week of weekyear, and one digit day of week (xxxx-'W'ww-e). * week of weekyear, and one digit day of week (xxxx-'W'ww-e).
*/ */
private static final CompoundDateTimeFormatter WEEK_DATE = new CompoundDateTimeFormatter(STRICT_WEEK_DATE.printer, WEEK_DATE_FORMATTER); private static final DateFormatter WEEK_DATE = new JavaDateFormatter("week_date", ISO_WEEK_DATE, WEEK_DATE_FORMATTER);
/* /*
* Returns a formatter for a four digit weekyear and two digit week of * Returns a formatter for a four digit weekyear and two digit week of
* weekyear. (xxxx-'W'ww) * weekyear. (xxxx-'W'ww)
*/ */
private static final CompoundDateTimeFormatter WEEKYEAR_WEEK = new CompoundDateTimeFormatter(STRICT_WEEKYEAR_WEEK.printer, private static final DateFormatter WEEKYEAR_WEEK = new JavaDateFormatter("weekyear_week", STRICT_WEEKYEAR_WEEK_FORMATTER,
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.appendValue(WeekFields.ISO.weekBasedYear()) .appendValue(WeekFields.ISO.weekBasedYear())
.appendLiteral("-W") .appendLiteral("-W")
@ -1259,8 +1255,12 @@ public class DateFormatters {
* Returns a formatter for a four digit weekyear, two digit week of * Returns a formatter for a four digit weekyear, two digit week of
* weekyear, and one digit day of week. (xxxx-'W'ww-e) * weekyear, and one digit day of week. (xxxx-'W'ww-e)
*/ */
private static final CompoundDateTimeFormatter WEEKYEAR_WEEK_DAY = new CompoundDateTimeFormatter( private static final DateFormatter WEEKYEAR_WEEK_DAY = new JavaDateFormatter("weekyear_week_day",
STRICT_WEEKYEAR_WEEK_DAY.printer, new DateTimeFormatterBuilder()
.append(STRICT_WEEKYEAR_WEEK_FORMATTER)
.appendLiteral("-")
.appendValue(WeekFields.ISO.dayOfWeek())
.toFormatter(Locale.ROOT),
new DateTimeFormatterBuilder() new DateTimeFormatterBuilder()
.appendValue(WeekFields.ISO.weekBasedYear()) .appendValue(WeekFields.ISO.weekBasedYear())
.appendLiteral("-W") .appendLiteral("-W")
@ -1276,11 +1276,11 @@ public class DateFormatters {
// //
///////////////////////////////////////// /////////////////////////////////////////
public static CompoundDateTimeFormatter forPattern(String input) { public static DateFormatter forPattern(String input) {
return forPattern(input, Locale.ROOT); return forPattern(input, Locale.ROOT);
} }
public static CompoundDateTimeFormatter forPattern(String input, Locale locale) { public static DateFormatter forPattern(String input, Locale locale) {
if (Strings.hasLength(input)) { if (Strings.hasLength(input)) {
input = input.trim(); input = input.trim();
} }
@ -1452,21 +1452,20 @@ public class DateFormatters {
if (formats.length == 1) { if (formats.length == 1) {
return forPattern(formats[0], locale); return forPattern(formats[0], locale);
} else { } else {
Collection<DateTimeFormatter> parsers = new LinkedHashSet<>(formats.length);
for (String format : formats) {
CompoundDateTimeFormatter dateTimeFormatter = forPattern(format, locale);
try { try {
parsers.addAll(Arrays.asList(dateTimeFormatter.parsers)); DateFormatter[] formatters = new DateFormatter[formats.length];
for (int i = 0; i < formats.length; i++) {
formatters[i] = forPattern(formats[i], locale);
}
return DateFormatter.merge(formatters);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid format: [" + input + "]: " + e.getMessage(), e); throw new IllegalArgumentException("Invalid format: [" + input + "]: " + e.getMessage(), e);
} }
} }
return new CompoundDateTimeFormatter(parsers.toArray(new DateTimeFormatter[0]));
}
} else { } else {
try { try {
return new CompoundDateTimeFormatter(new DateTimeFormatterBuilder().appendPattern(input).toFormatter(locale)); return new JavaDateFormatter(input, new DateTimeFormatterBuilder().appendPattern(input).toFormatter(locale));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid format: [" + input + "]: " + e.getMessage(), e); throw new IllegalArgumentException("Invalid format: [" + input + "]: " + e.getMessage(), e);
} }

View File

@ -58,10 +58,10 @@ public class DateMathParser {
ROUND_UP_BASE_FIELDS.put(ChronoField.MILLI_OF_SECOND, 999L); ROUND_UP_BASE_FIELDS.put(ChronoField.MILLI_OF_SECOND, 999L);
} }
private final CompoundDateTimeFormatter formatter; private final DateFormatter formatter;
private final CompoundDateTimeFormatter roundUpFormatter; private final DateFormatter roundUpFormatter;
public DateMathParser(CompoundDateTimeFormatter formatter) { public DateMathParser(DateFormatter formatter) {
Objects.requireNonNull(formatter); Objects.requireNonNull(formatter);
this.formatter = formatter; this.formatter = formatter;
this.roundUpFormatter = formatter.parseDefaulting(ROUND_UP_BASE_FIELDS); this.roundUpFormatter = formatter.parseDefaulting(ROUND_UP_BASE_FIELDS);
@ -247,7 +247,7 @@ public class DateMathParser {
} }
private long parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNoTime) { private long parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNoTime) {
CompoundDateTimeFormatter formatter = roundUpIfNoTime ? this.roundUpFormatter : this.formatter; DateFormatter formatter = roundUpIfNoTime ? this.roundUpFormatter : this.formatter;
try { try {
if (timeZone == null) { if (timeZone == null) {
return DateFormatters.toZonedDateTime(formatter.parse(value)).toInstant().toEpochMilli(); return DateFormatters.toZonedDateTime(formatter.parse(value)).toInstant().toEpochMilli();

View File

@ -0,0 +1,73 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.time;
import java.time.Instant;
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.Map;
/**
* This is a special formatter to parse the milliseconds since the epoch.
* There is no way using a native java time date formatter to resemble
* the required behaviour to parse negative milliseconds as well.
*
* This implementation simply tries to convert the input to a long and uses
* this as the milliseconds since the epoch without involving any other
* java time code
*/
class EpochMillisDateFormatter implements DateFormatter {
public static DateFormatter INSTANCE = new EpochMillisDateFormatter();
private EpochMillisDateFormatter() {}
@Override
public TemporalAccessor parse(String input) {
try {
return Instant.ofEpochMilli(Long.valueOf(input)).atZone(ZoneOffset.UTC);
} catch (NumberFormatException e) {
throw new DateTimeParseException("invalid number", input, 0, e);
}
}
@Override
public DateFormatter withZone(ZoneId zoneId) {
return this;
}
@Override
public String format(TemporalAccessor accessor) {
return String.valueOf(Instant.from(accessor).toEpochMilli());
}
@Override
public String pattern() {
return "epoch_millis";
}
@Override
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
return this;
}
}

View File

@ -16,6 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.elasticsearch.common.time; package org.elasticsearch.common.time;
import java.time.ZoneId; import java.time.ZoneId;
@ -27,33 +28,28 @@ 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.Map;
import java.util.function.Consumer;
/** class JavaDateFormatter implements DateFormatter {
* wrapper class around java.time.DateTimeFormatter that supports multiple formats for easier parsing,
* and one specific format for printing
*/
public class CompoundDateTimeFormatter {
private static final Consumer<DateTimeFormatter[]> SAME_TIME_ZONE_VALIDATOR = (parsers) -> { private final String format;
private final DateTimeFormatter printer;
private final DateTimeFormatter[] parsers;
JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter... parsers) {
long distinctZones = Arrays.stream(parsers).map(DateTimeFormatter::getZone).distinct().count(); long distinctZones = Arrays.stream(parsers).map(DateTimeFormatter::getZone).distinct().count();
if (distinctZones > 1) { if (distinctZones > 1) {
throw new IllegalArgumentException("formatters must have the same time zone"); throw new IllegalArgumentException("formatters must have the same time zone");
} }
};
final DateTimeFormatter printer;
final DateTimeFormatter[] parsers;
CompoundDateTimeFormatter(DateTimeFormatter ... parsers) {
if (parsers.length == 0) { if (parsers.length == 0) {
throw new IllegalArgumentException("at least one date time formatter is required"); this.parsers = new DateTimeFormatter[]{printer};
} } else {
SAME_TIME_ZONE_VALIDATOR.accept(parsers);
this.printer = parsers[0];
this.parsers = parsers; this.parsers = parsers;
} }
this.format = format;
this.printer = printer;
}
@Override
public TemporalAccessor parse(String input) { public TemporalAccessor parse(String input) {
DateTimeParseException failure = null; DateTimeParseException failure = null;
for (int i = 0; i < parsers.length; i++) { for (int i = 0; i < parsers.length; i++) {
@ -72,13 +68,8 @@ public class CompoundDateTimeFormatter {
throw failure; throw failure;
} }
/** @Override
* Configure a specific time zone for a date formatter public DateFormatter withZone(ZoneId zoneId) {
*
* @param zoneId The zoneId this formatter shoulduse
* @return The new formatter with all parsers switched to the specified timezone
*/
public CompoundDateTimeFormatter withZone(ZoneId zoneId) {
// shortcurt to not create new objects unnecessarily // shortcurt to not create new objects unnecessarily
if (zoneId.equals(parsers[0].getZone())) { if (zoneId.equals(parsers[0].getZone())) {
return this; return this;
@ -89,25 +80,33 @@ public class CompoundDateTimeFormatter {
parsersWithZone[i] = parsers[i].withZone(zoneId); parsersWithZone[i] = parsers[i].withZone(zoneId);
} }
return new CompoundDateTimeFormatter(parsersWithZone); return new JavaDateFormatter(format, printer.withZone(zoneId), parsersWithZone);
} }
/** @Override
* Configure defaults for missing values in a parser, then return a new compound date formatter public String format(TemporalAccessor accessor) {
*/ return printer.format(accessor);
CompoundDateTimeFormatter parseDefaulting(Map<TemporalField, Long> fields) { }
@Override
public String pattern() {
return format;
}
@Override
public DateFormatter parseDefaulting(Map<TemporalField, Long> fields) {
final DateTimeFormatterBuilder parseDefaultingBuilder = new DateTimeFormatterBuilder().append(printer);
fields.forEach(parseDefaultingBuilder::parseDefaulting);
if (parsers.length == 1 && parsers[0].equals(printer)) {
return new JavaDateFormatter(format, parseDefaultingBuilder.toFormatter(Locale.ROOT));
} else {
final DateTimeFormatter[] parsersWithDefaulting = new DateTimeFormatter[parsers.length]; final DateTimeFormatter[] parsersWithDefaulting = new DateTimeFormatter[parsers.length];
for (int i = 0; i < parsers.length; i++) { for (int i = 0; i < parsers.length; i++) {
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().append(parsers[i]); DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().append(parsers[i]);
fields.forEach(builder::parseDefaulting); fields.forEach(builder::parseDefaulting);
parsersWithDefaulting[i] = builder.toFormatter(Locale.ROOT); parsersWithDefaulting[i] = builder.toFormatter(Locale.ROOT);
} }
return new JavaDateFormatter(format, parseDefaultingBuilder.toFormatter(Locale.ROOT), parsersWithDefaulting);
return new CompoundDateTimeFormatter(parsersWithDefaulting);
} }
public String format(TemporalAccessor accessor) {
return printer.format(accessor);
} }
} }

View File

@ -21,7 +21,7 @@ package org.elasticsearch.common.xcontent;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
@ -64,9 +64,9 @@ import java.util.function.Function;
public class XContentElasticsearchExtension implements XContentBuilderExtension { public class XContentElasticsearchExtension implements XContentBuilderExtension {
public static final DateTimeFormatter DEFAULT_DATE_PRINTER = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC); public static final DateTimeFormatter DEFAULT_DATE_PRINTER = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC);
public static final CompoundDateTimeFormatter DEFAULT_FORMATTER = DateFormatters.forPattern("strict_date_optional_time_nanos"); public static final DateFormatter DEFAULT_FORMATTER = DateFormatters.forPattern("strict_date_optional_time_nanos");
public static final CompoundDateTimeFormatter LOCAL_TIME_FORMATTER = DateFormatters.forPattern("HH:mm:ss.SSS"); public static final DateFormatter LOCAL_TIME_FORMATTER = DateFormatters.forPattern("HH:mm:ss.SSS");
public static final CompoundDateTimeFormatter OFFSET_TIME_FORMATTER = DateFormatters.forPattern("HH:mm:ss.SSSZZZZZ"); public static final DateFormatter OFFSET_TIME_FORMATTER = DateFormatters.forPattern("HH:mm:ss.SSSZZZZZ");
@Override @Override
public Map<Class<?>, XContentBuilder.Writer> getXContentWriters() { public Map<Class<?>, XContentBuilder.Writer> getXContentWriters() {

View File

@ -21,7 +21,7 @@ package org.elasticsearch.monitor.jvm;
import org.apache.lucene.util.CollectionUtil; import org.apache.lucene.util.CollectionUtil;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
@ -43,7 +43,7 @@ public class HotThreads {
private static final Object mutex = new Object(); private static final Object mutex = new Object();
private static final CompoundDateTimeFormatter DATE_TIME_FORMATTER = DateFormatters.forPattern("dateOptionalTime"); private static final DateFormatter DATE_TIME_FORMATTER = DateFormatters.forPattern("dateOptionalTime");
private int busiestThreads = 3; private int busiestThreads = 3;
private TimeValue interval = new TimeValue(500, TimeUnit.MILLISECONDS); private TimeValue interval = new TimeValue(500, TimeUnit.MILLISECONDS);

View File

@ -25,7 +25,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse
import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Table; import org.elasticsearch.common.Table;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestController;
@ -99,7 +99,7 @@ public class RestSnapshotAction extends AbstractCatAction {
.endHeaders(); .endHeaders();
} }
private static final CompoundDateTimeFormatter FORMATTER = DateFormatters.forPattern("HH:mm:ss").withZone(ZoneOffset.UTC); private static final DateFormatter FORMATTER = DateFormatters.forPattern("HH:mm:ss").withZone(ZoneOffset.UTC);
private Table buildTable(RestRequest req, GetSnapshotsResponse getSnapshotsResponse) { private Table buildTable(RestRequest req, GetSnapshotsResponse getSnapshotsResponse) {
Table table = getTableWithHeader(req); Table table = getTableWithHeader(req);

View File

@ -27,7 +27,7 @@ import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.Table; import org.elasticsearch.common.Table;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestController;
@ -125,7 +125,7 @@ public class RestTasksAction extends AbstractCatAction {
return table; return table;
} }
private static final CompoundDateTimeFormatter FORMATTER = DateFormatters.forPattern("HH:mm:ss").withZone(ZoneOffset.UTC); private static final DateFormatter FORMATTER = DateFormatters.forPattern("HH:mm:ss").withZone(ZoneOffset.UTC);
private void buildRow(Table table, boolean fullId, boolean detailed, DiscoveryNodes discoveryNodes, TaskInfo taskInfo) { private void buildRow(Table table, boolean fullId, boolean detailed, DiscoveryNodes discoveryNodes, TaskInfo taskInfo) {
table.startRow(); table.startRow();

View File

@ -27,7 +27,7 @@ import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser;
@ -52,7 +52,7 @@ public final class SnapshotInfo implements Comparable<SnapshotInfo>, ToXContent,
public static final String CONTEXT_MODE_PARAM = "context_mode"; public static final String CONTEXT_MODE_PARAM = "context_mode";
public static final String CONTEXT_MODE_SNAPSHOT = "SNAPSHOT"; public static final String CONTEXT_MODE_SNAPSHOT = "SNAPSHOT";
private static final CompoundDateTimeFormatter DATE_TIME_FORMATTER = DateFormatters.forPattern("strictDateOptionalTime"); private static final DateFormatter DATE_TIME_FORMATTER = DateFormatters.forPattern("strictDateOptionalTime");
private static final String SNAPSHOT = "snapshot"; private static final String SNAPSHOT = "snapshot";
private static final String UUID = "uuid"; private static final String UUID = "uuid";
private static final String INDICES = "indices"; private static final String INDICES = "indices";

View File

@ -19,7 +19,7 @@
package org.elasticsearch.common.joda; package org.elasticsearch.common.joda;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -485,7 +485,7 @@ public class JavaJodaTimeDuellingTests extends ESTestCase {
FormatDateTimeFormatter jodaFormatter = Joda.forPattern(format); FormatDateTimeFormatter jodaFormatter = Joda.forPattern(format);
DateTime jodaDateTime = jodaFormatter.parser().parseDateTime(input); DateTime jodaDateTime = jodaFormatter.parser().parseDateTime(input);
CompoundDateTimeFormatter javaTimeFormatter = DateFormatters.forPattern(format); DateFormatter javaTimeFormatter = DateFormatters.forPattern(format);
TemporalAccessor javaTimeAccessor = javaTimeFormatter.parse(input); TemporalAccessor javaTimeAccessor = javaTimeFormatter.parse(input);
ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(javaTimeAccessor); ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(javaTimeAccessor);
@ -507,7 +507,7 @@ public class JavaJodaTimeDuellingTests extends ESTestCase {
} }
private void assertJavaTimeParseException(String input, String format, String expectedMessage) { private void assertJavaTimeParseException(String input, String format, String expectedMessage) {
CompoundDateTimeFormatter javaTimeFormatter = DateFormatters.forPattern(format); DateFormatter javaTimeFormatter = DateFormatters.forPattern(format);
DateTimeParseException dateTimeParseException = expectThrows(DateTimeParseException.class, () -> javaTimeFormatter.parse(input)); DateTimeParseException dateTimeParseException = expectThrows(DateTimeParseException.class, () -> javaTimeFormatter.parse(input));
assertThat(dateTimeParseException.getMessage(), startsWith(expectedMessage)); assertThat(dateTimeParseException.getMessage(), startsWith(expectedMessage));
} }

View File

@ -31,17 +31,15 @@ import static org.hamcrest.Matchers.is;
public class DateFormattersTests extends ESTestCase { public class DateFormattersTests extends ESTestCase {
// the epoch milli parser is a bit special, as it does not use date formatter, see comments in DateFormatters
public void testEpochMilliParser() { public void testEpochMilliParser() {
CompoundDateTimeFormatter formatter = DateFormatters.forPattern("epoch_millis"); DateFormatter formatter = DateFormatters.forPattern("epoch_millis");
DateTimeParseException e = expectThrows(DateTimeParseException.class, () -> formatter.parse("invalid")); DateTimeParseException e = expectThrows(DateTimeParseException.class, () -> formatter.parse("invalid"));
assertThat(e.getMessage(), containsString("invalid number")); assertThat(e.getMessage(), containsString("invalid number"));
// different zone, should still yield the same output, as epoch is time zoned independent // different zone, should still yield the same output, as epoch is time zone independent
ZoneId zoneId = randomZone(); ZoneId zoneId = randomZone();
CompoundDateTimeFormatter zonedFormatter = formatter.withZone(zoneId); DateFormatter zonedFormatter = formatter.withZone(zoneId);
assertThat(zonedFormatter.printer.getZone(), is(zoneId));
// test with negative and non negative values // test with negative and non negative values
assertThatSameDateTime(formatter, zonedFormatter, randomNonNegativeLong() * -1); assertThatSameDateTime(formatter, zonedFormatter, randomNonNegativeLong() * -1);
@ -58,14 +56,21 @@ public class DateFormattersTests extends ESTestCase {
assertSameFormat(formatter, 1); assertSameFormat(formatter, 1);
} }
private void assertThatSameDateTime(CompoundDateTimeFormatter formatter, CompoundDateTimeFormatter zonedFormatter, long millis) { public void testEpochMilliParsersWithDifferentFormatters() {
DateFormatter formatter = DateFormatters.forPattern("strict_date_optional_time||epoch_millis");
TemporalAccessor accessor = formatter.parse("123");
assertThat(DateFormatters.toZonedDateTime(accessor).toInstant().toEpochMilli(), is(123L));
assertThat(formatter.pattern(), is("strict_date_optional_time||epoch_millis"));
}
private void assertThatSameDateTime(DateFormatter formatter, DateFormatter zonedFormatter, long millis) {
String millisAsString = String.valueOf(millis); String millisAsString = String.valueOf(millis);
ZonedDateTime formatterZonedDateTime = DateFormatters.toZonedDateTime(formatter.parse(millisAsString)); ZonedDateTime formatterZonedDateTime = DateFormatters.toZonedDateTime(formatter.parse(millisAsString));
ZonedDateTime zonedFormatterZonedDateTime = DateFormatters.toZonedDateTime(zonedFormatter.parse(millisAsString)); ZonedDateTime zonedFormatterZonedDateTime = DateFormatters.toZonedDateTime(zonedFormatter.parse(millisAsString));
assertThat(formatterZonedDateTime.toInstant().toEpochMilli(), is(zonedFormatterZonedDateTime.toInstant().toEpochMilli())); assertThat(formatterZonedDateTime.toInstant().toEpochMilli(), is(zonedFormatterZonedDateTime.toInstant().toEpochMilli()));
} }
private void assertSameFormat(CompoundDateTimeFormatter formatter, long millis) { private void assertSameFormat(DateFormatter formatter, long millis) {
String millisAsString = String.valueOf(millis); String millisAsString = String.valueOf(millis);
TemporalAccessor accessor = formatter.parse(millisAsString); TemporalAccessor accessor = formatter.parse(millisAsString);
assertThat(millisAsString, is(formatter.format(accessor))); assertThat(millisAsString, is(formatter.format(accessor)));

View File

@ -35,7 +35,7 @@ import static org.hamcrest.Matchers.is;
public class DateMathParserTests extends ESTestCase { public class DateMathParserTests extends ESTestCase {
private final CompoundDateTimeFormatter formatter = DateFormatters.forPattern("dateOptionalTime||epoch_millis"); private final DateFormatter formatter = DateFormatters.forPattern("dateOptionalTime||epoch_millis");
private final DateMathParser parser = new DateMathParser(formatter); private final DateMathParser parser = new DateMathParser(formatter);
public void testBasicDates() { public void testBasicDates() {
@ -138,7 +138,7 @@ public class DateMathParserTests 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
CompoundDateTimeFormatter formatter = DateFormatters.forPattern("HH:mm:ss"); DateFormatter formatter = DateFormatters.forPattern("HH:mm:ss");
DateMathParser parser = new DateMathParser(formatter); DateMathParser parser = new DateMathParser(formatter);
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));
@ -164,7 +164,7 @@ public class DateMathParserTests extends ESTestCase {
assertDateMathEquals("2014-11-18T09:20", "2014-11-18T08:20:59.999Z", 0, true, ZoneId.of("CET")); assertDateMathEquals("2014-11-18T09:20", "2014-11-18T08:20:59.999Z", 0, true, ZoneId.of("CET"));
// implicit rounding with explicit timezone in the date format // implicit rounding with explicit timezone in the date format
CompoundDateTimeFormatter formatter = DateFormatters.forPattern("yyyy-MM-ddXXX"); DateFormatter formatter = DateFormatters.forPattern("yyyy-MM-ddXXX");
DateMathParser parser = new DateMathParser(formatter); DateMathParser parser = new DateMathParser(formatter);
long time = parser.parse("2011-10-09+01:00", () -> 0, false, null); long time = parser.parse("2011-10-09+01:00", () -> 0, false, 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);

View File

@ -26,7 +26,7 @@ import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.elasticsearch.common.time.CompoundDateTimeFormatter; import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.search.aggregations.BaseAggregationTestCase; import org.elasticsearch.search.aggregations.BaseAggregationTestCase;
@ -137,7 +137,7 @@ public class DateHistogramTests extends BaseAggregationTestCase<DateHistogramAgg
} }
public void testRewriteTimeZone() throws IOException { public void testRewriteTimeZone() throws IOException {
CompoundDateTimeFormatter format = DateFormatters.forPattern("strict_date_optional_time"); DateFormatter format = DateFormatters.forPattern("strict_date_optional_time");
try (Directory dir = newDirectory(); try (Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) { IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {