Core: Abstract DateMathParser in an interface (#33905)
This commits creates a DateMathParser interface, which is already implemented for both joda and java time. While currently the java time DateMathParser is not used, this change will allow a followup which will create a DateMathParser from a DateFormatter, so the caller does not need to know the internals of the DateFormatter they have.
This commit is contained in:
parent
ff2bbdf765
commit
7800b4fa91
|
@ -27,10 +27,11 @@ import org.elasticsearch.common.Nullable;
|
|||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateUtils;
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
|
@ -923,8 +924,9 @@ public class IndexNameExpressionResolver extends AbstractComponent {
|
|||
}
|
||||
DateTimeFormatter parser = dateFormatter.withZone(timeZone);
|
||||
FormatDateTimeFormatter formatter = new FormatDateTimeFormatter(dateFormatterPattern, parser, Locale.ROOT);
|
||||
DateMathParser dateMathParser = new DateMathParser(formatter);
|
||||
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false, timeZone);
|
||||
DateMathParser dateMathParser = formatter.toDateMathParser();
|
||||
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false,
|
||||
DateUtils.dateTimeZoneToZoneId(timeZone));
|
||||
|
||||
String time = formatter.printer().print(millis);
|
||||
beforePlaceHolderSb.append(time);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.common.joda;
|
||||
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
@ -64,4 +65,8 @@ public class FormatDateTimeFormatter {
|
|||
public Locale locale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public DateMathParser toDateMathParser() {
|
||||
return new JodaDateMathParser(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,13 @@
|
|||
package org.elasticsearch.common.joda;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateUtils;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.MutableDateTime;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.Objects;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
|
@ -34,23 +37,21 @@ import java.util.function.LongSupplier;
|
|||
* is appended to a datetime with the following syntax:
|
||||
* <code>||[+-/](\d+)?[yMwdhHms]</code>.
|
||||
*/
|
||||
public class DateMathParser {
|
||||
public class JodaDateMathParser implements DateMathParser {
|
||||
|
||||
private final FormatDateTimeFormatter dateTimeFormatter;
|
||||
|
||||
public DateMathParser(FormatDateTimeFormatter dateTimeFormatter) {
|
||||
public JodaDateMathParser(FormatDateTimeFormatter dateTimeFormatter) {
|
||||
Objects.requireNonNull(dateTimeFormatter);
|
||||
this.dateTimeFormatter = dateTimeFormatter;
|
||||
}
|
||||
|
||||
public long parse(String text, LongSupplier now) {
|
||||
return parse(text, now, false, null);
|
||||
}
|
||||
|
||||
// Note: we take a callable here for the timestamp in order to be able to figure out
|
||||
// if it has been used. For instance, the request cache does not cache requests that make
|
||||
// use of `now`.
|
||||
public long parse(String text, LongSupplier now, boolean roundUp, DateTimeZone timeZone) {
|
||||
@Override
|
||||
public long parse(String text, LongSupplier now, boolean roundUp, ZoneId tz) {
|
||||
final DateTimeZone timeZone = tz == null ? null : DateUtils.zoneIdToDateTimeZone(tz);
|
||||
long time;
|
||||
String mathString;
|
||||
if (text.startsWith("now")) {
|
|
@ -19,56 +19,31 @@
|
|||
|
||||
package org.elasticsearch.common.time;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.temporal.TemporalQueries;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
/**
|
||||
* A parser for date/time formatted text with optional date math.
|
||||
*
|
||||
* The format of the datetime is configurable, and unix timestamps can also be used. Datemath
|
||||
* is appended to a datetime with the following syntax:
|
||||
* <code>||[+-/](\d+)?[yMwdhHms]</code>.
|
||||
* An abstraction over date math parsing to allow different implementation for joda and java time.
|
||||
*/
|
||||
public class DateMathParser {
|
||||
public interface DateMathParser {
|
||||
|
||||
// base fields which should be used for default parsing, when we round up
|
||||
private static final Map<TemporalField, Long> ROUND_UP_BASE_FIELDS = new HashMap<>(6);
|
||||
{
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.HOUR_OF_DAY, 23L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MINUTE_OF_HOUR, 59L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.SECOND_OF_MINUTE, 59L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MILLI_OF_SECOND, 999L);
|
||||
/**
|
||||
* Parse a date math expression without timzeone info and rounding down.
|
||||
*/
|
||||
default long parse(String text, LongSupplier now) {
|
||||
return parse(text, now, false, (ZoneId) null);
|
||||
}
|
||||
|
||||
private final DateFormatter formatter;
|
||||
private final DateFormatter roundUpFormatter;
|
||||
// Note: we take a callable here for the timestamp in order to be able to figure out
|
||||
// if it has been used. For instance, the request cache does not cache requests that make
|
||||
// use of `now`.
|
||||
|
||||
public DateMathParser(DateFormatter formatter) {
|
||||
Objects.requireNonNull(formatter);
|
||||
this.formatter = formatter;
|
||||
this.roundUpFormatter = formatter.parseDefaulting(ROUND_UP_BASE_FIELDS);
|
||||
}
|
||||
|
||||
public long parse(String text, LongSupplier now) {
|
||||
return parse(text, now, false, null);
|
||||
// exists for backcompat, do not use!
|
||||
@Deprecated
|
||||
default long parse(String text, LongSupplier now, boolean roundUp, DateTimeZone tz) {
|
||||
return parse(text, now, roundUp, tz == null ? null : ZoneId.of(tz.getID()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,176 +67,8 @@ public class DateMathParser {
|
|||
* @param text the input
|
||||
* @param now a supplier to retrieve the current date in milliseconds, if needed for additions
|
||||
* @param roundUp should the result be rounded up
|
||||
* @param timeZone an optional timezone that should be applied before returning the milliseconds since the epoch
|
||||
* @param tz an optional timezone that should be applied before returning the milliseconds since the epoch
|
||||
* @return the parsed date in milliseconds since the epoch
|
||||
*/
|
||||
public long parse(String text, LongSupplier now, boolean roundUp, ZoneId timeZone) {
|
||||
long time;
|
||||
String mathString;
|
||||
if (text.startsWith("now")) {
|
||||
try {
|
||||
time = now.getAsLong();
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchParseException("could not read the current timestamp", e);
|
||||
}
|
||||
mathString = text.substring("now".length());
|
||||
} else {
|
||||
int index = text.indexOf("||");
|
||||
if (index == -1) {
|
||||
return parseDateTime(text, timeZone, roundUp);
|
||||
}
|
||||
time = parseDateTime(text.substring(0, index), timeZone, false);
|
||||
mathString = text.substring(index + 2);
|
||||
}
|
||||
|
||||
return parseMath(mathString, time, roundUp, timeZone);
|
||||
}
|
||||
|
||||
private long parseMath(final String mathString, final long time, final boolean roundUp,
|
||||
ZoneId timeZone) throws ElasticsearchParseException {
|
||||
if (timeZone == null) {
|
||||
timeZone = ZoneOffset.UTC;
|
||||
}
|
||||
ZonedDateTime dateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(time), timeZone);
|
||||
for (int i = 0; i < mathString.length(); ) {
|
||||
char c = mathString.charAt(i++);
|
||||
final boolean round;
|
||||
final int sign;
|
||||
if (c == '/') {
|
||||
round = true;
|
||||
sign = 1;
|
||||
} else {
|
||||
round = false;
|
||||
if (c == '+') {
|
||||
sign = 1;
|
||||
} else if (c == '-') {
|
||||
sign = -1;
|
||||
} else {
|
||||
throw new ElasticsearchParseException("operator not supported for date math [{}]", mathString);
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= mathString.length()) {
|
||||
throw new ElasticsearchParseException("truncated date math [{}]", mathString);
|
||||
}
|
||||
|
||||
final int num;
|
||||
if (!Character.isDigit(mathString.charAt(i))) {
|
||||
num = 1;
|
||||
} else {
|
||||
int numFrom = i;
|
||||
while (i < mathString.length() && Character.isDigit(mathString.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
if (i >= mathString.length()) {
|
||||
throw new ElasticsearchParseException("truncated date math [{}]", mathString);
|
||||
}
|
||||
num = Integer.parseInt(mathString.substring(numFrom, i));
|
||||
}
|
||||
if (round) {
|
||||
if (num != 1) {
|
||||
throw new ElasticsearchParseException("rounding `/` can only be used on single unit types [{}]", mathString);
|
||||
}
|
||||
}
|
||||
char unit = mathString.charAt(i++);
|
||||
switch (unit) {
|
||||
case 'y':
|
||||
if (round) {
|
||||
dateTime = dateTime.withDayOfYear(1).with(LocalTime.MIN);
|
||||
} else {
|
||||
dateTime = dateTime.plusYears(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusYears(1);
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
if (round) {
|
||||
dateTime = dateTime.withDayOfMonth(1).with(LocalTime.MIN);
|
||||
} else {
|
||||
dateTime = dateTime.plusMonths(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusMonths(1);
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
if (round) {
|
||||
dateTime = dateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).with(LocalTime.MIN);
|
||||
} else {
|
||||
dateTime = dateTime.plusWeeks(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusWeeks(1);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (round) {
|
||||
dateTime = dateTime.with(LocalTime.MIN);
|
||||
} else {
|
||||
dateTime = dateTime.plusDays(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusDays(1);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
if (round) {
|
||||
dateTime = dateTime.withMinute(0).withSecond(0).withNano(0);
|
||||
} else {
|
||||
dateTime = dateTime.plusHours(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusHours(1);
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (round) {
|
||||
dateTime = dateTime.withSecond(0).withNano(0);
|
||||
} else {
|
||||
dateTime = dateTime.plusMinutes(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusMinutes(1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (round) {
|
||||
dateTime = dateTime.withNano(0);
|
||||
} else {
|
||||
dateTime = dateTime.plusSeconds(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusSeconds(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ElasticsearchParseException("unit [{}] not supported for date math [{}]", unit, mathString);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.minus(1, ChronoField.MILLI_OF_SECOND.getBaseUnit());
|
||||
}
|
||||
}
|
||||
return dateTime.toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
private long parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNoTime) {
|
||||
DateFormatter formatter = roundUpIfNoTime ? this.roundUpFormatter : this.formatter;
|
||||
try {
|
||||
if (timeZone == null) {
|
||||
return DateFormatters.toZonedDateTime(formatter.parse(value)).toInstant().toEpochMilli();
|
||||
} else {
|
||||
TemporalAccessor accessor = formatter.parse(value);
|
||||
ZoneId zoneId = TemporalQueries.zone().queryFrom(accessor);
|
||||
if (zoneId != null) {
|
||||
timeZone = zoneId;
|
||||
}
|
||||
|
||||
return DateFormatters.toZonedDateTime(accessor).withZoneSameLocal(timeZone).toInstant().toEpochMilli();
|
||||
}
|
||||
} catch (IllegalArgumentException | DateTimeException e) {
|
||||
throw new ElasticsearchParseException("failed to parse date field [{}]: [{}]", e, value, e.getMessage());
|
||||
}
|
||||
}
|
||||
long parse(String text, LongSupplier now, boolean roundUp, ZoneId tz);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 org.apache.logging.log4j.LogManager;
|
||||
import org.elasticsearch.common.logging.DeprecationLogger;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DateUtils {
|
||||
public static DateTimeZone zoneIdToDateTimeZone(ZoneId zoneId) {
|
||||
if (zoneId == null) {
|
||||
return null;
|
||||
}
|
||||
if (zoneId instanceof ZoneOffset) {
|
||||
// the id for zoneoffset is not ISO compatible, so cannot be read by ZoneId.of
|
||||
return DateTimeZone.forOffsetMillis(((ZoneOffset)zoneId).getTotalSeconds() * 1000);
|
||||
}
|
||||
return DateTimeZone.forID(zoneId.getId());
|
||||
}
|
||||
|
||||
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(LogManager.getLogger(DateFormatters.class));
|
||||
// pkg private for tests
|
||||
static final Map<String, String> DEPRECATED_SHORT_TIMEZONES;
|
||||
static {
|
||||
Map<String, String> tzs = new HashMap<>();
|
||||
tzs.put("EST", "-05:00"); // eastern time without daylight savings
|
||||
tzs.put("HST", "-10:00");
|
||||
tzs.put("MST", "-07:00");
|
||||
tzs.put("ROC", "Asia/Taipei");
|
||||
tzs.put("Eire", "Europe/London");
|
||||
DEPRECATED_SHORT_TIMEZONES = Collections.unmodifiableMap(tzs);
|
||||
}
|
||||
|
||||
public static ZoneId dateTimeZoneToZoneId(DateTimeZone timeZone) {
|
||||
if (timeZone == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String deprecatedId = DEPRECATED_SHORT_TIMEZONES.get(timeZone.getID());
|
||||
if (deprecatedId != null) {
|
||||
DEPRECATION_LOGGER.deprecatedAndMaybeLog("timezone",
|
||||
"Use of short timezone id " + timeZone.getID() + " is deprecated. Use " + deprecatedId + " instead");
|
||||
return ZoneId.of(deprecatedId);
|
||||
}
|
||||
return ZoneId.of(timeZone.getID());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* 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 org.elasticsearch.ElasticsearchParseException;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.temporal.TemporalQueries;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
/**
|
||||
* A parser for date/time formatted text with optional date math.
|
||||
*
|
||||
* The format of the datetime is configurable, and unix timestamps can also be used. Datemath
|
||||
* is appended to a datetime with the following syntax:
|
||||
* <code>||[+-/](\d+)?[yMwdhHms]</code>.
|
||||
*/
|
||||
public class JavaDateMathParser implements DateMathParser {
|
||||
|
||||
// base fields which should be used for default parsing, when we round up
|
||||
private static final Map<TemporalField, Long> ROUND_UP_BASE_FIELDS = new HashMap<>(6);
|
||||
{
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.HOUR_OF_DAY, 23L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MINUTE_OF_HOUR, 59L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.SECOND_OF_MINUTE, 59L);
|
||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MILLI_OF_SECOND, 999L);
|
||||
}
|
||||
|
||||
private final DateFormatter formatter;
|
||||
private final DateFormatter roundUpFormatter;
|
||||
|
||||
public JavaDateMathParser(DateFormatter formatter) {
|
||||
Objects.requireNonNull(formatter);
|
||||
this.formatter = formatter;
|
||||
this.roundUpFormatter = formatter.parseDefaulting(ROUND_UP_BASE_FIELDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long parse(String text, LongSupplier now, boolean roundUp, ZoneId timeZone) {
|
||||
long time;
|
||||
String mathString;
|
||||
if (text.startsWith("now")) {
|
||||
try {
|
||||
time = now.getAsLong();
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchParseException("could not read the current timestamp", e);
|
||||
}
|
||||
mathString = text.substring("now".length());
|
||||
} else {
|
||||
int index = text.indexOf("||");
|
||||
if (index == -1) {
|
||||
return parseDateTime(text, timeZone, roundUp);
|
||||
}
|
||||
time = parseDateTime(text.substring(0, index), timeZone, false);
|
||||
mathString = text.substring(index + 2);
|
||||
}
|
||||
|
||||
return parseMath(mathString, time, roundUp, timeZone);
|
||||
}
|
||||
|
||||
private long parseMath(final String mathString, final long time, final boolean roundUp,
|
||||
ZoneId timeZone) throws ElasticsearchParseException {
|
||||
if (timeZone == null) {
|
||||
timeZone = ZoneOffset.UTC;
|
||||
}
|
||||
ZonedDateTime dateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(time), timeZone);
|
||||
for (int i = 0; i < mathString.length(); ) {
|
||||
char c = mathString.charAt(i++);
|
||||
final boolean round;
|
||||
final int sign;
|
||||
if (c == '/') {
|
||||
round = true;
|
||||
sign = 1;
|
||||
} else {
|
||||
round = false;
|
||||
if (c == '+') {
|
||||
sign = 1;
|
||||
} else if (c == '-') {
|
||||
sign = -1;
|
||||
} else {
|
||||
throw new ElasticsearchParseException("operator not supported for date math [{}]", mathString);
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= mathString.length()) {
|
||||
throw new ElasticsearchParseException("truncated date math [{}]", mathString);
|
||||
}
|
||||
|
||||
final int num;
|
||||
if (!Character.isDigit(mathString.charAt(i))) {
|
||||
num = 1;
|
||||
} else {
|
||||
int numFrom = i;
|
||||
while (i < mathString.length() && Character.isDigit(mathString.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
if (i >= mathString.length()) {
|
||||
throw new ElasticsearchParseException("truncated date math [{}]", mathString);
|
||||
}
|
||||
num = Integer.parseInt(mathString.substring(numFrom, i));
|
||||
}
|
||||
if (round) {
|
||||
if (num != 1) {
|
||||
throw new ElasticsearchParseException("rounding `/` can only be used on single unit types [{}]", mathString);
|
||||
}
|
||||
}
|
||||
char unit = mathString.charAt(i++);
|
||||
switch (unit) {
|
||||
case 'y':
|
||||
if (round) {
|
||||
dateTime = dateTime.withDayOfYear(1).with(LocalTime.MIN);
|
||||
} else {
|
||||
dateTime = dateTime.plusYears(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusYears(1);
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
if (round) {
|
||||
dateTime = dateTime.withDayOfMonth(1).with(LocalTime.MIN);
|
||||
} else {
|
||||
dateTime = dateTime.plusMonths(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusMonths(1);
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
if (round) {
|
||||
dateTime = dateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).with(LocalTime.MIN);
|
||||
} else {
|
||||
dateTime = dateTime.plusWeeks(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusWeeks(1);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (round) {
|
||||
dateTime = dateTime.with(LocalTime.MIN);
|
||||
} else {
|
||||
dateTime = dateTime.plusDays(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusDays(1);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
if (round) {
|
||||
dateTime = dateTime.withMinute(0).withSecond(0).withNano(0);
|
||||
} else {
|
||||
dateTime = dateTime.plusHours(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusHours(1);
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (round) {
|
||||
dateTime = dateTime.withSecond(0).withNano(0);
|
||||
} else {
|
||||
dateTime = dateTime.plusMinutes(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusMinutes(1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (round) {
|
||||
dateTime = dateTime.withNano(0);
|
||||
} else {
|
||||
dateTime = dateTime.plusSeconds(sign * num);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.plusSeconds(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ElasticsearchParseException("unit [{}] not supported for date math [{}]", unit, mathString);
|
||||
}
|
||||
if (roundUp) {
|
||||
dateTime = dateTime.minus(1, ChronoField.MILLI_OF_SECOND.getBaseUnit());
|
||||
}
|
||||
}
|
||||
return dateTime.toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
private long parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNoTime) {
|
||||
DateFormatter formatter = roundUpIfNoTime ? this.roundUpFormatter : this.formatter;
|
||||
try {
|
||||
if (timeZone == null) {
|
||||
return DateFormatters.toZonedDateTime(formatter.parse(value)).toInstant().toEpochMilli();
|
||||
} else {
|
||||
TemporalAccessor accessor = formatter.parse(value);
|
||||
ZoneId zoneId = TemporalQueries.zone().queryFrom(accessor);
|
||||
if (zoneId != null) {
|
||||
timeZone = zoneId;
|
||||
}
|
||||
|
||||
return DateFormatters.toZonedDateTime(accessor).withZoneSameLocal(timeZone).toInstant().toEpochMilli();
|
||||
}
|
||||
} catch (IllegalArgumentException | DateTimeException e) {
|
||||
throw new ElasticsearchParseException("failed to parse date field [{}]: [{}]", e, value, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,10 +36,11 @@ import org.apache.lucene.util.BytesRef;
|
|||
import org.elasticsearch.common.Explicit;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateUtils;
|
||||
import org.elasticsearch.common.util.LocaleUtils;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||
|
@ -231,7 +232,7 @@ public class DateFieldMapper extends FieldMapper {
|
|||
public void setDateTimeFormatter(FormatDateTimeFormatter dateTimeFormatter) {
|
||||
checkIfFrozen();
|
||||
this.dateTimeFormatter = dateTimeFormatter;
|
||||
this.dateMathParser = new DateMathParser(dateTimeFormatter);
|
||||
this.dateMathParser = dateTimeFormatter.toDateMathParser();
|
||||
}
|
||||
|
||||
protected DateMathParser dateMathParser() {
|
||||
|
@ -296,8 +297,8 @@ public class DateFieldMapper extends FieldMapper {
|
|||
return query;
|
||||
}
|
||||
|
||||
public long parseToMilliseconds(Object value, boolean roundUp,
|
||||
@Nullable DateTimeZone zone, @Nullable DateMathParser forcedDateParser, QueryRewriteContext context) {
|
||||
public long parseToMilliseconds(Object value, boolean roundUp, @Nullable DateTimeZone zone,
|
||||
@Nullable DateMathParser forcedDateParser, QueryRewriteContext context) {
|
||||
DateMathParser dateParser = dateMathParser();
|
||||
if (forcedDateParser != null) {
|
||||
dateParser = forcedDateParser;
|
||||
|
@ -309,13 +310,13 @@ public class DateFieldMapper extends FieldMapper {
|
|||
} else {
|
||||
strValue = value.toString();
|
||||
}
|
||||
return dateParser.parse(strValue, context::nowInMillis, roundUp, zone);
|
||||
return dateParser.parse(strValue, context::nowInMillis, roundUp, DateUtils.dateTimeZoneToZoneId(zone));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relation isFieldWithinQuery(IndexReader reader,
|
||||
Object from, Object to, boolean includeLower, boolean includeUpper,
|
||||
DateTimeZone timeZone, DateMathParser dateParser, QueryRewriteContext context) throws IOException {
|
||||
public Relation isFieldWithinQuery(IndexReader reader, Object from, Object to, boolean includeLower, boolean includeUpper,
|
||||
DateTimeZone timeZone, DateMathParser dateParser,
|
||||
QueryRewriteContext context) throws IOException {
|
||||
if (dateParser == null) {
|
||||
dateParser = this.dateMathParser;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ import org.apache.lucene.util.BytesRef;
|
|||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.unit.Fuzziness;
|
||||
import org.elasticsearch.index.analysis.NamedAnalyzer;
|
||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||
|
|
|
@ -44,11 +44,12 @@ import org.elasticsearch.common.Explicit;
|
|||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.network.InetAddresses;
|
||||
import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateUtils;
|
||||
import org.elasticsearch.common.util.LocaleUtils;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
@ -60,6 +61,7 @@ import org.joda.time.DateTimeZone;
|
|||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -257,7 +259,7 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
public void setDateTimeFormatter(FormatDateTimeFormatter dateTimeFormatter) {
|
||||
checkIfFrozen();
|
||||
this.dateTimeFormatter = dateTimeFormatter;
|
||||
this.dateMathParser = new DateMathParser(dateTimeFormatter);
|
||||
this.dateMathParser = dateTimeFormatter.toDateMathParser();
|
||||
}
|
||||
|
||||
protected DateMathParser dateMathParser() {
|
||||
|
@ -588,14 +590,15 @@ public class RangeFieldMapper extends FieldMapper {
|
|||
boolean includeUpper, ShapeRelation relation, @Nullable DateTimeZone timeZone,
|
||||
@Nullable DateMathParser parser, QueryShardContext context) {
|
||||
DateTimeZone zone = (timeZone == null) ? DateTimeZone.UTC : timeZone;
|
||||
ZoneId zoneId = DateUtils.dateTimeZoneToZoneId(zone);
|
||||
DateMathParser dateMathParser = (parser == null) ?
|
||||
new DateMathParser(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER) : parser;
|
||||
DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.toDateMathParser() : parser;
|
||||
Long low = lowerTerm == null ? Long.MIN_VALUE :
|
||||
dateMathParser.parse(lowerTerm instanceof BytesRef ? ((BytesRef) lowerTerm).utf8ToString() : lowerTerm.toString(),
|
||||
context::nowInMillis, false, zone);
|
||||
context::nowInMillis, false, zoneId);
|
||||
Long high = upperTerm == null ? Long.MAX_VALUE :
|
||||
dateMathParser.parse(upperTerm instanceof BytesRef ? ((BytesRef) upperTerm).utf8ToString() : upperTerm.toString(),
|
||||
context::nowInMillis, false, zone);
|
||||
context::nowInMillis, false, zoneId);
|
||||
|
||||
return super.rangeQuery(field, hasDocValues, low, high, includeLower, includeUpper, relation, zone,
|
||||
dateMathParser, context);
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.index.mapper;
|
|||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ import org.elasticsearch.common.Strings;
|
|||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.lucene.BytesRefs;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
|
||||
|
@ -302,7 +302,7 @@ public class RangeQueryBuilder extends AbstractQueryBuilder<RangeQueryBuilder> i
|
|||
|
||||
DateMathParser getForceDateParser() { // pkg private for testing
|
||||
if (this.format != null) {
|
||||
return new DateMathParser(this.format);
|
||||
return this.format.toDateMathParser();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -25,11 +25,12 @@ import org.elasticsearch.common.geo.GeoHashUtils;
|
|||
import org.elasticsearch.common.io.stream.NamedWriteable;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.network.InetAddresses;
|
||||
import org.elasticsearch.common.network.NetworkAddress;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateUtils;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -171,13 +172,14 @@ public interface DocValueFormat extends NamedWriteable {
|
|||
public static final String NAME = "date_time";
|
||||
|
||||
final FormatDateTimeFormatter formatter;
|
||||
// TODO: change this to ZoneId, but will require careful change to serialization
|
||||
final DateTimeZone timeZone;
|
||||
private final DateMathParser parser;
|
||||
|
||||
public DateTime(FormatDateTimeFormatter formatter, DateTimeZone timeZone) {
|
||||
this.formatter = Objects.requireNonNull(formatter);
|
||||
this.timeZone = Objects.requireNonNull(timeZone);
|
||||
this.parser = new DateMathParser(formatter);
|
||||
this.parser = formatter.toDateMathParser();
|
||||
}
|
||||
|
||||
public DateTime(StreamInput in) throws IOException {
|
||||
|
@ -212,7 +214,7 @@ public interface DocValueFormat extends NamedWriteable {
|
|||
|
||||
@Override
|
||||
public long parseLong(String value, boolean roundUp, LongSupplier now) {
|
||||
return parser.parse(value, now, roundUp, timeZone);
|
||||
return parser.parse(value, now, roundUp, DateUtils.dateTimeZoneToZoneId(timeZone));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,10 +25,10 @@ import org.apache.lucene.index.SortedNumericDocValues;
|
|||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.rounding.DateTimeUnit;
|
||||
import org.elasticsearch.common.rounding.Rounding;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -72,7 +72,7 @@ import static java.util.Collections.unmodifiableMap;
|
|||
public class DateHistogramAggregationBuilder extends ValuesSourceAggregationBuilder<ValuesSource.Numeric, DateHistogramAggregationBuilder>
|
||||
implements MultiBucketAggregationBuilder {
|
||||
public static final String NAME = "date_histogram";
|
||||
private static DateMathParser EPOCH_MILLIS_PARSER = new DateMathParser(Joda.forPattern("epoch_millis", Locale.ROOT));
|
||||
private static DateMathParser EPOCH_MILLIS_PARSER = Joda.forPattern("epoch_millis", Locale.ROOT).toDateMathParser();
|
||||
|
||||
public static final Map<String, DateTimeUnit> DATE_FIELD_UNITS;
|
||||
|
||||
|
|
|
@ -24,17 +24,17 @@ import org.elasticsearch.ExceptionsHelper;
|
|||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.util.TimeZone;
|
||||
import java.time.ZoneId;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class DateMathParserTests extends ESTestCase {
|
||||
public class JodaDateMathParserTests extends ESTestCase {
|
||||
|
||||
FormatDateTimeFormatter formatter = Joda.forPattern("dateOptionalTime||epoch_millis");
|
||||
DateMathParser parser = new DateMathParser(formatter);
|
||||
JodaDateMathParser parser = new JodaDateMathParser(formatter);
|
||||
|
||||
void assertDateMathEquals(String toTest, String expected) {
|
||||
assertDateMathEquals(toTest, expected, 0, false, null);
|
||||
|
@ -145,7 +145,7 @@ public class DateMathParserTests extends ESTestCase {
|
|||
|
||||
|
||||
public void testNow() {
|
||||
final long now = parser.parse("2014-11-18T14:27:32", () -> 0, false, null);
|
||||
final long now = parser.parse("2014-11-18T14:27:32", () -> 0, false, (ZoneId) null);
|
||||
|
||||
assertDateMathEquals("now", "2014-11-18T14:27:32", now, false, null);
|
||||
assertDateMathEquals("now+M", "2014-12-18T14:27:32", now, false, null);
|
||||
|
@ -159,13 +159,13 @@ public class DateMathParserTests extends ESTestCase {
|
|||
public void testRoundingPreservesEpochAsBaseDate() {
|
||||
// If a user only specifies times, then the date needs to always be 1970-01-01 regardless of rounding
|
||||
FormatDateTimeFormatter formatter = Joda.forPattern("HH:mm:ss");
|
||||
DateMathParser parser = new DateMathParser(formatter);
|
||||
JodaDateMathParser parser = new JodaDateMathParser(formatter);
|
||||
assertEquals(
|
||||
this.formatter.parser().parseMillis("1970-01-01T04:52:20.000Z"),
|
||||
parser.parse("04:52:20", () -> 0, false, null));
|
||||
parser.parse("04:52:20", () -> 0, false, (ZoneId) null));
|
||||
assertEquals(
|
||||
this.formatter.parser().parseMillis("1970-01-01T04:52:20.999Z"),
|
||||
parser.parse("04:52:20", () -> 0, true, null));
|
||||
parser.parse("04:52:20", () -> 0, true, (ZoneId) null));
|
||||
}
|
||||
|
||||
// Implicit rounding happening when parts of the date are not specified
|
||||
|
@ -184,10 +184,10 @@ public class DateMathParserTests extends ESTestCase {
|
|||
|
||||
// implicit rounding with explicit timezone in the date format
|
||||
FormatDateTimeFormatter formatter = Joda.forPattern("YYYY-MM-ddZ");
|
||||
DateMathParser parser = new DateMathParser(formatter);
|
||||
long time = parser.parse("2011-10-09+01:00", () -> 0, false, null);
|
||||
JodaDateMathParser parser = new JodaDateMathParser(formatter);
|
||||
long time = parser.parse("2011-10-09+01:00", () -> 0, false, (ZoneId) null);
|
||||
assertEquals(this.parser.parse("2011-10-09T00:00:00.000+01:00", () -> 0), time);
|
||||
time = parser.parse("2011-10-09+01:00", () -> 0, true, null);
|
||||
time = parser.parse("2011-10-09+01:00", () -> 0, true, (ZoneId) null);
|
||||
assertEquals(this.parser.parse("2011-10-09T23:59:59.999+01:00", () -> 0), time);
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,7 @@ public class DateMathParserTests extends ESTestCase {
|
|||
assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000");
|
||||
|
||||
// also check other time units
|
||||
DateMathParser parser = new DateMathParser(Joda.forPattern("epoch_second||dateOptionalTime"));
|
||||
JodaDateMathParser parser = new JodaDateMathParser(Joda.forPattern("epoch_second||dateOptionalTime"));
|
||||
long datetime = parser.parse("1418248078", () -> 0);
|
||||
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
|
||||
|
||||
|
@ -298,16 +298,16 @@ public class DateMathParserTests extends ESTestCase {
|
|||
called.set(true);
|
||||
return 42L;
|
||||
};
|
||||
parser.parse("2014-11-18T14:27:32", now, false, null);
|
||||
parser.parse("2014-11-18T14:27:32", now, false, (ZoneId) null);
|
||||
assertFalse(called.get());
|
||||
parser.parse("now/d", now, false, null);
|
||||
parser.parse("now/d", now, false, (ZoneId) null);
|
||||
assertTrue(called.get());
|
||||
}
|
||||
|
||||
public void testThatUnixTimestampMayNotHaveTimeZone() {
|
||||
DateMathParser parser = new DateMathParser(Joda.forPattern("epoch_millis"));
|
||||
JodaDateMathParser parser = new JodaDateMathParser(Joda.forPattern("epoch_millis"));
|
||||
try {
|
||||
parser.parse("1234567890123", () -> 42, false, DateTimeZone.forTimeZone(TimeZone.getTimeZone("CET")));
|
||||
parser.parse("1234567890123", () -> 42, false, ZoneId.of("CET"));
|
||||
fail("Expected ElasticsearchParseException");
|
||||
} catch(ElasticsearchParseException e) {
|
||||
assertThat(e.getMessage(), containsString("failed to parse date field"));
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 org.elasticsearch.test.ESTestCase;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class DateUtilsTests extends ESTestCase {
|
||||
private static final Set<String> IGNORE = new HashSet<>(Arrays.asList(
|
||||
"Eire", "Europe/Dublin" // dublin timezone in joda does not account for DST
|
||||
));
|
||||
public void testTimezoneIds() {
|
||||
assertNull(DateUtils.dateTimeZoneToZoneId(null));
|
||||
assertNull(DateUtils.zoneIdToDateTimeZone(null));
|
||||
for (String jodaId : DateTimeZone.getAvailableIDs()) {
|
||||
if (IGNORE.contains(jodaId)) continue;
|
||||
DateTimeZone jodaTz = DateTimeZone.forID(jodaId);
|
||||
ZoneId zoneId = DateUtils.dateTimeZoneToZoneId(jodaTz); // does not throw
|
||||
long now = 0;
|
||||
assertThat(jodaId, zoneId.getRules().getOffset(Instant.ofEpochMilli(now)).getTotalSeconds() * 1000,
|
||||
equalTo(jodaTz.getOffset(now)));
|
||||
if (DateUtils.DEPRECATED_SHORT_TIMEZONES.containsKey(jodaTz.getID())) {
|
||||
assertWarnings("Use of short timezone id " + jodaId + " is deprecated. Use " + zoneId.getId() + " instead");
|
||||
}
|
||||
// roundtrip does not throw either
|
||||
assertNotNull(DateUtils.zoneIdToDateTimeZone(zoneId));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,10 +33,10 @@ import java.util.function.LongSupplier;
|
|||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class DateMathParserTests extends ESTestCase {
|
||||
public class JavaDateMathParserTests extends ESTestCase {
|
||||
|
||||
private final DateFormatter formatter = DateFormatters.forPattern("dateOptionalTime||epoch_millis");
|
||||
private final DateMathParser parser = new DateMathParser(formatter);
|
||||
private final JavaDateMathParser parser = new JavaDateMathParser(formatter);
|
||||
|
||||
public void testBasicDates() {
|
||||
assertDateMathEquals("2014", "2014-01-01T00:00:00.000");
|
||||
|
@ -125,7 +125,7 @@ public class DateMathParserTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testNow() {
|
||||
final long now = parser.parse("2014-11-18T14:27:32", () -> 0, false, null);
|
||||
final long now = parser.parse("2014-11-18T14:27:32", () -> 0, false, (ZoneId) null);
|
||||
|
||||
assertDateMathEquals("now", "2014-11-18T14:27:32", now, false, null);
|
||||
assertDateMathEquals("now+M", "2014-12-18T14:27:32", now, false, null);
|
||||
|
@ -139,14 +139,14 @@ public class DateMathParserTests extends ESTestCase {
|
|||
public void testRoundingPreservesEpochAsBaseDate() {
|
||||
// If a user only specifies times, then the date needs to always be 1970-01-01 regardless of rounding
|
||||
DateFormatter formatter = DateFormatters.forPattern("HH:mm:ss");
|
||||
DateMathParser parser = new DateMathParser(formatter);
|
||||
JavaDateMathParser parser = new JavaDateMathParser(formatter);
|
||||
ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(formatter.parse("04:52:20"));
|
||||
assertThat(zonedDateTime.getYear(), is(1970));
|
||||
long millisStart = zonedDateTime.toInstant().toEpochMilli();
|
||||
assertEquals(millisStart, parser.parse("04:52:20", () -> 0, false, null));
|
||||
assertEquals(millisStart, parser.parse("04:52:20", () -> 0, false, (ZoneId) null));
|
||||
// due to rounding up, we have to add the number of milliseconds here manually
|
||||
long millisEnd = DateFormatters.toZonedDateTime(formatter.parse("04:52:20")).toInstant().toEpochMilli() + 999;
|
||||
assertEquals(millisEnd, parser.parse("04:52:20", () -> 0, true, null));
|
||||
assertEquals(millisEnd, parser.parse("04:52:20", () -> 0, true, (ZoneId) null));
|
||||
}
|
||||
|
||||
// Implicit rounding happening when parts of the date are not specified
|
||||
|
@ -165,10 +165,10 @@ public class DateMathParserTests extends ESTestCase {
|
|||
|
||||
// implicit rounding with explicit timezone in the date format
|
||||
DateFormatter formatter = DateFormatters.forPattern("yyyy-MM-ddXXX");
|
||||
DateMathParser parser = new DateMathParser(formatter);
|
||||
long time = parser.parse("2011-10-09+01:00", () -> 0, false, null);
|
||||
JavaDateMathParser parser = new JavaDateMathParser(formatter);
|
||||
long time = parser.parse("2011-10-09+01:00", () -> 0, false, (ZoneId) null);
|
||||
assertEquals(this.parser.parse("2011-10-09T00:00:00.000+01:00", () -> 0), time);
|
||||
time = parser.parse("2011-10-09+01:00", () -> 0, true, null);
|
||||
time = parser.parse("2011-10-09+01:00", () -> 0, true, (ZoneId) null);
|
||||
assertEquals(this.parser.parse("2011-10-09T23:59:59.999+01:00", () -> 0), time);
|
||||
}
|
||||
|
||||
|
@ -239,7 +239,7 @@ public class DateMathParserTests extends ESTestCase {
|
|||
assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000");
|
||||
|
||||
// also check other time units
|
||||
DateMathParser parser = new DateMathParser(DateFormatters.forPattern("epoch_second||dateOptionalTime"));
|
||||
JavaDateMathParser parser = new JavaDateMathParser(DateFormatters.forPattern("epoch_second||dateOptionalTime"));
|
||||
long datetime = parser.parse("1418248078", () -> 0);
|
||||
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
|
||||
|
||||
|
@ -279,9 +279,9 @@ public class DateMathParserTests extends ESTestCase {
|
|||
called.set(true);
|
||||
return 42L;
|
||||
};
|
||||
parser.parse("2014-11-18T14:27:32", now, false, null);
|
||||
parser.parse("2014-11-18T14:27:32", now, false, (ZoneId) null);
|
||||
assertFalse(called.get());
|
||||
parser.parse("now/d", now, false, null);
|
||||
parser.parse("now/d", now, false, (ZoneId) null);
|
||||
assertTrue(called.get());
|
||||
}
|
||||
|
|
@ -29,12 +29,12 @@ import org.apache.lucene.index.MultiReader;
|
|||
import org.apache.lucene.search.IndexOrDocValuesQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.elasticsearch.core.internal.io.IOUtils;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.core.internal.io.IOUtils;
|
||||
import org.elasticsearch.index.IndexSettings;
|
||||
import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType.Relation;
|
||||
|
@ -121,7 +121,7 @@ public class DateFieldTypeTests extends FieldTypeTestCase {
|
|||
DirectoryReader reader = DirectoryReader.open(w);
|
||||
DateFieldType ft = new DateFieldType();
|
||||
ft.setName("my_date");
|
||||
DateMathParser alternateFormat = new DateMathParser(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER);
|
||||
DateMathParser alternateFormat = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.toDateMathParser();
|
||||
doTestIsFieldWithinQuery(ft, reader, null, null);
|
||||
doTestIsFieldWithinQuery(ft, reader, null, alternateFormat);
|
||||
doTestIsFieldWithinQuery(ft, reader, DateTimeZone.UTC, null);
|
||||
|
|
|
@ -31,8 +31,8 @@ import org.apache.lucene.search.Query;
|
|||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.network.InetAddresses;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.index.query.QueryStringQueryBuilder;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
|
|
|
@ -23,9 +23,9 @@ import org.elasticsearch.action.index.IndexRequestBuilder;
|
|||
import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.mapper.DateFieldMapper;
|
||||
import org.elasticsearch.index.query.MatchNoneQueryBuilder;
|
||||
|
@ -1120,7 +1120,7 @@ public class DateHistogramIT extends ESIntegTestCase {
|
|||
.setSettings(Settings.builder().put(indexSettings()).put("index.number_of_shards", 1).put("index.number_of_replicas", 0))
|
||||
.execute().actionGet();
|
||||
|
||||
DateMathParser parser = new DateMathParser(Joda.getStrictStandardDateFormatter());
|
||||
DateMathParser parser = Joda.getStrictStandardDateFormatter().toDateMathParser();
|
||||
|
||||
// we pick a random timezone offset of +12/-12 hours and insert two documents
|
||||
// one at 00:00 in that time zone and one at 12:00
|
||||
|
|
|
@ -408,7 +408,7 @@ public abstract class ESTestCase extends LuceneTestCase {
|
|||
}
|
||||
try {
|
||||
final List<String> actualWarnings = threadContext.getResponseHeaders().get("Warning");
|
||||
assertNotNull(actualWarnings);
|
||||
assertNotNull("no warnings, expected: " + Arrays.asList(expectedWarnings), actualWarnings);
|
||||
final Set<String> actualWarningValues =
|
||||
actualWarnings.stream().map(DeprecationLogger::extractWarningValueFromWarningHeader).collect(Collectors.toSet());
|
||||
for (String msg : expectedWarnings) {
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
package org.elasticsearch.license.licensor;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -38,8 +38,7 @@ public class TestUtils {
|
|||
|
||||
private static final FormatDateTimeFormatter formatDateTimeFormatter =
|
||||
Joda.forPattern("yyyy-MM-dd");
|
||||
private static final DateMathParser dateMathParser =
|
||||
new DateMathParser(formatDateTimeFormatter);
|
||||
private static final DateMathParser dateMathParser = formatDateTimeFormatter.toDateMathParser();
|
||||
private static final DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer();
|
||||
|
||||
public static String dumpLicense(License license) throws Exception {
|
||||
|
|
|
@ -16,7 +16,7 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
|
@ -88,7 +88,7 @@ public class GetOverallBucketsAction extends Action<GetOverallBucketsAction.Resp
|
|||
}
|
||||
|
||||
static long parseDateOrThrow(String date, ParseField paramName, LongSupplier now) {
|
||||
DateMathParser dateMathParser = new DateMathParser(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER);
|
||||
DateMathParser dateMathParser = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.toDateMathParser();
|
||||
|
||||
try {
|
||||
return dateMathParser.parse(date, now);
|
||||
|
|
|
@ -17,7 +17,7 @@ import org.elasticsearch.client.ElasticsearchClient;
|
|||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
|
@ -153,7 +153,7 @@ public class StartDatafeedAction extends Action<AcknowledgedResponse> {
|
|||
}
|
||||
|
||||
static long parseDateOrThrow(String date, ParseField paramName, LongSupplier now) {
|
||||
DateMathParser dateMathParser = new DateMathParser(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER);
|
||||
DateMathParser dateMathParser = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.toDateMathParser();
|
||||
|
||||
try {
|
||||
return dateMathParser.parse(date, now);
|
||||
|
|
|
@ -9,8 +9,8 @@ import org.elasticsearch.ElasticsearchParseException;
|
|||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit;
|
|||
public class WatcherDateTimeUtils {
|
||||
|
||||
public static final FormatDateTimeFormatter dateTimeFormatter = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER;
|
||||
public static final DateMathParser dateMathParser = new DateMathParser(dateTimeFormatter);
|
||||
public static final DateMathParser dateMathParser = dateTimeFormatter.toDateMathParser();
|
||||
|
||||
private WatcherDateTimeUtils() {
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ import org.elasticsearch.Version;
|
|||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -50,7 +50,7 @@ import static org.junit.Assert.assertThat;
|
|||
public class TestUtils {
|
||||
|
||||
private static final FormatDateTimeFormatter formatDateTimeFormatter = Joda.forPattern("yyyy-MM-dd");
|
||||
private static final DateMathParser dateMathParser = new DateMathParser(formatDateTimeFormatter);
|
||||
private static final DateMathParser dateMathParser = formatDateTimeFormatter.toDateMathParser();
|
||||
private static final DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer();
|
||||
|
||||
public static String dateMathString(String time, final long now) {
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.elasticsearch.action.search.SearchRequest;
|
|||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchResponseSections;
|
||||
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.rounding.Rounding;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
@ -47,13 +46,13 @@ import org.elasticsearch.search.aggregations.AggregatorTestCase;
|
|||
import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregation;
|
||||
import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder;
|
||||
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
||||
import org.elasticsearch.xpack.core.indexing.IndexerState;
|
||||
import org.elasticsearch.xpack.core.rollup.ConfigTestHelpers;
|
||||
import org.elasticsearch.xpack.core.rollup.job.DateHistogramGroupConfig;
|
||||
import org.elasticsearch.xpack.core.rollup.job.GroupConfig;
|
||||
import org.elasticsearch.xpack.core.rollup.job.MetricConfig;
|
||||
import org.elasticsearch.xpack.core.rollup.job.RollupJob;
|
||||
import org.elasticsearch.xpack.core.rollup.job.RollupJobConfig;
|
||||
import org.elasticsearch.xpack.core.indexing.IndexerState;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Before;
|
||||
|
@ -601,7 +600,7 @@ public class RollupIndexerIndexingTests extends AggregatorTestCase {
|
|||
RangeQueryBuilder range = (RangeQueryBuilder) request.source().query();
|
||||
final DateTimeZone timeZone = range.timeZone() != null ? DateTimeZone.forID(range.timeZone()) : null;
|
||||
Query query = timestampField.rangeQuery(range.from(), range.to(), range.includeLower(), range.includeUpper(),
|
||||
null, timeZone, new DateMathParser(Joda.forPattern(range.format())), queryShardContext);
|
||||
null, timeZone, Joda.forPattern(range.format()).toDateMathParser(), queryShardContext);
|
||||
|
||||
// extract composite agg
|
||||
assertThat(request.source().aggregations().getAggregatorFactories().size(), equalTo(1));
|
||||
|
|
Loading…
Reference in New Issue