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.Strings;
|
||||||
import org.elasticsearch.common.collect.Tuple;
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
import org.elasticsearch.common.regex.Regex;
|
import org.elasticsearch.common.regex.Regex;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
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.common.util.set.Sets;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.index.IndexNotFoundException;
|
import org.elasticsearch.index.IndexNotFoundException;
|
||||||
|
@ -923,8 +924,9 @@ public class IndexNameExpressionResolver extends AbstractComponent {
|
||||||
}
|
}
|
||||||
DateTimeFormatter parser = dateFormatter.withZone(timeZone);
|
DateTimeFormatter parser = dateFormatter.withZone(timeZone);
|
||||||
FormatDateTimeFormatter formatter = new FormatDateTimeFormatter(dateFormatterPattern, parser, Locale.ROOT);
|
FormatDateTimeFormatter formatter = new FormatDateTimeFormatter(dateFormatterPattern, parser, Locale.ROOT);
|
||||||
DateMathParser dateMathParser = new DateMathParser(formatter);
|
DateMathParser dateMathParser = formatter.toDateMathParser();
|
||||||
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false, timeZone);
|
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false,
|
||||||
|
DateUtils.dateTimeZoneToZoneId(timeZone));
|
||||||
|
|
||||||
String time = formatter.printer().print(millis);
|
String time = formatter.printer().print(millis);
|
||||||
beforePlaceHolderSb.append(time);
|
beforePlaceHolderSb.append(time);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.common.joda;
|
package org.elasticsearch.common.joda;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.joda.time.format.DateTimeFormatter;
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -64,4 +65,8 @@ public class FormatDateTimeFormatter {
|
||||||
public Locale locale() {
|
public Locale locale() {
|
||||||
return locale;
|
return locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateMathParser toDateMathParser() {
|
||||||
|
return new JodaDateMathParser(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,13 @@
|
||||||
package org.elasticsearch.common.joda;
|
package org.elasticsearch.common.joda;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
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.DateTimeZone;
|
||||||
import org.joda.time.MutableDateTime;
|
import org.joda.time.MutableDateTime;
|
||||||
import org.joda.time.format.DateTimeFormatter;
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.LongSupplier;
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
|
@ -34,23 +37,21 @@ import java.util.function.LongSupplier;
|
||||||
* is appended to a datetime with the following syntax:
|
* is appended to a datetime with the following syntax:
|
||||||
* <code>||[+-/](\d+)?[yMwdhHms]</code>.
|
* <code>||[+-/](\d+)?[yMwdhHms]</code>.
|
||||||
*/
|
*/
|
||||||
public class DateMathParser {
|
public class JodaDateMathParser implements DateMathParser {
|
||||||
|
|
||||||
private final FormatDateTimeFormatter dateTimeFormatter;
|
private final FormatDateTimeFormatter dateTimeFormatter;
|
||||||
|
|
||||||
public DateMathParser(FormatDateTimeFormatter dateTimeFormatter) {
|
public JodaDateMathParser(FormatDateTimeFormatter dateTimeFormatter) {
|
||||||
Objects.requireNonNull(dateTimeFormatter);
|
Objects.requireNonNull(dateTimeFormatter);
|
||||||
this.dateTimeFormatter = 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
|
// 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
|
// if it has been used. For instance, the request cache does not cache requests that make
|
||||||
// use of `now`.
|
// 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;
|
long time;
|
||||||
String mathString;
|
String mathString;
|
||||||
if (text.startsWith("now")) {
|
if (text.startsWith("now")) {
|
|
@ -19,56 +19,31 @@
|
||||||
|
|
||||||
package org.elasticsearch.common.time;
|
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.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;
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A parser for date/time formatted text with optional date math.
|
* An abstraction over date math parsing to allow different implementation for joda and java time.
|
||||||
*
|
|
||||||
* 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 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);
|
* Parse a date math expression without timzeone info and rounding down.
|
||||||
{
|
*/
|
||||||
ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L);
|
default long parse(String text, LongSupplier now) {
|
||||||
ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L);
|
return parse(text, now, false, (ZoneId) null);
|
||||||
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;
|
// Note: we take a callable here for the timestamp in order to be able to figure out
|
||||||
private final DateFormatter roundUpFormatter;
|
// if it has been used. For instance, the request cache does not cache requests that make
|
||||||
|
// use of `now`.
|
||||||
|
|
||||||
public DateMathParser(DateFormatter formatter) {
|
// exists for backcompat, do not use!
|
||||||
Objects.requireNonNull(formatter);
|
@Deprecated
|
||||||
this.formatter = formatter;
|
default long parse(String text, LongSupplier now, boolean roundUp, DateTimeZone tz) {
|
||||||
this.roundUpFormatter = formatter.parseDefaulting(ROUND_UP_BASE_FIELDS);
|
return parse(text, now, roundUp, tz == null ? null : ZoneId.of(tz.getID()));
|
||||||
}
|
|
||||||
|
|
||||||
public long parse(String text, LongSupplier now) {
|
|
||||||
return parse(text, now, false, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,176 +67,8 @@ public class DateMathParser {
|
||||||
* @param text the input
|
* @param text the input
|
||||||
* @param now a supplier to retrieve the current date in milliseconds, if needed for additions
|
* @param now a supplier to retrieve the current date in milliseconds, if needed for additions
|
||||||
* @param roundUp should the result be rounded up
|
* @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
|
* @return the parsed date in milliseconds since the epoch
|
||||||
*/
|
*/
|
||||||
public long parse(String text, LongSupplier now, boolean roundUp, ZoneId timeZone) {
|
long parse(String text, LongSupplier now, boolean roundUp, ZoneId tz);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.Explicit;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.geo.ShapeRelation;
|
import org.elasticsearch.common.geo.ShapeRelation;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
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.util.LocaleUtils;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||||
|
@ -231,7 +232,7 @@ public class DateFieldMapper extends FieldMapper {
|
||||||
public void setDateTimeFormatter(FormatDateTimeFormatter dateTimeFormatter) {
|
public void setDateTimeFormatter(FormatDateTimeFormatter dateTimeFormatter) {
|
||||||
checkIfFrozen();
|
checkIfFrozen();
|
||||||
this.dateTimeFormatter = dateTimeFormatter;
|
this.dateTimeFormatter = dateTimeFormatter;
|
||||||
this.dateMathParser = new DateMathParser(dateTimeFormatter);
|
this.dateMathParser = dateTimeFormatter.toDateMathParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DateMathParser dateMathParser() {
|
protected DateMathParser dateMathParser() {
|
||||||
|
@ -296,8 +297,8 @@ public class DateFieldMapper extends FieldMapper {
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long parseToMilliseconds(Object value, boolean roundUp,
|
public long parseToMilliseconds(Object value, boolean roundUp, @Nullable DateTimeZone zone,
|
||||||
@Nullable DateTimeZone zone, @Nullable DateMathParser forcedDateParser, QueryRewriteContext context) {
|
@Nullable DateMathParser forcedDateParser, QueryRewriteContext context) {
|
||||||
DateMathParser dateParser = dateMathParser();
|
DateMathParser dateParser = dateMathParser();
|
||||||
if (forcedDateParser != null) {
|
if (forcedDateParser != null) {
|
||||||
dateParser = forcedDateParser;
|
dateParser = forcedDateParser;
|
||||||
|
@ -309,13 +310,13 @@ public class DateFieldMapper extends FieldMapper {
|
||||||
} else {
|
} else {
|
||||||
strValue = value.toString();
|
strValue = value.toString();
|
||||||
}
|
}
|
||||||
return dateParser.parse(strValue, context::nowInMillis, roundUp, zone);
|
return dateParser.parse(strValue, context::nowInMillis, roundUp, DateUtils.dateTimeZoneToZoneId(zone));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Relation isFieldWithinQuery(IndexReader reader,
|
public Relation isFieldWithinQuery(IndexReader reader, Object from, Object to, boolean includeLower, boolean includeUpper,
|
||||||
Object from, Object to, boolean includeLower, boolean includeUpper,
|
DateTimeZone timeZone, DateMathParser dateParser,
|
||||||
DateTimeZone timeZone, DateMathParser dateParser, QueryRewriteContext context) throws IOException {
|
QueryRewriteContext context) throws IOException {
|
||||||
if (dateParser == null) {
|
if (dateParser == null) {
|
||||||
dateParser = this.dateMathParser;
|
dateParser = this.dateMathParser;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.geo.ShapeRelation;
|
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.common.unit.Fuzziness;
|
||||||
import org.elasticsearch.index.analysis.NamedAnalyzer;
|
import org.elasticsearch.index.analysis.NamedAnalyzer;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
|
|
|
@ -44,11 +44,12 @@ import org.elasticsearch.common.Explicit;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.collect.Tuple;
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
import org.elasticsearch.common.geo.ShapeRelation;
|
import org.elasticsearch.common.geo.ShapeRelation;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
import org.elasticsearch.common.network.InetAddresses;
|
import org.elasticsearch.common.network.InetAddresses;
|
||||||
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.DateMathParser;
|
||||||
|
import org.elasticsearch.common.time.DateUtils;
|
||||||
import org.elasticsearch.common.util.LocaleUtils;
|
import org.elasticsearch.common.util.LocaleUtils;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
@ -60,6 +61,7 @@ import org.joda.time.DateTimeZone;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -257,7 +259,7 @@ public class RangeFieldMapper extends FieldMapper {
|
||||||
public void setDateTimeFormatter(FormatDateTimeFormatter dateTimeFormatter) {
|
public void setDateTimeFormatter(FormatDateTimeFormatter dateTimeFormatter) {
|
||||||
checkIfFrozen();
|
checkIfFrozen();
|
||||||
this.dateTimeFormatter = dateTimeFormatter;
|
this.dateTimeFormatter = dateTimeFormatter;
|
||||||
this.dateMathParser = new DateMathParser(dateTimeFormatter);
|
this.dateMathParser = dateTimeFormatter.toDateMathParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DateMathParser dateMathParser() {
|
protected DateMathParser dateMathParser() {
|
||||||
|
@ -588,14 +590,15 @@ public class RangeFieldMapper extends FieldMapper {
|
||||||
boolean includeUpper, ShapeRelation relation, @Nullable DateTimeZone timeZone,
|
boolean includeUpper, ShapeRelation relation, @Nullable DateTimeZone timeZone,
|
||||||
@Nullable DateMathParser parser, QueryShardContext context) {
|
@Nullable DateMathParser parser, QueryShardContext context) {
|
||||||
DateTimeZone zone = (timeZone == null) ? DateTimeZone.UTC : timeZone;
|
DateTimeZone zone = (timeZone == null) ? DateTimeZone.UTC : timeZone;
|
||||||
|
ZoneId zoneId = DateUtils.dateTimeZoneToZoneId(zone);
|
||||||
DateMathParser dateMathParser = (parser == null) ?
|
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 :
|
Long low = lowerTerm == null ? Long.MIN_VALUE :
|
||||||
dateMathParser.parse(lowerTerm instanceof BytesRef ? ((BytesRef) lowerTerm).utf8ToString() : lowerTerm.toString(),
|
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 :
|
Long high = upperTerm == null ? Long.MAX_VALUE :
|
||||||
dateMathParser.parse(upperTerm instanceof BytesRef ? ((BytesRef) upperTerm).utf8ToString() : upperTerm.toString(),
|
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,
|
return super.rangeQuery(field, hasDocValues, low, high, includeLower, includeUpper, relation, zone,
|
||||||
dateMathParser, context);
|
dateMathParser, context);
|
||||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.elasticsearch.common.geo.ShapeRelation;
|
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.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,10 @@ import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.geo.ShapeRelation;
|
import org.elasticsearch.common.geo.ShapeRelation;
|
||||||
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.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
import org.elasticsearch.common.lucene.BytesRefs;
|
import org.elasticsearch.common.lucene.BytesRefs;
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
|
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
|
||||||
|
@ -302,7 +302,7 @@ public class RangeQueryBuilder extends AbstractQueryBuilder<RangeQueryBuilder> i
|
||||||
|
|
||||||
DateMathParser getForceDateParser() { // pkg private for testing
|
DateMathParser getForceDateParser() { // pkg private for testing
|
||||||
if (this.format != null) {
|
if (this.format != null) {
|
||||||
return new DateMathParser(this.format);
|
return this.format.toDateMathParser();
|
||||||
}
|
}
|
||||||
return null;
|
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.NamedWriteable;
|
||||||
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.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
import org.elasticsearch.common.network.InetAddresses;
|
import org.elasticsearch.common.network.InetAddresses;
|
||||||
import org.elasticsearch.common.network.NetworkAddress;
|
import org.elasticsearch.common.network.NetworkAddress;
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
|
import org.elasticsearch.common.time.DateUtils;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -171,13 +172,14 @@ public interface DocValueFormat extends NamedWriteable {
|
||||||
public static final String NAME = "date_time";
|
public static final String NAME = "date_time";
|
||||||
|
|
||||||
final FormatDateTimeFormatter formatter;
|
final FormatDateTimeFormatter formatter;
|
||||||
|
// TODO: change this to ZoneId, but will require careful change to serialization
|
||||||
final DateTimeZone timeZone;
|
final DateTimeZone timeZone;
|
||||||
private final DateMathParser parser;
|
private final DateMathParser parser;
|
||||||
|
|
||||||
public DateTime(FormatDateTimeFormatter formatter, DateTimeZone timeZone) {
|
public DateTime(FormatDateTimeFormatter formatter, DateTimeZone timeZone) {
|
||||||
this.formatter = Objects.requireNonNull(formatter);
|
this.formatter = Objects.requireNonNull(formatter);
|
||||||
this.timeZone = Objects.requireNonNull(timeZone);
|
this.timeZone = Objects.requireNonNull(timeZone);
|
||||||
this.parser = new DateMathParser(formatter);
|
this.parser = formatter.toDateMathParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime(StreamInput in) throws IOException {
|
public DateTime(StreamInput in) throws IOException {
|
||||||
|
@ -212,7 +214,7 @@ public interface DocValueFormat extends NamedWriteable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long parseLong(String value, boolean roundUp, LongSupplier now) {
|
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
|
@Override
|
||||||
|
|
|
@ -25,10 +25,10 @@ import org.apache.lucene.index.SortedNumericDocValues;
|
||||||
import org.apache.lucene.search.DocIdSetIterator;
|
import org.apache.lucene.search.DocIdSetIterator;
|
||||||
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.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
import org.elasticsearch.common.rounding.DateTimeUnit;
|
import org.elasticsearch.common.rounding.DateTimeUnit;
|
||||||
import org.elasticsearch.common.rounding.Rounding;
|
import org.elasticsearch.common.rounding.Rounding;
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
@ -72,7 +72,7 @@ import static java.util.Collections.unmodifiableMap;
|
||||||
public class DateHistogramAggregationBuilder extends ValuesSourceAggregationBuilder<ValuesSource.Numeric, DateHistogramAggregationBuilder>
|
public class DateHistogramAggregationBuilder extends ValuesSourceAggregationBuilder<ValuesSource.Numeric, DateHistogramAggregationBuilder>
|
||||||
implements MultiBucketAggregationBuilder {
|
implements MultiBucketAggregationBuilder {
|
||||||
public static final String NAME = "date_histogram";
|
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;
|
public static final Map<String, DateTimeUnit> DATE_FIELD_UNITS;
|
||||||
|
|
||||||
|
|
|
@ -24,17 +24,17 @@ import org.elasticsearch.ExceptionsHelper;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
import java.util.TimeZone;
|
import java.time.ZoneId;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.LongSupplier;
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
public class DateMathParserTests extends ESTestCase {
|
public class JodaDateMathParserTests extends ESTestCase {
|
||||||
|
|
||||||
FormatDateTimeFormatter formatter = Joda.forPattern("dateOptionalTime||epoch_millis");
|
FormatDateTimeFormatter formatter = Joda.forPattern("dateOptionalTime||epoch_millis");
|
||||||
DateMathParser parser = new DateMathParser(formatter);
|
JodaDateMathParser parser = new JodaDateMathParser(formatter);
|
||||||
|
|
||||||
void assertDateMathEquals(String toTest, String expected) {
|
void assertDateMathEquals(String toTest, String expected) {
|
||||||
assertDateMathEquals(toTest, expected, 0, false, null);
|
assertDateMathEquals(toTest, expected, 0, false, null);
|
||||||
|
@ -145,7 +145,7 @@ public class DateMathParserTests extends ESTestCase {
|
||||||
|
|
||||||
|
|
||||||
public void testNow() {
|
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", "2014-11-18T14:27:32", now, false, null);
|
||||||
assertDateMathEquals("now+M", "2014-12-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() {
|
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
|
||||||
FormatDateTimeFormatter formatter = Joda.forPattern("HH:mm:ss");
|
FormatDateTimeFormatter formatter = Joda.forPattern("HH:mm:ss");
|
||||||
DateMathParser parser = new DateMathParser(formatter);
|
JodaDateMathParser parser = new JodaDateMathParser(formatter);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
this.formatter.parser().parseMillis("1970-01-01T04:52:20.000Z"),
|
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(
|
assertEquals(
|
||||||
this.formatter.parser().parseMillis("1970-01-01T04:52:20.999Z"),
|
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
|
// 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
|
// implicit rounding with explicit timezone in the date format
|
||||||
FormatDateTimeFormatter formatter = Joda.forPattern("YYYY-MM-ddZ");
|
FormatDateTimeFormatter formatter = Joda.forPattern("YYYY-MM-ddZ");
|
||||||
DateMathParser parser = new DateMathParser(formatter);
|
JodaDateMathParser parser = new JodaDateMathParser(formatter);
|
||||||
long time = parser.parse("2011-10-09+01:00", () -> 0, false, null);
|
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);
|
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);
|
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");
|
assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000");
|
||||||
|
|
||||||
// also check other time units
|
// 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);
|
long datetime = parser.parse("1418248078", () -> 0);
|
||||||
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
|
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
|
||||||
|
|
||||||
|
@ -298,16 +298,16 @@ public class DateMathParserTests extends ESTestCase {
|
||||||
called.set(true);
|
called.set(true);
|
||||||
return 42L;
|
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());
|
assertFalse(called.get());
|
||||||
parser.parse("now/d", now, false, null);
|
parser.parse("now/d", now, false, (ZoneId) null);
|
||||||
assertTrue(called.get());
|
assertTrue(called.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testThatUnixTimestampMayNotHaveTimeZone() {
|
public void testThatUnixTimestampMayNotHaveTimeZone() {
|
||||||
DateMathParser parser = new DateMathParser(Joda.forPattern("epoch_millis"));
|
JodaDateMathParser parser = new JodaDateMathParser(Joda.forPattern("epoch_millis"));
|
||||||
try {
|
try {
|
||||||
parser.parse("1234567890123", () -> 42, false, DateTimeZone.forTimeZone(TimeZone.getTimeZone("CET")));
|
parser.parse("1234567890123", () -> 42, false, ZoneId.of("CET"));
|
||||||
fail("Expected ElasticsearchParseException");
|
fail("Expected ElasticsearchParseException");
|
||||||
} catch(ElasticsearchParseException e) {
|
} catch(ElasticsearchParseException e) {
|
||||||
assertThat(e.getMessage(), containsString("failed to parse date field"));
|
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.containsString;
|
||||||
import static org.hamcrest.Matchers.is;
|
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 DateFormatter formatter = DateFormatters.forPattern("dateOptionalTime||epoch_millis");
|
||||||
private final DateMathParser parser = new DateMathParser(formatter);
|
private final JavaDateMathParser parser = new JavaDateMathParser(formatter);
|
||||||
|
|
||||||
public void testBasicDates() {
|
public void testBasicDates() {
|
||||||
assertDateMathEquals("2014", "2014-01-01T00:00:00.000");
|
assertDateMathEquals("2014", "2014-01-01T00:00:00.000");
|
||||||
|
@ -125,7 +125,7 @@ public class DateMathParserTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNow() {
|
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", "2014-11-18T14:27:32", now, false, null);
|
||||||
assertDateMathEquals("now+M", "2014-12-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() {
|
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
|
||||||
DateFormatter formatter = DateFormatters.forPattern("HH:mm:ss");
|
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"));
|
ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(formatter.parse("04:52:20"));
|
||||||
assertThat(zonedDateTime.getYear(), is(1970));
|
assertThat(zonedDateTime.getYear(), is(1970));
|
||||||
long millisStart = zonedDateTime.toInstant().toEpochMilli();
|
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
|
// 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;
|
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
|
// 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
|
// implicit rounding with explicit timezone in the date format
|
||||||
DateFormatter formatter = DateFormatters.forPattern("yyyy-MM-ddXXX");
|
DateFormatter formatter = DateFormatters.forPattern("yyyy-MM-ddXXX");
|
||||||
DateMathParser parser = new DateMathParser(formatter);
|
JavaDateMathParser parser = new JavaDateMathParser(formatter);
|
||||||
long time = parser.parse("2011-10-09+01:00", () -> 0, false, null);
|
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);
|
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);
|
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");
|
assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000");
|
||||||
|
|
||||||
// also check other time units
|
// 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);
|
long datetime = parser.parse("1418248078", () -> 0);
|
||||||
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
|
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
|
||||||
|
|
||||||
|
@ -279,9 +279,9 @@ public class DateMathParserTests extends ESTestCase {
|
||||||
called.set(true);
|
called.set(true);
|
||||||
return 42L;
|
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());
|
assertFalse(called.get());
|
||||||
parser.parse("now/d", now, false, null);
|
parser.parse("now/d", now, false, (ZoneId) null);
|
||||||
assertTrue(called.get());
|
assertTrue(called.get());
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,12 @@ import org.apache.lucene.index.MultiReader;
|
||||||
import org.apache.lucene.search.IndexOrDocValuesQuery;
|
import org.apache.lucene.search.IndexOrDocValuesQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.store.Directory;
|
import org.apache.lucene.store.Directory;
|
||||||
import org.elasticsearch.core.internal.io.IOUtils;
|
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
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.IndexSettings;
|
||||||
import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType;
|
import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType;
|
||||||
import org.elasticsearch.index.mapper.MappedFieldType.Relation;
|
import org.elasticsearch.index.mapper.MappedFieldType.Relation;
|
||||||
|
@ -121,7 +121,7 @@ public class DateFieldTypeTests extends FieldTypeTestCase {
|
||||||
DirectoryReader reader = DirectoryReader.open(w);
|
DirectoryReader reader = DirectoryReader.open(w);
|
||||||
DateFieldType ft = new DateFieldType();
|
DateFieldType ft = new DateFieldType();
|
||||||
ft.setName("my_date");
|
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, null);
|
||||||
doTestIsFieldWithinQuery(ft, reader, null, alternateFormat);
|
doTestIsFieldWithinQuery(ft, reader, null, alternateFormat);
|
||||||
doTestIsFieldWithinQuery(ft, reader, DateTimeZone.UTC, null);
|
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.action.admin.indices.mapping.put.PutMappingRequest;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.compress.CompressedXContent;
|
import org.elasticsearch.common.compress.CompressedXContent;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.network.InetAddresses;
|
import org.elasticsearch.common.network.InetAddresses;
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.index.query.QueryStringQueryBuilder;
|
import org.elasticsearch.index.query.QueryStringQueryBuilder;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
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.SearchPhaseExecutionException;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.index.mapper.DateFieldMapper;
|
import org.elasticsearch.index.mapper.DateFieldMapper;
|
||||||
import org.elasticsearch.index.query.MatchNoneQueryBuilder;
|
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))
|
.setSettings(Settings.builder().put(indexSettings()).put("index.number_of_shards", 1).put("index.number_of_replicas", 0))
|
||||||
.execute().actionGet();
|
.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
|
// 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
|
// one at 00:00 in that time zone and one at 12:00
|
||||||
|
|
|
@ -408,7 +408,7 @@ public abstract class ESTestCase extends LuceneTestCase {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final List<String> actualWarnings = threadContext.getResponseHeaders().get("Warning");
|
final List<String> actualWarnings = threadContext.getResponseHeaders().get("Warning");
|
||||||
assertNotNull(actualWarnings);
|
assertNotNull("no warnings, expected: " + Arrays.asList(expectedWarnings), actualWarnings);
|
||||||
final Set<String> actualWarningValues =
|
final Set<String> actualWarningValues =
|
||||||
actualWarnings.stream().map(DeprecationLogger::extractWarningValueFromWarningHeader).collect(Collectors.toSet());
|
actualWarnings.stream().map(DeprecationLogger::extractWarningValueFromWarningHeader).collect(Collectors.toSet());
|
||||||
for (String msg : expectedWarnings) {
|
for (String msg : expectedWarnings) {
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
package org.elasticsearch.license.licensor;
|
package org.elasticsearch.license.licensor;
|
||||||
|
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
@ -38,8 +38,7 @@ public class TestUtils {
|
||||||
|
|
||||||
private static final FormatDateTimeFormatter formatDateTimeFormatter =
|
private static final FormatDateTimeFormatter formatDateTimeFormatter =
|
||||||
Joda.forPattern("yyyy-MM-dd");
|
Joda.forPattern("yyyy-MM-dd");
|
||||||
private static final DateMathParser dateMathParser =
|
private static final DateMathParser dateMathParser = formatDateTimeFormatter.toDateMathParser();
|
||||||
new DateMathParser(formatDateTimeFormatter);
|
|
||||||
private static final DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer();
|
private static final DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer();
|
||||||
|
|
||||||
public static String dumpLicense(License license) throws Exception {
|
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.Strings;
|
||||||
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.joda.DateMathParser;
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
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) {
|
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 {
|
try {
|
||||||
return dateMathParser.parse(date, now);
|
return dateMathParser.parse(date, now);
|
||||||
|
|
|
@ -17,7 +17,7 @@ import org.elasticsearch.client.ElasticsearchClient;
|
||||||
import org.elasticsearch.common.ParseField;
|
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.joda.DateMathParser;
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
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) {
|
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 {
|
try {
|
||||||
return dateMathParser.parse(date, now);
|
return dateMathParser.parse(date, now);
|
||||||
|
|
|
@ -9,8 +9,8 @@ import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
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.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
public class WatcherDateTimeUtils {
|
public class WatcherDateTimeUtils {
|
||||||
|
|
||||||
public static final FormatDateTimeFormatter dateTimeFormatter = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER;
|
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() {
|
private WatcherDateTimeUtils() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@ import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.time.DateMathParser;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
@ -50,7 +50,7 @@ import static org.junit.Assert.assertThat;
|
||||||
public class TestUtils {
|
public class TestUtils {
|
||||||
|
|
||||||
private static final FormatDateTimeFormatter formatDateTimeFormatter = Joda.forPattern("yyyy-MM-dd");
|
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();
|
private static final DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer();
|
||||||
|
|
||||||
public static String dateMathString(String time, final long now) {
|
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.SearchResponse;
|
||||||
import org.elasticsearch.action.search.SearchResponseSections;
|
import org.elasticsearch.action.search.SearchResponseSections;
|
||||||
import org.elasticsearch.action.search.ShardSearchFailure;
|
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
|
||||||
import org.elasticsearch.common.joda.Joda;
|
import org.elasticsearch.common.joda.Joda;
|
||||||
import org.elasticsearch.common.rounding.Rounding;
|
import org.elasticsearch.common.rounding.Rounding;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
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.CompositeAggregation;
|
||||||
import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder;
|
import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder;
|
||||||
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
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.ConfigTestHelpers;
|
||||||
import org.elasticsearch.xpack.core.rollup.job.DateHistogramGroupConfig;
|
import org.elasticsearch.xpack.core.rollup.job.DateHistogramGroupConfig;
|
||||||
import org.elasticsearch.xpack.core.rollup.job.GroupConfig;
|
import org.elasticsearch.xpack.core.rollup.job.GroupConfig;
|
||||||
import org.elasticsearch.xpack.core.rollup.job.MetricConfig;
|
import org.elasticsearch.xpack.core.rollup.job.MetricConfig;
|
||||||
import org.elasticsearch.xpack.core.rollup.job.RollupJob;
|
import org.elasticsearch.xpack.core.rollup.job.RollupJob;
|
||||||
import org.elasticsearch.xpack.core.rollup.job.RollupJobConfig;
|
import org.elasticsearch.xpack.core.rollup.job.RollupJobConfig;
|
||||||
import org.elasticsearch.xpack.core.indexing.IndexerState;
|
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -601,7 +600,7 @@ public class RollupIndexerIndexingTests extends AggregatorTestCase {
|
||||||
RangeQueryBuilder range = (RangeQueryBuilder) request.source().query();
|
RangeQueryBuilder range = (RangeQueryBuilder) request.source().query();
|
||||||
final DateTimeZone timeZone = range.timeZone() != null ? DateTimeZone.forID(range.timeZone()) : null;
|
final DateTimeZone timeZone = range.timeZone() != null ? DateTimeZone.forID(range.timeZone()) : null;
|
||||||
Query query = timestampField.rangeQuery(range.from(), range.to(), range.includeLower(), range.includeUpper(),
|
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
|
// extract composite agg
|
||||||
assertThat(request.source().aggregations().getAggregatorFactories().size(), equalTo(1));
|
assertThat(request.source().aggregations().getAggregatorFactories().size(), equalTo(1));
|
||||||
|
|
Loading…
Reference in New Issue