mirror of https://github.com/apache/lucene.git
SOLR-9080 SOLR-9085: Fix date math before the year 1582.
note: DateMathParser no longer needs a Locale
This commit is contained in:
parent
927454b8a2
commit
4193e60b9f
|
@ -47,6 +47,11 @@ Optimizations
|
||||||
|
|
||||||
================== 6.1.0 ==================
|
================== 6.1.0 ==================
|
||||||
|
|
||||||
|
Upgrading from Solr any prior release
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* If you use historical dates, specifically on or before the year 1582, you should re-index.
|
||||||
|
|
||||||
Detailed Change List
|
Detailed Change List
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
@ -206,6 +211,10 @@ Bug Fixes
|
||||||
* SOLR-8970: Change SSLTestConfig to use a keystore file that is included as a resource in the
|
* SOLR-8970: Change SSLTestConfig to use a keystore file that is included as a resource in the
|
||||||
test-framework jar so users subclassing SolrTestCaseJ4 don't need to preserve magic paths (hossman)
|
test-framework jar so users subclassing SolrTestCaseJ4 don't need to preserve magic paths (hossman)
|
||||||
|
|
||||||
|
* SOLR-9080, SOLR-9085: (6.0 bug) For years <= 1582, date math (round,add,sub) introduced error. Range faceting
|
||||||
|
on such dates was also affected. With this fixed, this is the first release range faceting works on BC years.
|
||||||
|
(David Smiley)
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
* SOLR-8722: Don't force a full ZkStateReader refresh on every Overseer operation.
|
* SOLR-8722: Don't force a full ZkStateReader refresh on every Overseer operation.
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class DataImportHandlerException extends RuntimeException {
|
||||||
return errCode;
|
return errCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void wrapAndThrow(int err, Exception e) {
|
public static DataImportHandlerException wrapAndThrow(int err, Exception e) {
|
||||||
if (e instanceof DataImportHandlerException) {
|
if (e instanceof DataImportHandlerException) {
|
||||||
throw (DataImportHandlerException) e;
|
throw (DataImportHandlerException) e;
|
||||||
} else {
|
} else {
|
||||||
|
@ -62,7 +62,7 @@ public class DataImportHandlerException extends RuntimeException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void wrapAndThrow(int err, Exception e, String msg) {
|
public static DataImportHandlerException wrapAndThrow(int err, Exception e, String msg) {
|
||||||
if (e instanceof DataImportHandlerException) {
|
if (e instanceof DataImportHandlerException) {
|
||||||
throw (DataImportHandlerException) e;
|
throw (DataImportHandlerException) e;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.handler.dataimport;
|
package org.apache.solr.handler.dataimport;
|
||||||
|
|
||||||
import static org.apache.solr.handler.dataimport.DataImportHandlerException.SEVERE;
|
|
||||||
import static org.apache.solr.handler.dataimport.DataImportHandlerException.wrapAndThrow;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -35,6 +32,9 @@ import org.apache.solr.common.util.SuppressForbidden;
|
||||||
import org.apache.solr.handler.dataimport.config.EntityField;
|
import org.apache.solr.handler.dataimport.config.EntityField;
|
||||||
import org.apache.solr.util.DateMathParser;
|
import org.apache.solr.util.DateMathParser;
|
||||||
|
|
||||||
|
import static org.apache.solr.handler.dataimport.DataImportHandlerException.SEVERE;
|
||||||
|
import static org.apache.solr.handler.dataimport.DataImportHandlerException.wrapAndThrow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Formats values using a given date format. </p>
|
* <p>Formats values using a given date format. </p>
|
||||||
* <p>Pass three parameters:
|
* <p>Pass three parameters:
|
||||||
|
@ -99,7 +99,7 @@ public class DateFormatEvaluator extends Evaluator {
|
||||||
throw new DataImportHandlerException(SEVERE, "Malformed / non-existent locale: " + localeStr, ex);
|
throw new DataImportHandlerException(SEVERE, "Malformed / non-existent locale: " + localeStr, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TimeZone tz = TimeZone.getDefault();
|
TimeZone tz = TimeZone.getDefault(); // DWS TODO: is this the right default for us? Deserves explanation if so.
|
||||||
if(l.size()==4) {
|
if(l.size()==4) {
|
||||||
Object tzObj = l.get(3);
|
Object tzObj = l.get(3);
|
||||||
String tzStr = null;
|
String tzStr = null;
|
||||||
|
@ -153,24 +153,19 @@ public class DateFormatEvaluator extends Evaluator {
|
||||||
* @return the result of evaluating a string
|
* @return the result of evaluating a string
|
||||||
*/
|
*/
|
||||||
protected Date evaluateString(String datemathfmt, Locale locale, TimeZone tz) {
|
protected Date evaluateString(String datemathfmt, Locale locale, TimeZone tz) {
|
||||||
Date date = null;
|
// note: DMP does not use the locale but perhaps a subclass might use it, for e.g. parsing a date in a custom
|
||||||
datemathfmt = datemathfmt.replaceAll("NOW", "");
|
// string that doesn't necessarily have date math?
|
||||||
|
//TODO refactor DateMathParser.parseMath a bit to have a static method for this logic.
|
||||||
|
if (datemathfmt.startsWith("NOW")) {
|
||||||
|
datemathfmt = datemathfmt.substring("NOW".length());
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
DateMathParser parser = getDateMathParser(locale, tz);
|
DateMathParser parser = new DateMathParser(tz);
|
||||||
date = parseMathString(parser,datemathfmt);
|
parser.setNow(new Date());// thus do *not* use SolrRequestInfo
|
||||||
} catch (ParseException e) {
|
|
||||||
wrapAndThrow(SEVERE, e, "Invalid expression for date");
|
|
||||||
}
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTE: declared as a method to allow for extensibility
|
|
||||||
* @lucene.experimental
|
|
||||||
* @return the result of resolving the variable wrapper
|
|
||||||
*/
|
|
||||||
protected Date parseMathString(DateMathParser parser, String datemathfmt) throws ParseException {
|
|
||||||
return parser.parseMath(datemathfmt);
|
return parser.parseMath(datemathfmt);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw wrapAndThrow(SEVERE, e, "Invalid expression for date");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -182,16 +177,4 @@ public class DateFormatEvaluator extends Evaluator {
|
||||||
return variableWrapper.resolve();
|
return variableWrapper.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @lucene.experimental
|
|
||||||
* @return a DateMathParser
|
|
||||||
*/
|
|
||||||
protected DateMathParser getDateMathParser(Locale l, TimeZone tz) {
|
|
||||||
return new DateMathParser(tz, l) {
|
|
||||||
@Override
|
|
||||||
public Date getNow() {
|
|
||||||
return new Date();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,13 @@ import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -153,10 +159,14 @@ public class FileListEntityProcessor extends EntityProcessorBase {
|
||||||
}
|
}
|
||||||
m = Evaluator.IN_SINGLE_QUOTES.matcher(dateStr);
|
m = Evaluator.IN_SINGLE_QUOTES.matcher(dateStr);
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
String expr = null;
|
String expr = m.group(1);
|
||||||
expr = m.group(1).replaceAll("NOW", "");
|
//TODO refactor DateMathParser.parseMath a bit to have a static method for this logic.
|
||||||
|
if (expr.startsWith("NOW")) {
|
||||||
|
expr = expr.substring("NOW".length());
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return new DateMathParser(TimeZone.getDefault(), Locale.ROOT).parseMath(expr);
|
// DWS TODO: is this TimeZone the right default for us? Deserves explanation if so.
|
||||||
|
return new DateMathParser(TimeZone.getDefault()).parseMath(expr);
|
||||||
} catch (ParseException exp) {
|
} catch (ParseException exp) {
|
||||||
throw new DataImportHandlerException(DataImportHandlerException.SEVERE,
|
throw new DataImportHandlerException(DataImportHandlerException.SEVERE,
|
||||||
"Invalid expression for date", exp);
|
"Invalid expression for date", exp);
|
||||||
|
|
|
@ -16,12 +16,19 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.handler.dataimport;
|
package org.apache.solr.handler.dataimport;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.apache.solr.util.DateMathParser;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.apache.solr.util.DateMathParser;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -103,7 +110,7 @@ public class TestVariableResolver extends AbstractDataImportHandlerTestCase {
|
||||||
.<Map<String,String>> emptyList()));
|
.<Map<String,String>> emptyList()));
|
||||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT);
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT);
|
||||||
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
DateMathParser dmp = new DateMathParser(TimeZone.getDefault(), Locale.ROOT);
|
DateMathParser dmp = new DateMathParser(TimeZone.getDefault());
|
||||||
|
|
||||||
String s = vri
|
String s = vri
|
||||||
.replaceTokens("${dataimporter.functions.formatDate('NOW/DAY','yyyy-MM-dd HH:mm')}");
|
.replaceTokens("${dataimporter.functions.formatDate('NOW/DAY','yyyy-MM-dd HH:mm')}");
|
||||||
|
@ -144,7 +151,7 @@ public class TestVariableResolver extends AbstractDataImportHandlerTestCase {
|
||||||
|
|
||||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT);
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT);
|
||||||
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
DateMathParser dmp = new DateMathParser(TimeZone.getDefault(), Locale.ROOT);
|
DateMathParser dmp = new DateMathParser(TimeZone.getDefault());
|
||||||
|
|
||||||
String s = resolver
|
String s = resolver
|
||||||
.replaceTokens("${dataimporter.functions.formatDate('NOW/DAY','yyyy-MM-dd HH:mm')}");
|
.replaceTokens("${dataimporter.functions.formatDate('NOW/DAY','yyyy-MM-dd HH:mm')}");
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import org.apache.lucene.index.IndexCommit;
|
import org.apache.lucene.index.IndexCommit;
|
||||||
import org.apache.lucene.index.IndexDeletionPolicy;
|
import org.apache.lucene.index.IndexDeletionPolicy;
|
||||||
|
@ -174,7 +173,7 @@ public class SolrDeletionPolicy extends IndexDeletionPolicy implements NamedList
|
||||||
try {
|
try {
|
||||||
if (maxCommitAge != null) {
|
if (maxCommitAge != null) {
|
||||||
if (maxCommitAgeTimeStamp==-1) {
|
if (maxCommitAgeTimeStamp==-1) {
|
||||||
DateMathParser dmp = new DateMathParser(DateMathParser.UTC, Locale.ROOT);
|
DateMathParser dmp = new DateMathParser(DateMathParser.UTC);
|
||||||
maxCommitAgeTimeStamp = dmp.parseMath(maxCommitAge).getTime();
|
maxCommitAgeTimeStamp = dmp.parseMath(maxCommitAge).getTime();
|
||||||
}
|
}
|
||||||
if (IndexDeletionPolicyWrapper.getCommitTimestamp(commit) < maxCommitAgeTimeStamp) {
|
if (IndexDeletionPolicyWrapper.getCommitTimestamp(commit) < maxCommitAgeTimeStamp) {
|
||||||
|
|
|
@ -39,16 +39,18 @@ import org.apache.solr.util.DateMathParser;
|
||||||
import org.locationtech.spatial4j.shape.Shape;
|
import org.locationtech.spatial4j.shape.Shape;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A field for indexed dates and date ranges. It's mostly compatible with TrieDateField.
|
* A field for indexed dates and date ranges. It's mostly compatible with TrieDateField. It has the potential to allow
|
||||||
|
* efficient faceting, similar to facet.enum.
|
||||||
*
|
*
|
||||||
* @see NumberRangePrefixTreeStrategy
|
* @see NumberRangePrefixTreeStrategy
|
||||||
* @see DateRangePrefixTree
|
* @see DateRangePrefixTree
|
||||||
*/
|
*/
|
||||||
public class DateRangeField extends AbstractSpatialPrefixTreeFieldType<NumberRangePrefixTreeStrategy> {
|
public class DateRangeField extends AbstractSpatialPrefixTreeFieldType<NumberRangePrefixTreeStrategy>
|
||||||
|
implements DateValueFieldType { // used by ParseDateFieldUpdateProcessorFactory
|
||||||
|
|
||||||
private static final String OP_PARAM = "op";//local-param to resolve SpatialOperation
|
private static final String OP_PARAM = "op";//local-param to resolve SpatialOperation
|
||||||
|
|
||||||
private static final DateRangePrefixTree tree = DateRangePrefixTree.INSTANCE;
|
private static final DateRangePrefixTree tree = new DateRangePrefixTree(DateRangePrefixTree.JAVA_UTIL_TIME_COMPAT_CAL);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init(IndexSchema schema, Map<String, String> args) {
|
protected void init(IndexSchema schema, Map<String, String> args) {
|
||||||
|
@ -69,17 +71,24 @@ public class DateRangeField extends AbstractSpatialPrefixTreeFieldType<NumberRan
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getStoredValue(Shape shape, String shapeStr) {
|
protected String getStoredValue(Shape shape, String shapeStr) {
|
||||||
|
// even if shapeStr is set, it might have included some dateMath, so see if we can resolve it first:
|
||||||
if (shape instanceof UnitNRShape) {
|
if (shape instanceof UnitNRShape) {
|
||||||
UnitNRShape unitShape = (UnitNRShape) shape;
|
UnitNRShape unitShape = (UnitNRShape) shape;
|
||||||
if (unitShape.getLevel() == tree.getMaxLevels()) {
|
if (unitShape.getLevel() == tree.getMaxLevels()) {
|
||||||
//fully precise date. We can be fully compatible with TrieDateField.
|
//fully precise date. We can be fully compatible with TrieDateField (incl. 'Z')
|
||||||
Date date = tree.toCalendar(unitShape).getTime();
|
return shape.toString() + 'Z';
|
||||||
return date.toInstant().toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (shapeStr == null ? shape.toString() : shapeStr);//we don't normalize ranges here; should we?
|
return (shapeStr == null ? shape.toString() : shapeStr);//we don't normalize ranges here; should we?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Won't be called because we override getStoredValue? any way; easy to implement in terms of that
|
||||||
|
@Override
|
||||||
|
public String shapeToString(Shape shape) {
|
||||||
|
return getStoredValue(shape, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NRShape parseShape(String str) {
|
public NRShape parseShape(String str) {
|
||||||
if (str.contains(" TO ")) {
|
if (str.contains(" TO ")) {
|
||||||
|
@ -96,9 +105,9 @@ public class DateRangeField extends AbstractSpatialPrefixTreeFieldType<NumberRan
|
||||||
}
|
}
|
||||||
|
|
||||||
private Calendar parseCalendar(String str) {
|
private Calendar parseCalendar(String str) {
|
||||||
if (str.startsWith("NOW") || str.lastIndexOf('Z') >= 0) {
|
if (str.startsWith("NOW") || str.lastIndexOf('Z') >= 0) { // ? but not if Z is last char ? Ehh, whatever.
|
||||||
//use Solr standard date format parsing rules.
|
//use Solr standard date format parsing rules:
|
||||||
//TODO parse a Calendar instead of a Date, rounded according to DateMath syntax.
|
//TODO add DMP utility to return ZonedDateTime alternative, then set cal fields manually, which is faster?
|
||||||
Date date = DateMathParser.parseMath(null, str);
|
Date date = DateMathParser.parseMath(null, str);
|
||||||
Calendar cal = tree.newCal();
|
Calendar cal = tree.newCal();
|
||||||
cal.setTime(date);
|
cal.setTime(date);
|
||||||
|
@ -119,19 +128,6 @@ public class DateRangeField extends AbstractSpatialPrefixTreeFieldType<NumberRan
|
||||||
return DateMathParser.parseMath(now, rawval);
|
return DateMathParser.parseMath(now, rawval);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String shapeToString(Shape shape) {
|
|
||||||
if (shape instanceof UnitNRShape) {
|
|
||||||
UnitNRShape unitShape = (UnitNRShape) shape;
|
|
||||||
if (unitShape.getLevel() == tree.getMaxLevels()) {
|
|
||||||
//fully precise date. We can be fully compatible with TrieDateField.
|
|
||||||
Date date = tree.toCalendar(unitShape).getTime();
|
|
||||||
return date.toInstant().toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return shape.toString();//range shape
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SpatialArgs parseSpatialArgs(QParser parser, String externalVal) {
|
protected SpatialArgs parseSpatialArgs(QParser parser, String externalVal) {
|
||||||
//We avoid SpatialArgsParser entirely because it isn't very Solr-friendly
|
//We avoid SpatialArgsParser entirely because it isn't very Solr-friendly
|
||||||
|
|
|
@ -18,9 +18,15 @@ package org.apache.solr.util;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -93,6 +99,11 @@ import org.apache.solr.request.SolrRequestInfo;
|
||||||
* request param.
|
* request param.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* Historical dates: The calendar computation is completely done with the
|
||||||
|
* Gregorian system/algorithm. It does <em>not</em> switch to Julian or
|
||||||
|
* anything else, unlike the default {@link java.util.GregorianCalendar}.
|
||||||
|
* </p>
|
||||||
* @see SolrRequestInfo#getClientTimeZone
|
* @see SolrRequestInfo#getClientTimeZone
|
||||||
* @see SolrRequestInfo#getNOW
|
* @see SolrRequestInfo#getNOW
|
||||||
*/
|
*/
|
||||||
|
@ -103,9 +114,6 @@ public class DateMathParser {
|
||||||
/** Default TimeZone for DateMath rounding (UTC) */
|
/** Default TimeZone for DateMath rounding (UTC) */
|
||||||
public static final TimeZone DEFAULT_MATH_TZ = UTC;
|
public static final TimeZone DEFAULT_MATH_TZ = UTC;
|
||||||
|
|
||||||
/** Default Locale for DateMath rounding (Locale.ROOT) */
|
|
||||||
public static final Locale DEFAULT_MATH_LOCALE = Locale.ROOT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Differs by {@link DateTimeFormatter#ISO_INSTANT} in that it's lenient.
|
* Differs by {@link DateTimeFormatter#ISO_INSTANT} in that it's lenient.
|
||||||
* @see #parseNoMath(String)
|
* @see #parseNoMath(String)
|
||||||
|
@ -115,22 +123,22 @@ public class DateMathParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping from (uppercased) String labels identifying time units,
|
* A mapping from (uppercased) String labels identifying time units,
|
||||||
* to the corresponding Calendar constant used to set/add/roll that unit
|
* to the corresponding {@link ChronoUnit} enum (e.g. "YEARS") used to
|
||||||
* of measurement.
|
* set/add/roll that unit of measurement.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* A single logical unit of time might be represented by multiple labels
|
* A single logical unit of time might be represented by multiple labels
|
||||||
* for convenience (ie: <code>DATE==DAY</code>,
|
* for convenience (ie: <code>DATE==DAYS</code>,
|
||||||
* <code>MILLI==MILLISECOND</code>)
|
* <code>MILLI==MILLIS</code>)
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @see Calendar
|
* @see Calendar
|
||||||
*/
|
*/
|
||||||
public static final Map<String,Integer> CALENDAR_UNITS = makeUnitsMap();
|
public static final Map<String,ChronoUnit> CALENDAR_UNITS = makeUnitsMap();
|
||||||
|
|
||||||
|
|
||||||
/** @see #CALENDAR_UNITS */
|
/** @see #CALENDAR_UNITS */
|
||||||
private static Map<String,Integer> makeUnitsMap() {
|
private static Map<String,ChronoUnit> makeUnitsMap() {
|
||||||
|
|
||||||
// NOTE: consciously choosing not to support WEEK at this time,
|
// NOTE: consciously choosing not to support WEEK at this time,
|
||||||
// because of complexity in rounding down to the nearest week
|
// because of complexity in rounding down to the nearest week
|
||||||
|
@ -141,90 +149,69 @@ public class DateMathParser {
|
||||||
// we probably need to change "Locale loc" to default to something
|
// we probably need to change "Locale loc" to default to something
|
||||||
// from a param via SolrRequestInfo as well.
|
// from a param via SolrRequestInfo as well.
|
||||||
|
|
||||||
Map<String,Integer> units = new HashMap<>(13);
|
Map<String,ChronoUnit> units = new HashMap<>(13);
|
||||||
units.put("YEAR", Calendar.YEAR);
|
units.put("YEAR", ChronoUnit.YEARS);
|
||||||
units.put("YEARS", Calendar.YEAR);
|
units.put("YEARS", ChronoUnit.YEARS);
|
||||||
units.put("MONTH", Calendar.MONTH);
|
units.put("MONTH", ChronoUnit.MONTHS);
|
||||||
units.put("MONTHS", Calendar.MONTH);
|
units.put("MONTHS", ChronoUnit.MONTHS);
|
||||||
units.put("DAY", Calendar.DATE);
|
units.put("DAY", ChronoUnit.DAYS);
|
||||||
units.put("DAYS", Calendar.DATE);
|
units.put("DAYS", ChronoUnit.DAYS);
|
||||||
units.put("DATE", Calendar.DATE);
|
units.put("DATE", ChronoUnit.DAYS);
|
||||||
units.put("HOUR", Calendar.HOUR_OF_DAY);
|
units.put("HOUR", ChronoUnit.HOURS);
|
||||||
units.put("HOURS", Calendar.HOUR_OF_DAY);
|
units.put("HOURS", ChronoUnit.HOURS);
|
||||||
units.put("MINUTE", Calendar.MINUTE);
|
units.put("MINUTE", ChronoUnit.MINUTES);
|
||||||
units.put("MINUTES", Calendar.MINUTE);
|
units.put("MINUTES", ChronoUnit.MINUTES);
|
||||||
units.put("SECOND", Calendar.SECOND);
|
units.put("SECOND", ChronoUnit.SECONDS);
|
||||||
units.put("SECONDS", Calendar.SECOND);
|
units.put("SECONDS", ChronoUnit.SECONDS);
|
||||||
units.put("MILLI", Calendar.MILLISECOND);
|
units.put("MILLI", ChronoUnit.MILLIS);
|
||||||
units.put("MILLIS", Calendar.MILLISECOND);
|
units.put("MILLIS", ChronoUnit.MILLIS);
|
||||||
units.put("MILLISECOND", Calendar.MILLISECOND);
|
units.put("MILLISECOND", ChronoUnit.MILLIS);
|
||||||
units.put("MILLISECONDS",Calendar.MILLISECOND);
|
units.put("MILLISECONDS",ChronoUnit.MILLIS);
|
||||||
|
|
||||||
|
// NOTE: Maybe eventually support NANOS
|
||||||
|
|
||||||
return units;
|
return units;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies the specified Calendar by "adding" the specified value of units
|
* Returns a modified time by "adding" the specified value of units
|
||||||
*
|
*
|
||||||
* @exception IllegalArgumentException if unit isn't recognized.
|
* @exception IllegalArgumentException if unit isn't recognized.
|
||||||
* @see #CALENDAR_UNITS
|
* @see #CALENDAR_UNITS
|
||||||
*/
|
*/
|
||||||
public static void add(Calendar c, int val, String unit) {
|
private static LocalDateTime add(LocalDateTime t, int val, String unit) {
|
||||||
Integer uu = CALENDAR_UNITS.get(unit);
|
ChronoUnit uu = CALENDAR_UNITS.get(unit);
|
||||||
if (null == uu) {
|
if (null == uu) {
|
||||||
throw new IllegalArgumentException("Adding Unit not recognized: "
|
throw new IllegalArgumentException("Adding Unit not recognized: "
|
||||||
+ unit);
|
+ unit);
|
||||||
}
|
}
|
||||||
c.add(uu.intValue(), val);
|
return t.plus(val, uu);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies the specified Calendar by "rounding" down to the specified unit
|
* Returns a modified time by "rounding" down to the specified unit
|
||||||
*
|
*
|
||||||
* @exception IllegalArgumentException if unit isn't recognized.
|
* @exception IllegalArgumentException if unit isn't recognized.
|
||||||
* @see #CALENDAR_UNITS
|
* @see #CALENDAR_UNITS
|
||||||
*/
|
*/
|
||||||
public static void round(Calendar c, String unit) {
|
private static LocalDateTime round(LocalDateTime t, String unit) {
|
||||||
Integer uu = CALENDAR_UNITS.get(unit);
|
ChronoUnit uu = CALENDAR_UNITS.get(unit);
|
||||||
if (null == uu) {
|
if (null == uu) {
|
||||||
throw new IllegalArgumentException("Rounding Unit not recognized: "
|
throw new IllegalArgumentException("Rounding Unit not recognized: "
|
||||||
+ unit);
|
+ unit);
|
||||||
}
|
}
|
||||||
int u = uu.intValue();
|
// note: OffsetDateTime.truncatedTo does not support >= DAYS units so we handle those
|
||||||
|
switch (uu) {
|
||||||
switch (u) {
|
case YEARS:
|
||||||
|
return LocalDateTime.of(LocalDate.of(t.getYear(), 1, 1), LocalTime.MIDNIGHT); // midnight is 00:00:00
|
||||||
case Calendar.YEAR:
|
case MONTHS:
|
||||||
c.clear(Calendar.MONTH);
|
return LocalDateTime.of(LocalDate.of(t.getYear(), t.getMonth(), 1), LocalTime.MIDNIGHT);
|
||||||
/* fall through */
|
case DAYS:
|
||||||
case Calendar.MONTH:
|
return LocalDateTime.of(t.toLocalDate(), LocalTime.MIDNIGHT);
|
||||||
c.clear(Calendar.DAY_OF_MONTH);
|
|
||||||
c.clear(Calendar.DAY_OF_WEEK);
|
|
||||||
c.clear(Calendar.DAY_OF_WEEK_IN_MONTH);
|
|
||||||
c.clear(Calendar.DAY_OF_YEAR);
|
|
||||||
c.clear(Calendar.WEEK_OF_MONTH);
|
|
||||||
c.clear(Calendar.WEEK_OF_YEAR);
|
|
||||||
/* fall through */
|
|
||||||
case Calendar.DATE:
|
|
||||||
c.clear(Calendar.HOUR_OF_DAY);
|
|
||||||
c.clear(Calendar.HOUR);
|
|
||||||
c.clear(Calendar.AM_PM);
|
|
||||||
/* fall through */
|
|
||||||
case Calendar.HOUR_OF_DAY:
|
|
||||||
c.clear(Calendar.MINUTE);
|
|
||||||
/* fall through */
|
|
||||||
case Calendar.MINUTE:
|
|
||||||
c.clear(Calendar.SECOND);
|
|
||||||
/* fall through */
|
|
||||||
case Calendar.SECOND:
|
|
||||||
c.clear(Calendar.MILLISECOND);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException(
|
assert !uu.isDateBased();// >= DAY
|
||||||
"No logic for rounding value ("+u+") " + unit
|
return t.truncatedTo(uu);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -290,23 +277,19 @@ public class DateMathParser {
|
||||||
* otherwise specified in the SolrRequestInfo
|
* otherwise specified in the SolrRequestInfo
|
||||||
*
|
*
|
||||||
* @see SolrRequestInfo#getClientTimeZone
|
* @see SolrRequestInfo#getClientTimeZone
|
||||||
* @see #DEFAULT_MATH_LOCALE
|
|
||||||
*/
|
*/
|
||||||
public DateMathParser() {
|
public DateMathParser() {
|
||||||
this(null, DEFAULT_MATH_LOCALE);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param tz The TimeZone used for rounding (to determine when hours/days begin). If null, then this method defaults to the value dicated by the SolrRequestInfo if it
|
* @param tz The TimeZone used for rounding (to determine when hours/days begin). If null, then this method defaults
|
||||||
* exists -- otherwise it uses UTC.
|
* to the value dictated by the SolrRequestInfo if it exists -- otherwise it uses UTC.
|
||||||
* @param l The Locale used for rounding (to determine when weeks begin). If null, then this method defaults to en_US.
|
|
||||||
* @see #DEFAULT_MATH_TZ
|
* @see #DEFAULT_MATH_TZ
|
||||||
* @see #DEFAULT_MATH_LOCALE
|
|
||||||
* @see Calendar#getInstance(TimeZone,Locale)
|
* @see Calendar#getInstance(TimeZone,Locale)
|
||||||
* @see SolrRequestInfo#getClientTimeZone
|
* @see SolrRequestInfo#getClientTimeZone
|
||||||
*/
|
*/
|
||||||
public DateMathParser(TimeZone tz, Locale l) {
|
public DateMathParser(TimeZone tz) {
|
||||||
loc = (null != l) ? l : DEFAULT_MATH_LOCALE;
|
|
||||||
if (null == tz) {
|
if (null == tz) {
|
||||||
SolrRequestInfo reqInfo = SolrRequestInfo.getRequestInfo();
|
SolrRequestInfo reqInfo = SolrRequestInfo.getRequestInfo();
|
||||||
tz = (null != reqInfo) ? reqInfo.getClientTimeZone() : DEFAULT_MATH_TZ;
|
tz = (null != reqInfo) ? reqInfo.getClientTimeZone() : DEFAULT_MATH_TZ;
|
||||||
|
@ -321,13 +304,6 @@ public class DateMathParser {
|
||||||
return this.zone;
|
return this.zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the locale
|
|
||||||
*/
|
|
||||||
public Locale getLocale() {
|
|
||||||
return this.loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines this instance's concept of "now".
|
* Defines this instance's concept of "now".
|
||||||
* @see #getNow
|
* @see #getNow
|
||||||
|
@ -337,7 +313,7 @@ public class DateMathParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a cloned of this instance's concept of "now".
|
* Returns a clone of this instance's concept of "now" (never null).
|
||||||
*
|
*
|
||||||
* If setNow was never called (or if null was specified) then this method
|
* If setNow was never called (or if null was specified) then this method
|
||||||
* first defines 'now' as the value dictated by the SolrRequestInfo if it
|
* first defines 'now' as the value dictated by the SolrRequestInfo if it
|
||||||
|
@ -353,7 +329,7 @@ public class DateMathParser {
|
||||||
// fall back to current time if no request info set
|
// fall back to current time if no request info set
|
||||||
now = new Date();
|
now = new Date();
|
||||||
} else {
|
} else {
|
||||||
now = reqInfo.getNOW();
|
now = reqInfo.getNOW(); // never null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (Date) now.clone();
|
return (Date) now.clone();
|
||||||
|
@ -365,15 +341,15 @@ public class DateMathParser {
|
||||||
* @exception ParseException positions in ParseExceptions are token positions, not character positions.
|
* @exception ParseException positions in ParseExceptions are token positions, not character positions.
|
||||||
*/
|
*/
|
||||||
public Date parseMath(String math) throws ParseException {
|
public Date parseMath(String math) throws ParseException {
|
||||||
|
|
||||||
Calendar cal = Calendar.getInstance(zone, loc);
|
|
||||||
cal.setTime(getNow());
|
|
||||||
|
|
||||||
/* check for No-Op */
|
/* check for No-Op */
|
||||||
if (0==math.length()) {
|
if (0==math.length()) {
|
||||||
return cal.getTime();
|
return getNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZoneId zoneId = zone.toZoneId();
|
||||||
|
// localDateTime is a date and time local to the timezone specified
|
||||||
|
LocalDateTime localDateTime = ZonedDateTime.ofInstant(getNow().toInstant(), zoneId).toLocalDateTime();
|
||||||
|
|
||||||
String[] ops = splitter.split(math);
|
String[] ops = splitter.split(math);
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
while ( pos < ops.length ) {
|
while ( pos < ops.length ) {
|
||||||
|
@ -391,7 +367,7 @@ public class DateMathParser {
|
||||||
("Need a unit after command: \"" + command + "\"", pos);
|
("Need a unit after command: \"" + command + "\"", pos);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
round(cal, ops[pos++]);
|
localDateTime = round(localDateTime, ops[pos++]);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new ParseException
|
throw new ParseException
|
||||||
("Unit not recognized: \"" + ops[pos-1] + "\"", pos-1);
|
("Unit not recognized: \"" + ops[pos-1] + "\"", pos-1);
|
||||||
|
@ -415,7 +391,7 @@ public class DateMathParser {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String unit = ops[pos++];
|
String unit = ops[pos++];
|
||||||
add(cal, val, unit);
|
localDateTime = add(localDateTime, val, unit);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new ParseException
|
throw new ParseException
|
||||||
("Unit not recognized: \"" + ops[pos-1] + "\"", pos-1);
|
("Unit not recognized: \"" + ops[pos-1] + "\"", pos-1);
|
||||||
|
@ -427,7 +403,7 @@ public class DateMathParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cal.getTime();
|
return Date.from(ZonedDateTime.of(localDateTime, zoneId).toInstant());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Pattern splitter = Pattern.compile("\\b|(?<=\\d)(?=\\D)");
|
private static Pattern splitter = Pattern.compile("\\b|(?<=\\d)(?=\\D)");
|
||||||
|
|
|
@ -867,7 +867,7 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
|
||||||
|
|
||||||
assertQ("check counts using fixed NOW and TZ rounding",
|
assertQ("check counts using fixed NOW and TZ rounding",
|
||||||
req("q", "bday:[NOW/DAY TO NOW/DAY+1DAY]",
|
req("q", "bday:[NOW/DAY TO NOW/DAY+1DAY]",
|
||||||
"TZ", "GMT-23",
|
"TZ", "GMT+01",
|
||||||
"NOW", "205369736000" // 1976-07-04T23:08:56.235Z
|
"NOW", "205369736000" // 1976-07-04T23:08:56.235Z
|
||||||
),
|
),
|
||||||
"*[count(//doc)=0]");
|
"*[count(//doc)=0]");
|
||||||
|
|
|
@ -172,7 +172,7 @@ public class TestTrie extends SolrTestCaseJ4 {
|
||||||
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
|
||||||
assertU(delQ("*:*"));
|
assertU(delQ("*:*"));
|
||||||
DateMathParser dmp = new DateMathParser(DateMathParser.UTC, Locale.ROOT);
|
DateMathParser dmp = new DateMathParser(DateMathParser.UTC);
|
||||||
String largestDate = "";
|
String largestDate = "";
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
// index 10 days starting with today
|
// index 10 days starting with today
|
||||||
|
@ -221,7 +221,7 @@ public class TestTrie extends SolrTestCaseJ4 {
|
||||||
// For tdate tests
|
// For tdate tests
|
||||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT);
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT);
|
||||||
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
DateMathParser dmp = new DateMathParser(DateMathParser.UTC, Locale.ROOT);
|
DateMathParser dmp = new DateMathParser(DateMathParser.UTC);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
long l = Integer.MAX_VALUE + i*1L;
|
long l = Integer.MAX_VALUE + i*1L;
|
||||||
|
|
|
@ -1002,6 +1002,27 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
|
||||||
,meta+"/int[@name='between'][.='6']"
|
,meta+"/int[@name='between'][.='6']"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//Fixed by SOLR-9080 related to the Gregorian Change Date
|
||||||
|
assertQ("check BC era",
|
||||||
|
req( "q", "*:*"
|
||||||
|
,"rows", "0"
|
||||||
|
,"facet", "true"
|
||||||
|
,p, f
|
||||||
|
,p+".start", "-0200-01-01T00:00:00Z" // BC
|
||||||
|
,p+".end", "+0200-01-01T00:00:00Z" // AD
|
||||||
|
,p+".gap", "+100YEARS"
|
||||||
|
,p+".other", "all"
|
||||||
|
)
|
||||||
|
,pre+"/int[@name='-0200-01-01T00:00:00Z'][.='0']"
|
||||||
|
,pre+"/int[@name='-0100-01-01T00:00:00Z'][.='0']"
|
||||||
|
,pre+"/int[@name='0000-01-01T00:00:00Z'][.='0']"
|
||||||
|
,pre+"/int[@name='0100-01-01T00:00:00Z'][.='0']"
|
||||||
|
,meta+"/int[@name='before' ][.='0']"
|
||||||
|
,meta+"/int[@name='after' ][.='14']"
|
||||||
|
,meta+"/int[@name='between'][.='0']"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -55,6 +55,35 @@ public class DateRangeFieldTest extends SolrTestCaseJ4 {
|
||||||
assertQ(req("q", "dateRange:[1999 TO 2001]"), xpathMatches(0, 2));
|
assertQ(req("q", "dateRange:[1999 TO 2001]"), xpathMatches(0, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testBeforeGregorianChangeDate() { // GCD is the year 1582
|
||||||
|
assertU(delQ("*:*"));
|
||||||
|
assertU(adoc("id", "0", "dateRange", "1500-01-01T00:00:00Z"));
|
||||||
|
assertU(adoc("id", "1", "dateRange", "-1500-01-01T00:00:00Z")); // BC
|
||||||
|
assertU(adoc("id", "2", "dateRange", "1400-01-01T00:00:00Z/YEAR")); // date math of month or year can cause issues
|
||||||
|
assertU(adoc("id", "3", "dateRange", "1300")); // the whole year of 1300
|
||||||
|
assertU(commit());
|
||||||
|
|
||||||
|
//ensure round-trip toString
|
||||||
|
assertQ(req("q", "id:0", "fl", "dateRange"), "//result/doc/arr[@name='dateRange']/str[.='1500-01-01T00:00:00Z']");
|
||||||
|
assertQ(req("q", "id:1", "fl", "dateRange"), "//result/doc/arr[@name='dateRange']/str[.='-1500-01-01T00:00:00Z']");
|
||||||
|
// note: fixed by SOLR-9080, would instead find "1399-01-09T00:00:00Z"
|
||||||
|
assertQ(req("q", "id:2", "fl", "dateRange"), "//result/doc/arr[@name='dateRange']/str[.='1400-01-01T00:00:00Z']");
|
||||||
|
assertQ(req("q", "id:3", "fl", "dateRange"), "//result/doc/arr[@name='dateRange']/str[.='1300']");
|
||||||
|
|
||||||
|
//ensure range syntax works
|
||||||
|
assertQ(req("q", "dateRange:[1450-01-01T00:00:00Z TO 1499-12-31T23:59:59Z]"), xpathMatches());// before
|
||||||
|
assertQ(req("q", "dateRange:[1500-01-01T00:00:00Z TO 1500-01-01T00:00:00Z]"), xpathMatches(0));// spot on
|
||||||
|
assertQ(req("q", "dateRange:[1500-01-01T00:00:01Z TO 1550-01-01T00:00:00Z]"), xpathMatches());// after
|
||||||
|
|
||||||
|
assertQ(req("q", "dateRange:[-1500-01-01T00:00:00Z TO -1500-01-01T00:00:00Z]"), xpathMatches(1));
|
||||||
|
|
||||||
|
// do range queries in the vicinity of docId=3 val:"1300"
|
||||||
|
assertQ(req("q", "dateRange:[1299 TO 1299-12-31T23:59:59Z]"), xpathMatches());//adjacent
|
||||||
|
assertQ(req("q", "dateRange:[1299 TO 1300-01-01T00:00:00Z]"), xpathMatches(3));// expand + 1 sec
|
||||||
|
assertQ(req("q", "dateRange:1301"), xpathMatches()); // adjacent
|
||||||
|
assertQ(req("q", "dateRange:[1300-12-31T23:59:59Z TO 1301]"), xpathMatches(3)); // expand + 1 sec
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiValuedDateRanges() {
|
public void testMultiValuedDateRanges() {
|
||||||
assertU(delQ("*:*"));
|
assertU(delQ("*:*"));
|
||||||
|
|
|
@ -16,11 +16,10 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.util;
|
package org.apache.solr.util;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Calendar;
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -40,44 +39,37 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
* A formatter for specifying every last nuance of a Date for easy
|
* A formatter for specifying every last nuance of a Date for easy
|
||||||
* reference in assertion statements
|
* reference in assertion statements
|
||||||
*/
|
*/
|
||||||
private DateFormat fmt;
|
private DateTimeFormatter fmt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A parser for reading in explicit dates that are convenient to type
|
* A parser for reading in explicit dates that are convenient to type
|
||||||
* in a test
|
* in a test
|
||||||
*/
|
*/
|
||||||
private DateFormat parser;
|
private DateTimeFormatter parser;
|
||||||
|
|
||||||
public DateMathParserTest() {
|
public DateMathParserTest() {
|
||||||
super();
|
fmt = DateTimeFormatter.ofPattern("G yyyyy MM ww W D dd F E a HH hh mm ss SSS z Z", Locale.ROOT)
|
||||||
fmt = new SimpleDateFormat
|
.withZone(ZoneOffset.UTC);
|
||||||
("G yyyyy MM ww WW DD dd F E aa HH hh mm ss SSS z Z",Locale.ROOT);
|
|
||||||
fmt.setTimeZone(UTC);
|
|
||||||
|
|
||||||
parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS",Locale.ROOT);
|
parser = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneOffset.UTC); // basically without the 'Z'
|
||||||
parser.setTimeZone(UTC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** MACRO: Round: parses s, rounds with u, fmts */
|
/** MACRO: Round: parses s, rounds with u, fmts */
|
||||||
protected String r(String s, String u) throws Exception {
|
protected String r(String s, String u) throws Exception {
|
||||||
Date d = parser.parse(s);
|
Date dt = DateMathParser.parseMath(null, s + "Z/" + u);
|
||||||
Calendar c = Calendar.getInstance(UTC, Locale.ROOT);
|
return fmt.format(dt.toInstant());
|
||||||
c.setTime(d);
|
|
||||||
DateMathParser.round(c, u);
|
|
||||||
return fmt.format(c.getTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** MACRO: Add: parses s, adds v u, fmts */
|
/** MACRO: Add: parses s, adds v u, fmts */
|
||||||
protected String a(String s, int v, String u) throws Exception {
|
protected String a(String s, int v, String u) throws Exception {
|
||||||
Date d = parser.parse(s);
|
char sign = v >= 0 ? '+' : '-';
|
||||||
Calendar c = Calendar.getInstance(UTC, Locale.ROOT);
|
Date dt = DateMathParser.parseMath(null, s + 'Z' + sign + Math.abs(v) + u);
|
||||||
c.setTime(d);
|
return fmt.format(dt.toInstant());
|
||||||
DateMathParser.add(c, v, u);
|
|
||||||
return fmt.format(c.getTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** MACRO: Expected: parses s, fmts */
|
/** MACRO: Expected: parses s, fmts */
|
||||||
protected String e(String s) throws Exception {
|
protected String e(String s) throws Exception {
|
||||||
return fmt.format(parser.parse(s));
|
return fmt.format(parser.parse(s, Instant::from));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertRound(String e, String i, String u) throws Exception {
|
protected void assertRound(String e, String i, String u) throws Exception {
|
||||||
|
@ -85,6 +77,7 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
String rr = r(i,u);
|
String rr = r(i,u);
|
||||||
assertEquals(ee + " != " + rr + " round:" + i + ":" + u, ee, rr);
|
assertEquals(ee + " != " + rr + " round:" + i + ":" + u, ee, rr);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertAdd(String e, String i, int v, String u)
|
protected void assertAdd(String e, String i, int v, String u)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
|
@ -97,13 +90,17 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
String ee = e(e);
|
String ee = e(e);
|
||||||
String aa = fmt.format(p.parseMath(i));
|
String aa = fmt.format(p.parseMath(i).toInstant());
|
||||||
assertEquals(ee + " != " + aa + " math:" +
|
assertEquals(ee + " != " + aa + " math:" +
|
||||||
parser.format(p.getNow()) + ":" + i, ee, aa);
|
parser.format(p.getNow().toInstant()) + ":" + i, ee, aa);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setNow(DateMathParser p, String text) {
|
||||||
|
p.setNow(Date.from(parser.parse(text, Instant::from)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCalendarUnitsConsistency() throws Exception {
|
public void testCalendarUnitsConsistency() throws Exception {
|
||||||
String input = "2001-07-04T12:08:56.235";
|
String input = "1234-07-04T12:08:56.235";
|
||||||
for (String u : DateMathParser.CALENDAR_UNITS.keySet()) {
|
for (String u : DateMathParser.CALENDAR_UNITS.keySet()) {
|
||||||
try {
|
try {
|
||||||
r(input, u);
|
r(input, u);
|
||||||
|
@ -120,20 +117,20 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
|
|
||||||
public void testRound() throws Exception {
|
public void testRound() throws Exception {
|
||||||
|
|
||||||
String input = "2001-07-04T12:08:56.235";
|
String input = "1234-07-04T12:08:56.235";
|
||||||
|
|
||||||
assertRound("2001-07-04T12:08:56.000", input, "SECOND");
|
assertRound("1234-07-04T12:08:56.000", input, "SECOND");
|
||||||
assertRound("2001-07-04T12:08:00.000", input, "MINUTE");
|
assertRound("1234-07-04T12:08:00.000", input, "MINUTE");
|
||||||
assertRound("2001-07-04T12:00:00.000", input, "HOUR");
|
assertRound("1234-07-04T12:00:00.000", input, "HOUR");
|
||||||
assertRound("2001-07-04T00:00:00.000", input, "DAY");
|
assertRound("1234-07-04T00:00:00.000", input, "DAY");
|
||||||
assertRound("2001-07-01T00:00:00.000", input, "MONTH");
|
assertRound("1234-07-01T00:00:00.000", input, "MONTH");
|
||||||
assertRound("2001-01-01T00:00:00.000", input, "YEAR");
|
assertRound("1234-01-01T00:00:00.000", input, "YEAR");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAddZero() throws Exception {
|
public void testAddZero() throws Exception {
|
||||||
|
|
||||||
String input = "2001-07-04T12:08:56.235";
|
String input = "1234-07-04T12:08:56.235";
|
||||||
|
|
||||||
for (String u : DateMathParser.CALENDAR_UNITS.keySet()) {
|
for (String u : DateMathParser.CALENDAR_UNITS.keySet()) {
|
||||||
assertAdd(input, input, 0, u);
|
assertAdd(input, input, 0, u);
|
||||||
|
@ -143,24 +140,24 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
|
|
||||||
public void testAdd() throws Exception {
|
public void testAdd() throws Exception {
|
||||||
|
|
||||||
String input = "2001-07-04T12:08:56.235";
|
String input = "1234-07-04T12:08:56.235";
|
||||||
|
|
||||||
assertAdd("2001-07-04T12:08:56.236", input, 1, "MILLISECOND");
|
assertAdd("1234-07-04T12:08:56.236", input, 1, "MILLISECOND");
|
||||||
assertAdd("2001-07-04T12:08:57.235", input, 1, "SECOND");
|
assertAdd("1234-07-04T12:08:57.235", input, 1, "SECOND");
|
||||||
assertAdd("2001-07-04T12:09:56.235", input, 1, "MINUTE");
|
assertAdd("1234-07-04T12:09:56.235", input, 1, "MINUTE");
|
||||||
assertAdd("2001-07-04T13:08:56.235", input, 1, "HOUR");
|
assertAdd("1234-07-04T13:08:56.235", input, 1, "HOUR");
|
||||||
assertAdd("2001-07-05T12:08:56.235", input, 1, "DAY");
|
assertAdd("1234-07-05T12:08:56.235", input, 1, "DAY");
|
||||||
assertAdd("2001-08-04T12:08:56.235", input, 1, "MONTH");
|
assertAdd("1234-08-04T12:08:56.235", input, 1, "MONTH");
|
||||||
assertAdd("2002-07-04T12:08:56.235", input, 1, "YEAR");
|
assertAdd("1235-07-04T12:08:56.235", input, 1, "YEAR");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParseStatelessness() throws Exception {
|
public void testParseStatelessness() throws Exception {
|
||||||
|
|
||||||
DateMathParser p = new DateMathParser(UTC, Locale.ROOT);
|
DateMathParser p = new DateMathParser(UTC);
|
||||||
p.setNow(parser.parse("2001-07-04T12:08:56.235"));
|
setNow(p, "1234-07-04T12:08:56.235");
|
||||||
|
|
||||||
String e = fmt.format(p.parseMath(""));
|
String e = fmt.format(p.parseMath("").toInstant());
|
||||||
|
|
||||||
Date trash = p.parseMath("+7YEARS");
|
Date trash = p.parseMath("+7YEARS");
|
||||||
trash = p.parseMath("/MONTH");
|
trash = p.parseMath("/MONTH");
|
||||||
|
@ -168,90 +165,89 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
Thread.currentThread();
|
Thread.currentThread();
|
||||||
Thread.sleep(5);
|
Thread.sleep(5);
|
||||||
|
|
||||||
String a = fmt.format(p.parseMath(""));
|
String a =fmt.format(p.parseMath("").toInstant());
|
||||||
assertEquals("State of DateMathParser changed", e, a);
|
assertEquals("State of DateMathParser changed", e, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParseMath() throws Exception {
|
public void testParseMath() throws Exception {
|
||||||
|
|
||||||
DateMathParser p = new DateMathParser(UTC, Locale.ROOT);
|
DateMathParser p = new DateMathParser(UTC);
|
||||||
p.setNow(parser.parse("2001-07-04T12:08:56.235"));
|
setNow(p, "1234-07-04T12:08:56.235");
|
||||||
|
|
||||||
// No-Op
|
// No-Op
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "");
|
assertMath("1234-07-04T12:08:56.235", p, "");
|
||||||
|
|
||||||
// simple round
|
// simple round
|
||||||
assertMath("2001-07-04T12:08:56.000", p, "/SECOND");
|
assertMath("1234-07-04T12:08:56.235", p, "/MILLIS"); // no change
|
||||||
assertMath("2001-07-04T12:08:00.000", p, "/MINUTE");
|
assertMath("1234-07-04T12:08:56.000", p, "/SECOND");
|
||||||
assertMath("2001-07-04T12:00:00.000", p, "/HOUR");
|
assertMath("1234-07-04T12:08:00.000", p, "/MINUTE");
|
||||||
assertMath("2001-07-04T00:00:00.000", p, "/DAY");
|
assertMath("1234-07-04T12:00:00.000", p, "/HOUR");
|
||||||
assertMath("2001-07-01T00:00:00.000", p, "/MONTH");
|
assertMath("1234-07-04T00:00:00.000", p, "/DAY");
|
||||||
assertMath("2001-01-01T00:00:00.000", p, "/YEAR");
|
assertMath("1234-07-01T00:00:00.000", p, "/MONTH");
|
||||||
|
assertMath("1234-01-01T00:00:00.000", p, "/YEAR");
|
||||||
|
|
||||||
// simple addition
|
// simple addition
|
||||||
assertMath("2001-07-04T12:08:56.236", p, "+1MILLISECOND");
|
assertMath("1234-07-04T12:08:56.236", p, "+1MILLISECOND");
|
||||||
assertMath("2001-07-04T12:08:57.235", p, "+1SECOND");
|
assertMath("1234-07-04T12:08:57.235", p, "+1SECOND");
|
||||||
assertMath("2001-07-04T12:09:56.235", p, "+1MINUTE");
|
assertMath("1234-07-04T12:09:56.235", p, "+1MINUTE");
|
||||||
assertMath("2001-07-04T13:08:56.235", p, "+1HOUR");
|
assertMath("1234-07-04T13:08:56.235", p, "+1HOUR");
|
||||||
assertMath("2001-07-05T12:08:56.235", p, "+1DAY");
|
assertMath("1234-07-05T12:08:56.235", p, "+1DAY");
|
||||||
assertMath("2001-08-04T12:08:56.235", p, "+1MONTH");
|
assertMath("1234-08-04T12:08:56.235", p, "+1MONTH");
|
||||||
assertMath("2002-07-04T12:08:56.235", p, "+1YEAR");
|
assertMath("1235-07-04T12:08:56.235", p, "+1YEAR");
|
||||||
|
|
||||||
// simple subtraction
|
// simple subtraction
|
||||||
assertMath("2001-07-04T12:08:56.234", p, "-1MILLISECOND");
|
assertMath("1234-07-04T12:08:56.234", p, "-1MILLISECOND");
|
||||||
assertMath("2001-07-04T12:08:55.235", p, "-1SECOND");
|
assertMath("1234-07-04T12:08:55.235", p, "-1SECOND");
|
||||||
assertMath("2001-07-04T12:07:56.235", p, "-1MINUTE");
|
assertMath("1234-07-04T12:07:56.235", p, "-1MINUTE");
|
||||||
assertMath("2001-07-04T11:08:56.235", p, "-1HOUR");
|
assertMath("1234-07-04T11:08:56.235", p, "-1HOUR");
|
||||||
assertMath("2001-07-03T12:08:56.235", p, "-1DAY");
|
assertMath("1234-07-03T12:08:56.235", p, "-1DAY");
|
||||||
assertMath("2001-06-04T12:08:56.235", p, "-1MONTH");
|
assertMath("1234-06-04T12:08:56.235", p, "-1MONTH");
|
||||||
assertMath("2000-07-04T12:08:56.235", p, "-1YEAR");
|
assertMath("1233-07-04T12:08:56.235", p, "-1YEAR");
|
||||||
|
|
||||||
// simple '+/-'
|
// simple '+/-'
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "+1MILLISECOND-1MILLISECOND");
|
assertMath("1234-07-04T12:08:56.235", p, "+1MILLISECOND-1MILLISECOND");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "+1SECOND-1SECOND");
|
assertMath("1234-07-04T12:08:56.235", p, "+1SECOND-1SECOND");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "+1MINUTE-1MINUTE");
|
assertMath("1234-07-04T12:08:56.235", p, "+1MINUTE-1MINUTE");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "+1HOUR-1HOUR");
|
assertMath("1234-07-04T12:08:56.235", p, "+1HOUR-1HOUR");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "+1DAY-1DAY");
|
assertMath("1234-07-04T12:08:56.235", p, "+1DAY-1DAY");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "+1MONTH-1MONTH");
|
assertMath("1234-07-04T12:08:56.235", p, "+1MONTH-1MONTH");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "+1YEAR-1YEAR");
|
assertMath("1234-07-04T12:08:56.235", p, "+1YEAR-1YEAR");
|
||||||
|
|
||||||
// simple '-/+'
|
// simple '-/+'
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "-1MILLISECOND+1MILLISECOND");
|
assertMath("1234-07-04T12:08:56.235", p, "-1MILLISECOND+1MILLISECOND");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "-1SECOND+1SECOND");
|
assertMath("1234-07-04T12:08:56.235", p, "-1SECOND+1SECOND");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "-1MINUTE+1MINUTE");
|
assertMath("1234-07-04T12:08:56.235", p, "-1MINUTE+1MINUTE");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "-1HOUR+1HOUR");
|
assertMath("1234-07-04T12:08:56.235", p, "-1HOUR+1HOUR");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "-1DAY+1DAY");
|
assertMath("1234-07-04T12:08:56.235", p, "-1DAY+1DAY");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "-1MONTH+1MONTH");
|
assertMath("1234-07-04T12:08:56.235", p, "-1MONTH+1MONTH");
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "-1YEAR+1YEAR");
|
assertMath("1234-07-04T12:08:56.235", p, "-1YEAR+1YEAR");
|
||||||
|
|
||||||
// more complex stuff
|
// more complex stuff
|
||||||
assertMath("2000-07-04T12:08:56.236", p, "+1MILLISECOND-1YEAR");
|
assertMath("1233-07-04T12:08:56.236", p, "+1MILLISECOND-1YEAR");
|
||||||
assertMath("2000-07-04T12:08:57.235", p, "+1SECOND-1YEAR");
|
assertMath("1233-07-04T12:08:57.235", p, "+1SECOND-1YEAR");
|
||||||
assertMath("2000-07-04T12:09:56.235", p, "+1MINUTE-1YEAR");
|
assertMath("1233-07-04T12:09:56.235", p, "+1MINUTE-1YEAR");
|
||||||
assertMath("2000-07-04T13:08:56.235", p, "+1HOUR-1YEAR");
|
assertMath("1233-07-04T13:08:56.235", p, "+1HOUR-1YEAR");
|
||||||
assertMath("2000-07-05T12:08:56.235", p, "+1DAY-1YEAR");
|
assertMath("1233-07-05T12:08:56.235", p, "+1DAY-1YEAR");
|
||||||
assertMath("2000-08-04T12:08:56.235", p, "+1MONTH-1YEAR");
|
assertMath("1233-08-04T12:08:56.235", p, "+1MONTH-1YEAR");
|
||||||
assertMath("2000-07-04T12:08:56.236", p, "-1YEAR+1MILLISECOND");
|
assertMath("1233-07-04T12:08:56.236", p, "-1YEAR+1MILLISECOND");
|
||||||
assertMath("2000-07-04T12:08:57.235", p, "-1YEAR+1SECOND");
|
assertMath("1233-07-04T12:08:57.235", p, "-1YEAR+1SECOND");
|
||||||
assertMath("2000-07-04T12:09:56.235", p, "-1YEAR+1MINUTE");
|
assertMath("1233-07-04T12:09:56.235", p, "-1YEAR+1MINUTE");
|
||||||
assertMath("2000-07-04T13:08:56.235", p, "-1YEAR+1HOUR");
|
assertMath("1233-07-04T13:08:56.235", p, "-1YEAR+1HOUR");
|
||||||
assertMath("2000-07-05T12:08:56.235", p, "-1YEAR+1DAY");
|
assertMath("1233-07-05T12:08:56.235", p, "-1YEAR+1DAY");
|
||||||
assertMath("2000-08-04T12:08:56.235", p, "-1YEAR+1MONTH");
|
assertMath("1233-08-04T12:08:56.235", p, "-1YEAR+1MONTH");
|
||||||
assertMath("2000-07-01T00:00:00.000", p, "-1YEAR+1MILLISECOND/MONTH");
|
assertMath("1233-07-01T00:00:00.000", p, "-1YEAR+1MILLISECOND/MONTH");
|
||||||
assertMath("2000-07-04T00:00:00.000", p, "-1YEAR+1SECOND/DAY");
|
assertMath("1233-07-04T00:00:00.000", p, "-1YEAR+1SECOND/DAY");
|
||||||
assertMath("2000-07-04T00:00:00.000", p, "-1YEAR+1MINUTE/DAY");
|
assertMath("1233-07-04T00:00:00.000", p, "-1YEAR+1MINUTE/DAY");
|
||||||
assertMath("2000-07-04T13:00:00.000", p, "-1YEAR+1HOUR/HOUR");
|
assertMath("1233-07-04T13:00:00.000", p, "-1YEAR+1HOUR/HOUR");
|
||||||
assertMath("2000-07-05T12:08:56.000", p, "-1YEAR+1DAY/SECOND");
|
assertMath("1233-07-05T12:08:56.000", p, "-1YEAR+1DAY/SECOND");
|
||||||
assertMath("2000-08-04T12:08:56.000", p, "-1YEAR+1MONTH/SECOND");
|
assertMath("1233-08-04T12:08:56.000", p, "-1YEAR+1MONTH/SECOND");
|
||||||
|
|
||||||
// "tricky" cases
|
// "tricky" cases
|
||||||
p.setNow(parser.parse("2006-01-31T17:09:59.999"));
|
setNow(p, "2006-01-31T17:09:59.999");
|
||||||
assertMath("2006-02-28T17:09:59.999", p, "+1MONTH");
|
assertMath("2006-02-28T17:09:59.999", p, "+1MONTH");
|
||||||
assertMath("2008-02-29T17:09:59.999", p, "+25MONTH");
|
assertMath("2008-02-29T17:09:59.999", p, "+25MONTH");
|
||||||
assertMath("2006-02-01T00:00:00.000", p, "/MONTH+35DAYS/MONTH");
|
assertMath("2006-02-01T00:00:00.000", p, "/MONTH+35DAYS/MONTH");
|
||||||
assertMath("2006-01-31T17:10:00.000", p, "+3MILLIS/MINUTE");
|
assertMath("2006-01-31T17:10:00.000", p, "+3MILLIS/MINUTE");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParseMathTz() throws Exception {
|
public void testParseMathTz() throws Exception {
|
||||||
|
@ -267,12 +263,13 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
// US, Positive Offset with DST
|
// US, Positive Offset with DST
|
||||||
|
|
||||||
TimeZone tz = TimeZone.getTimeZone(PLUS_TZS);
|
TimeZone tz = TimeZone.getTimeZone(PLUS_TZS);
|
||||||
DateMathParser p = new DateMathParser(tz, Locale.ROOT);
|
DateMathParser p = new DateMathParser(tz);
|
||||||
|
|
||||||
p.setNow(parser.parse("2001-07-04T12:08:56.235"));
|
setNow(p, "2001-07-04T12:08:56.235");
|
||||||
|
|
||||||
// No-Op
|
// No-Op
|
||||||
assertMath("2001-07-04T12:08:56.235", p, "");
|
assertMath("2001-07-04T12:08:56.235", p, "");
|
||||||
|
assertMath("2001-07-04T12:08:56.235", p, "/MILLIS");
|
||||||
|
|
||||||
assertMath("2001-07-04T12:08:56.000", p, "/SECOND");
|
assertMath("2001-07-04T12:08:56.000", p, "/SECOND");
|
||||||
assertMath("2001-07-04T12:08:00.000", p, "/MINUTE");
|
assertMath("2001-07-04T12:08:00.000", p, "/MINUTE");
|
||||||
|
@ -289,8 +286,8 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
// France, Negative Offset with DST
|
// France, Negative Offset with DST
|
||||||
|
|
||||||
tz = TimeZone.getTimeZone(NEG_TZS);
|
tz = TimeZone.getTimeZone(NEG_TZS);
|
||||||
p = new DateMathParser(tz, Locale.ROOT);
|
p = new DateMathParser(tz);
|
||||||
p.setNow(parser.parse("2001-07-04T12:08:56.235"));
|
setNow(p, "2001-07-04T12:08:56.235");
|
||||||
|
|
||||||
assertMath("2001-07-04T12:08:56.000", p, "/SECOND");
|
assertMath("2001-07-04T12:08:56.000", p, "/SECOND");
|
||||||
assertMath("2001-07-04T12:08:00.000", p, "/MINUTE");
|
assertMath("2001-07-04T12:08:00.000", p, "/MINUTE");
|
||||||
|
@ -306,8 +303,8 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
|
|
||||||
public void testParseMathExceptions() throws Exception {
|
public void testParseMathExceptions() throws Exception {
|
||||||
|
|
||||||
DateMathParser p = new DateMathParser(UTC, Locale.ROOT);
|
DateMathParser p = new DateMathParser(UTC);
|
||||||
p.setNow(parser.parse("2001-07-04T12:08:56.235"));
|
setNow(p, "1234-07-04T12:08:56.235");
|
||||||
|
|
||||||
Map<String,Integer> badCommands = new HashMap<>();
|
Map<String,Integer> badCommands = new HashMap<>();
|
||||||
badCommands.put("/", 1);
|
badCommands.put("/", 1);
|
||||||
|
@ -373,7 +370,8 @@ public class DateMathParserTest extends LuceneTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertFormat(final String expected, final long millis) {
|
private void assertFormat(final String expected, final long millis) {
|
||||||
assertEquals(expected, Instant.ofEpochMilli(millis).toString());
|
assertEquals(expected, Instant.ofEpochMilli(millis).toString()); // assert same as ISO_INSTANT
|
||||||
|
assertEquals(millis, DateMathParser.parseMath(null, expected).getTime()); // assert DMP has same result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue