From 72f5eac2c5e7fb743f166fb3c1b25e73078ebdbe Mon Sep 17 00:00:00 2001 From: David Smiley Date: Wed, 30 Mar 2016 13:42:42 -0400 Subject: [PATCH] SOLR-8904: switch from SimpleDateFormat to Instant.parse and format. [value] and ms() and contrib/analytics now call DateMathParser to parse. DateFormatUtil is now removed. (cherry picked from commit 94c0423) (cherry picked from commit 39932f5) --- solr/CHANGES.txt | 8 + .../accumulator/BasicAccumulator.java | 6 +- .../accumulator/FacetingAccumulator.java | 8 +- .../expression/ExpressionFactory.java | 9 +- .../StatsCollectorSupplierFactory.java | 21 +- .../solr/analytics/util/AnalyticsParsers.java | 7 +- .../util/RangeEndpointCalculator.java | 5 +- .../util/valuesource/ConstDateSource.java | 9 +- .../util/valuesource/DateFieldSource.java | 6 +- .../util/valuesource/FilterFieldSource.java | 3 +- .../analytics/expression/ExpressionTest.java | 27 +- .../apache/solr/core/SolrDeletionPolicy.java | 22 +- .../solr/handler/CdcrReplicatorState.java | 21 +- .../solr/handler/PingRequestHandler.java | 6 +- .../handler/component/PivotFacetValue.java | 3 +- .../handler/component/RangeFacetRequest.java | 50 +--- .../solr/response/CSVResponseWriter.java | 10 +- .../solr/response/TextResponseWriter.java | 3 +- .../transform/ValueAugmenterFactory.java | 4 +- .../org/apache/solr/rest/ManagedResource.java | 24 +- .../apache/solr/schema/DateRangeField.java | 15 +- .../org/apache/solr/schema/TrieDateField.java | 5 +- .../org/apache/solr/schema/TrieField.java | 32 ++- .../apache/solr/search/ValueSourceParser.java | 37 ++- .../apache/solr/search/facet/FacetRange.java | 5 +- .../DocExpirationUpdateProcessorFactory.java | 56 ++-- .../org/apache/solr/util/DateFormatUtil.java | 245 ------------------ .../org/apache/solr/util/DateMathParser.java | 88 ++++++- .../apache/solr/BasicFunctionalityTest.java | 29 ++- .../src/test/org/apache/solr/TestTrie.java | 13 +- .../cloud/SegmentTerminateEarlyTestState.java | 6 +- .../solr/cloud/TestCloudPivotFacet.java | 23 +- .../DistributedFacetPivotLargeTest.java | 3 +- .../apache/solr/request/SimpleFacetsTest.java | 9 +- .../solr/response/TestCSVResponseWriter.java | 14 +- .../org/apache/solr/schema/DateFieldTest.java | 148 +---------- .../solr/schema/TestUseDocValuesAsStored.java | 9 +- .../update/processor/AtomicUpdatesTest.java | 48 ++-- .../apache/solr/util/DateMathParserTest.java | 125 ++++++++- .../apache/solr/client/solrj/SolrQuery.java | 21 +- .../client/solrj/impl/XMLResponseParser.java | 27 +- .../solr/client/solrj/util/ClientUtils.java | 20 +- .../solr/client/solrj/SolrQueryTest.java | 18 +- .../solrj/response/QueryResponseTest.java | 7 +- .../solr/BaseDistributedSearchTestCase.java | 64 +++-- .../java/org/apache/solr/SolrTestCaseJ4.java | 16 +- 46 files changed, 531 insertions(+), 804 deletions(-) delete mode 100644 solr/core/src/java/org/apache/solr/util/DateFormatUtil.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 11e2e9ef5bb..0397e7aa591 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -164,6 +164,10 @@ Upgrading from Solr 5.x to prevent future optizations from using the column-stored values over the row-stored values when fields have both stored="true" and docValues="true". +* Formatted date-times from Solr have some differences. If the year is more than 4 digits, there is a leading '+'. + When there is a non-zero number of milliseconds, it is padded with zeros to 3 digits. Negative year (BC) dates are + now possible. Parsing: It is now an error to supply a portion of the date out of its, range, like 67 seconds. + Detailed Change List ---------------------- @@ -517,6 +521,10 @@ Other Changes * SOLR-8810: Implement Connection.setReadOnly, Statement.set/getFetchSize, ResultSet.getType (Kevin Risden) +* SOLR-8904: All dates are formatted via Instant.toString() (ISO-8601); see Solr upgrade notes for differences. Will + now parse (and format) dates with a leading '+' or '-' (BC dates or dates > 4 digit year. + [value] and ms() and contrib/analytics now parse with date math. (David Smiley) + ================== 5.5.1 ================== Bug Fixes diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/BasicAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/BasicAccumulator.java index 53037c1ad57..bd21209e55d 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/BasicAccumulator.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/BasicAccumulator.java @@ -24,6 +24,7 @@ import java.util.Date; import java.util.List; import java.util.Set; +import com.google.common.base.Supplier; import org.apache.lucene.index.LeafReaderContext; import org.apache.solr.analytics.expression.Expression; import org.apache.solr.analytics.expression.ExpressionFactory; @@ -36,12 +37,9 @@ import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.util.NamedList; import org.apache.solr.search.DocSet; import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.util.DateFormatUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Supplier; - /** * A BasicAccumulator manages the ValueCounters and Expressions without regard to Facets. */ @@ -143,7 +141,7 @@ public class BasicAccumulator extends ValueAccumulator { if (expressionName.equals(expressionNames[count])) { Comparable value = expressions[count].getValue(); if (value.getClass().equals(Date.class)) { - return DateFormatUtil.formatExternal((Date)value); + return ((Date)value).toInstant().toString(); } else { return value.toString(); } diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/FacetingAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/FacetingAccumulator.java index 7cb0c144ea2..03392f0ef1a 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/FacetingAccumulator.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/FacetingAccumulator.java @@ -30,6 +30,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; +import com.google.common.collect.Iterables; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; @@ -60,9 +61,6 @@ import org.apache.solr.search.Filter; import org.apache.solr.search.QParser; import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.search.SyntaxError; -import org.apache.solr.util.DateFormatUtil; - -import com.google.common.collect.Iterables; /** * A FacetingAccumulator manages the StatsCollectors and Expressions for facets. @@ -376,7 +374,7 @@ public class FacetingAccumulator extends BasicAccumulator implements FacetValueA if (expressionName.equals(expressionNames[count])) { Comparable value = facetExpressions[count].getValue(); if (value.getClass().equals(Date.class)) { - return DateFormatUtil.formatExternal((Date)value); + return ((Date)value).toInstant().toString(); } else { return value.toString(); } @@ -430,7 +428,7 @@ public class FacetingAccumulator extends BasicAccumulator implements FacetValueA if (expressionName.equals(expressionNames[count])) { Comparable value = facetExpressions[count].getValue(); if (value.getClass().equals(Date.class)) { - return DateFormatUtil.formatExternal((Date)value); + return ((Date)value).toInstant().toString(); } else { return value.toString(); } diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java index 8ad4070d180..1f2d0e04b25 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java @@ -16,7 +16,6 @@ */ package org.apache.solr.analytics.expression; -import java.text.ParseException; import java.util.ArrayList; import java.util.List; @@ -24,7 +23,7 @@ import org.apache.solr.analytics.statistics.StatsCollector; import org.apache.solr.analytics.util.AnalyticsParams; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; -import org.apache.solr.util.DateFormatUtil; +import org.apache.solr.util.DateMathParser; public class ExpressionFactory { @@ -81,11 +80,7 @@ public class ExpressionFactory { throw new SolrException(ErrorCode.BAD_REQUEST, "The constant "+operands+" cannot be converted into a number.",e); } } else if (topOperation.equals(AnalyticsParams.CONSTANT_DATE)) { - try { - return new ConstantDateExpression(DateFormatUtil.parseDate(operands)); - } catch (ParseException e) { - throw new SolrException(ErrorCode.BAD_REQUEST, "The constant "+operands+" cannot be converted into a date.",e); - } + return new ConstantDateExpression(DateMathParser.parseMath(null, operands)); } else if (topOperation.equals(AnalyticsParams.CONSTANT_STRING)) { operands = expression.substring(paren+1, expression.lastIndexOf(')')); return new ConstantStringExpression(operands); diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollectorSupplierFactory.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollectorSupplierFactory.java index bee7431a69d..bd9f65d2427 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollectorSupplierFactory.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollectorSupplierFactory.java @@ -17,16 +17,14 @@ package org.apache.solr.analytics.statistics; import java.lang.invoke.MethodHandles; -import java.text.ParseException; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import com.google.common.base.Supplier; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.queries.function.valuesource.BytesRefFieldSource; import org.apache.lucene.queries.function.valuesource.DoubleFieldSource; @@ -36,7 +34,6 @@ import org.apache.lucene.queries.function.valuesource.LongFieldSource; import org.apache.solr.analytics.expression.ExpressionFactory; import org.apache.solr.analytics.request.ExpressionRequest; import org.apache.solr.analytics.util.AnalyticsParams; -import org.apache.solr.analytics.util.AnalyticsParsers; import org.apache.solr.analytics.util.valuesource.AbsoluteValueDoubleFunction; import org.apache.solr.analytics.util.valuesource.AddDoubleFunction; import org.apache.solr.analytics.util.valuesource.ConcatStringFunction; @@ -67,12 +64,10 @@ import org.apache.solr.schema.TrieDoubleField; import org.apache.solr.schema.TrieFloatField; import org.apache.solr.schema.TrieIntField; import org.apache.solr.schema.TrieLongField; -import org.apache.solr.util.DateFormatUtil; +import org.apache.solr.util.DateMathParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Supplier; - public class StatsCollectorSupplierFactory { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @@ -443,11 +438,7 @@ public class StatsCollectorSupplierFactory { throw new SolrException(ErrorCode.BAD_REQUEST,"The filter value "+arguments[1]+" cannot be converted into an integer.",e); } } else if ( src instanceof DateFieldSource || src instanceof MultiDateFunction) { - try { - defaultObject = DateFormatUtil.parseDate(arguments[1]); - } catch (ParseException e) { - throw new SolrException(ErrorCode.BAD_REQUEST,"The filter value "+arguments[1]+" cannot be converted into a date.",e); - } + defaultObject = DateMathParser.parseMath(null, arguments[1]); } else if ( src instanceof LongFieldSource ) { try { defaultObject = new Long(arguments[1]); @@ -578,11 +569,7 @@ public class StatsCollectorSupplierFactory { if (arguments.length!=1) { throw new SolrException(ErrorCode.BAD_REQUEST,"The constant date declaration ["+expressionString+"] does not have exactly 1 argument."); } - try { - return new ConstDateSource(DateFormatUtil.parseDate(operands)); - } catch (ParseException e) { - throw new SolrException(ErrorCode.BAD_REQUEST,"The constant "+operands+" cannot be converted into a date.",e); - } + return new ConstDateSource(DateMathParser.parseMath(null, operands)); } else if (operation.equals(AnalyticsParams.FILTER)) { return buildFilterSource(schema, operands, DATE_TYPE); } diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParsers.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParsers.java index dad4a5b28d4..7a7e697d059 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParsers.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParsers.java @@ -17,8 +17,8 @@ package org.apache.solr.analytics.util; import java.io.IOException; +import java.time.Instant; import java.util.Arrays; -import java.util.Date; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.LegacyNumericUtils; @@ -29,7 +29,6 @@ import org.apache.solr.schema.TrieDoubleField; import org.apache.solr.schema.TrieFloatField; import org.apache.solr.schema.TrieIntField; import org.apache.solr.schema.TrieLongField; -import org.apache.solr.util.DateFormatUtil; /** * Class to hold the parsers used for Solr Analytics. @@ -149,7 +148,7 @@ public class AnalyticsParsers { @SuppressWarnings("deprecation") public String parse(BytesRef bytes) throws IOException { try { - return DateFormatUtil.formatExternal(new Date(LegacyNumericUtils.prefixCodedToLong(bytes))); + return Instant.ofEpochMilli(LegacyNumericUtils.prefixCodedToLong(bytes)).toString(); } catch (NumberFormatException e) { throw new IOException("The byte array "+Arrays.toString(bytes.bytes)+" cannot be converted to a date."); } @@ -157,7 +156,7 @@ public class AnalyticsParsers { @SuppressWarnings("deprecation") @Override public String parseNum(long l) { - return ""+DateFormatUtil.formatExternal(new Date(l)); + return Instant.ofEpochMilli(l).toString(); } }; diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/RangeEndpointCalculator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/RangeEndpointCalculator.java index 6d899e0b9cd..fa9686d6785 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/RangeEndpointCalculator.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/RangeEndpointCalculator.java @@ -31,7 +31,6 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.schema.TrieDateField; import org.apache.solr.schema.TrieField; import org.apache.solr.util.DateMathParser; -import org.apache.solr.util.DateFormatUtil; public abstract class RangeEndpointCalculator> { @@ -332,12 +331,12 @@ public abstract class RangeEndpointCalculator> { @Override public String formatValue(Date val) { - return DateFormatUtil.formatExternal(val); + return val.toInstant().toString(); } @Override protected Date parseVal(String rawval) { - return DateFormatUtil.parseMath(now, rawval); + return DateMathParser.parseMath(now, rawval); } @Override diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDateSource.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDateSource.java index dc2e02ca037..3ce608f6987 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDateSource.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDateSource.java @@ -17,7 +17,7 @@ package org.apache.solr.analytics.util.valuesource; import java.io.IOException; -import java.text.ParseException; +import java.time.Instant; import java.util.Date; import java.util.Map; @@ -27,7 +27,6 @@ import org.apache.lucene.queries.function.docvalues.FloatDocValues; import org.apache.lucene.util.mutable.MutableValue; import org.apache.lucene.util.mutable.MutableValueDate; import org.apache.solr.analytics.util.AnalyticsParams; -import org.apache.solr.util.DateFormatUtil; /** * ConstDateSource returns a constant date for all documents @@ -35,7 +34,7 @@ import org.apache.solr.util.DateFormatUtil; public class ConstDateSource extends ConstDoubleSource { public final static String NAME = AnalyticsParams.CONSTANT_DATE; - public ConstDateSource(Date constant) throws ParseException { + public ConstDateSource(Date constant) { super(constant.getTime()); } @@ -46,7 +45,7 @@ public class ConstDateSource extends ConstDoubleSource { @SuppressWarnings("deprecation") @Override public String description() { - return name()+"(" + DateFormatUtil.formatExternal(new Date(getLong())) + ")"; + return name()+"(" + Instant.ofEpochMilli(getLong()) + ")"; } protected String name() { @@ -83,7 +82,7 @@ public class ConstDateSource extends ConstDoubleSource { @SuppressWarnings("deprecation") @Override public String strVal(int doc) { - return DateFormatUtil.formatExternal(new Date(longVal(doc))); + return Instant.ofEpochMilli(longVal(doc)).toString(); } @Override public boolean boolVal(int doc) { diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateFieldSource.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateFieldSource.java index 631aca0b63e..4d66e0025bc 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateFieldSource.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateFieldSource.java @@ -17,11 +17,12 @@ package org.apache.solr.analytics.util.valuesource; import java.io.IOException; +import java.time.Instant; import java.util.Date; import java.util.Map; -import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.NumericDocValues; import org.apache.lucene.queries.function.FunctionValues; import org.apache.lucene.queries.function.docvalues.LongDocValues; @@ -31,7 +32,6 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.LegacyNumericUtils; import org.apache.lucene.util.mutable.MutableValue; import org.apache.lucene.util.mutable.MutableValueDate; -import org.apache.solr.util.DateFormatUtil; /** * Extends {@link LongFieldSource} to have a field source that takes in @@ -53,7 +53,7 @@ public class DateFieldSource extends LongFieldSource { @SuppressWarnings("deprecation") public String longToString(long val) { - return DateFormatUtil.formatExternal((Date)longToObject(val)); + return Instant.ofEpochMilli(val).toString(); } @Override diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/FilterFieldSource.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/FilterFieldSource.java index 8eb35d144c3..22f5a572076 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/FilterFieldSource.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/FilterFieldSource.java @@ -25,7 +25,6 @@ import org.apache.lucene.queries.function.FunctionValues; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.util.mutable.MutableValue; import org.apache.solr.analytics.util.AnalyticsParams; -import org.apache.solr.util.DateFormatUtil; /** * DefaultIsMissingFieldSource wraps a field source to return missing values @@ -49,7 +48,7 @@ public class FilterFieldSource extends ValueSource { @Override public String description() { if (missValue.getClass().equals(Date.class)) { - return name()+"("+source.description()+","+DateFormatUtil.formatExternal((Date)missValue)+")"; + return name()+"("+source.description()+","+ ((Date)missValue).toInstant() +")"; } else { return name()+"("+source.description()+","+missValue.toString()+")"; } diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java index 25193588f6a..4a3276b9ce2 100644 --- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java +++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java @@ -16,22 +16,22 @@ */ package org.apache.solr.analytics.expression; -import com.google.common.collect.ObjectArrays; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.Scanner; +import com.google.common.collect.ObjectArrays; import org.apache.lucene.util.IOUtils; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.analytics.AbstractAnalyticsStatsTest; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.util.DateMathParser; -import org.apache.solr.util.DateFormatUtil; import org.junit.BeforeClass; import org.junit.Test; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Scanner; - public class ExpressionTest extends AbstractAnalyticsStatsTest { private static final String fileName = "/analytics/requestFiles/expressions.txt"; @@ -156,20 +156,19 @@ public class ExpressionTest extends AbstractAnalyticsStatsTest { assertEquals(getRawResponse(), 10, result, 0.0); } - @SuppressWarnings("deprecation") @Test public void dateMathTest() throws Exception { String math = (String) getStatResult("dmr", "cme", VAL_TYPE.STRING); - DateMathParser date = new DateMathParser(); - date.setNow(DateFormatUtil.parseDate((String) getStatResult("dmr", "median", VAL_TYPE.DATE))); + DateMathParser dateMathParser = new DateMathParser(); + dateMathParser.setNow(new Date(Instant.parse((String) getStatResult("dmr", "median", VAL_TYPE.DATE)).toEpochMilli())); String dateMath = (String) getStatResult("dmr", "dmme", VAL_TYPE.DATE); - assertEquals(getRawResponse(), DateFormatUtil.parseDate(dateMath), date.parseMath(math)); + assertEquals(getRawResponse(), new Date(Instant.parse(dateMath).toEpochMilli()), dateMathParser.parseMath(math)); math = (String) getStatResult("dmr", "cma", VAL_TYPE.STRING); - date = new DateMathParser(); - date.setNow(DateFormatUtil.parseDate((String) getStatResult("dmr", "max", VAL_TYPE.DATE))); + dateMathParser = new DateMathParser(); + dateMathParser.setNow(new Date(Instant.parse((String) getStatResult("dmr", "max", VAL_TYPE.DATE)).toEpochMilli())); dateMath = (String) getStatResult("dmr", "dmma", VAL_TYPE.DATE); - assertEquals(getRawResponse(), DateFormatUtil.parseDate(dateMath), date.parseMath(math)); + assertEquals(getRawResponse(), new Date(Instant.parse(dateMath).toEpochMilli()), dateMathParser.parseMath(math)); } @Test diff --git a/solr/core/src/java/org/apache/solr/core/SolrDeletionPolicy.java b/solr/core/src/java/org/apache/solr/core/SolrDeletionPolicy.java index 24b0e610d30..c7a668908ca 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrDeletionPolicy.java +++ b/solr/core/src/java/org/apache/solr/core/SolrDeletionPolicy.java @@ -15,16 +15,6 @@ * limitations under the License. */ package org.apache.solr.core; -import org.apache.lucene.index.IndexCommit; -import org.apache.lucene.index.IndexDeletionPolicy; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.FSDirectory; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.util.DateMathParser; -import org.apache.solr.util.DateFormatUtil; -import org.apache.solr.util.plugin.NamedListInitializedPlugin; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; @@ -32,6 +22,16 @@ import java.lang.invoke.MethodHandles; import java.util.List; import java.util.Locale; +import org.apache.lucene.index.IndexCommit; +import org.apache.lucene.index.IndexDeletionPolicy; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.util.DateMathParser; +import org.apache.solr.util.plugin.NamedListInitializedPlugin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Standard Solr deletion policy that allows reserving index commit points @@ -174,7 +174,7 @@ public class SolrDeletionPolicy extends IndexDeletionPolicy implements NamedList try { if (maxCommitAge != null) { if (maxCommitAgeTimeStamp==-1) { - DateMathParser dmp = new DateMathParser(DateFormatUtil.UTC, Locale.ROOT); + DateMathParser dmp = new DateMathParser(DateMathParser.UTC, Locale.ROOT); maxCommitAgeTimeStamp = dmp.parseMath(maxCommitAge).getTime(); } if (IndexDeletionPolicyWrapper.getCommitTimestamp(commit) < maxCommitAgeTimeStamp) { diff --git a/solr/core/src/java/org/apache/solr/handler/CdcrReplicatorState.java b/solr/core/src/java/org/apache/solr/handler/CdcrReplicatorState.java index d0b1c4a1152..9e01f117b67 100644 --- a/solr/core/src/java/org/apache/solr/handler/CdcrReplicatorState.java +++ b/solr/core/src/java/org/apache/solr/handler/CdcrReplicatorState.java @@ -16,17 +16,24 @@ */ package org.apache.solr.handler; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.update.CdcrUpdateLog; import org.apache.solr.update.UpdateLog; -import org.apache.solr.util.DateFormatUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.util.*; - /** * The state of the replication with a target cluster. */ @@ -133,7 +140,7 @@ class CdcrReplicatorState { Iterator it = errorsQueue.iterator(); while (it.hasNext()) { ErrorQueueEntry entry = it.next(); - lastErrors.add(new String[]{DateFormatUtil.formatExternal(entry.timestamp), entry.type.toLower()}); + lastErrors.add(new String[]{entry.timestamp.toInstant().toString(), entry.type.toLower()}); } } return lastErrors; @@ -145,7 +152,7 @@ class CdcrReplicatorState { String getTimestampOfLastProcessedOperation() { if (logReader != null && logReader.getLastVersion() != -1) { // Shift back to the right by 20 bits the version number - See VersionInfo#getNewClock - return DateFormatUtil.formatExternal(new Date(logReader.getLastVersion() >> 20)); + return Instant.ofEpochMilli(logReader.getLastVersion() >> 20).toString(); } return ""; } diff --git a/solr/core/src/java/org/apache/solr/handler/PingRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/PingRequestHandler.java index 5df46f5a1a5..4b72e0f5d6e 100644 --- a/solr/core/src/java/org/apache/solr/handler/PingRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/PingRequestHandler.java @@ -20,7 +20,7 @@ import java.io.File; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.nio.file.Files; -import java.util.Date; +import java.time.Instant; import java.util.Locale; import org.apache.commons.io.FileUtils; @@ -34,7 +34,6 @@ import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.util.DateFormatUtil; import org.apache.solr.util.plugin.SolrCoreAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -308,8 +307,7 @@ public class PingRequestHandler extends RequestHandlerBase implements SolrCoreAw if ( enable ) { try { // write out when the file was created - FileUtils.write(healthcheck, - DateFormatUtil.formatExternal(new Date()), "UTF-8"); + FileUtils.write(healthcheck, Instant.now().toString(), "UTF-8"); } catch (IOException e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to write healthcheck flag file", e); diff --git a/solr/core/src/java/org/apache/solr/handler/component/PivotFacetValue.java b/solr/core/src/java/org/apache/solr/handler/component/PivotFacetValue.java index a1eed07f679..3280c6c90be 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/PivotFacetValue.java +++ b/solr/core/src/java/org/apache/solr/handler/component/PivotFacetValue.java @@ -26,7 +26,6 @@ import java.util.Map; import org.apache.solr.common.params.FacetParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.util.DateFormatUtil; import org.apache.solr.util.PivotListEntry; /** @@ -92,7 +91,7 @@ public class PivotFacetValue { if (null == value) { out.add(null); } else if (value instanceof Date) { - out.add(DateFormatUtil.formatExternal((Date) value)); + out.add(((Date) value).toInstant().toString()); } else { out.add(value.toString()); } diff --git a/solr/core/src/java/org/apache/solr/handler/component/RangeFacetRequest.java b/solr/core/src/java/org/apache/solr/handler/component/RangeFacetRequest.java index 92b118a8da0..8c0c3812b45 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/RangeFacetRequest.java +++ b/solr/core/src/java/org/apache/solr/handler/component/RangeFacetRequest.java @@ -17,7 +17,6 @@ package org.apache.solr.handler.component; import java.lang.invoke.MethodHandles; - import java.util.ArrayList; import java.util.Date; import java.util.EnumSet; @@ -39,8 +38,6 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.schema.TrieDateField; import org.apache.solr.schema.TrieField; import org.apache.solr.util.DateMathParser; -import org.apache.solr.util.DateFormatUtil; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -165,7 +162,7 @@ public class RangeFacetRequest extends FacetComponent.FacetBase { "Unable to range facet on tried field of unexpected type:" + this.facetOn); } } else if (ft instanceof DateRangeField) { - calc = new DateRangeFieldEndpointCalculator(this, null); + calc = new DateRangeEndpointCalculator(this, null); } else { throw new SolrException (SolrException.ErrorCode.BAD_REQUEST, @@ -703,56 +700,19 @@ public class RangeFacetRequest extends FacetComponent.FacetBase { final Date now) { super(rangeFacetRequest); this.now = now; - if (!(field.getType() instanceof TrieDateField)) { - throw new IllegalArgumentException - (TYPE_ERR_MSG); + if (!(field.getType() instanceof TrieDateField) && !(field.getType() instanceof DateRangeField)) { + throw new IllegalArgumentException(TYPE_ERR_MSG); } } @Override public String formatValue(Date val) { - return DateFormatUtil.formatExternal(val); + return val.toInstant().toString(); } @Override protected Date parseVal(String rawval) { - return DateFormatUtil.parseMath(now, rawval); - } - - @Override - protected Object parseGap(final String rawval) { - return rawval; - } - - @Override - public Date parseAndAddGap(Date value, String gap) throws java.text.ParseException { - final DateMathParser dmp = new DateMathParser(); - dmp.setNow(value); - return dmp.parseMath(gap); - } - } - - private static class DateRangeFieldEndpointCalculator - extends RangeEndpointCalculator { - private final Date now; - - public DateRangeFieldEndpointCalculator(final RangeFacetRequest rangeFacetRequest, - final Date now) { - super(rangeFacetRequest); - this.now = now; - if (!(field.getType() instanceof DateRangeField)) { - throw new IllegalArgumentException(DateRangeEndpointCalculator.TYPE_ERR_MSG); - } - } - - @Override - public String formatValue(Date val) { - return DateFormatUtil.formatExternal(val); - } - - @Override - protected Date parseVal(String rawval) { - return ((DateRangeField) field.getType()).parseMath(now, rawval); + return DateMathParser.parseMath(now, rawval); } @Override diff --git a/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java b/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java index ba77821badb..326220c3dac 100644 --- a/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java @@ -29,12 +29,13 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import org.apache.lucene.index.IndexableField; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.DateUtil; import org.apache.solr.common.util.NamedList; import org.apache.solr.internal.csv.CSVPrinter; import org.apache.solr.internal.csv.CSVStrategy; @@ -46,9 +47,6 @@ import org.apache.solr.search.DocList; import org.apache.solr.search.ReturnFields; import org.apache.solr.util.FastWriter; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; - /** * */ @@ -497,9 +495,7 @@ class CSVWriter extends TextResponseWriter { @Override public void writeDate(String name, Date val) throws IOException { - StringBuilder sb = new StringBuilder(25); - cal = DateUtil.formatDate(val, cal, sb); - writeDate(name, sb.toString()); + writeDate(name, val.toInstant().toString()); } @Override diff --git a/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java b/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java index 3f9e0107126..bde5759957e 100644 --- a/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java @@ -40,7 +40,6 @@ import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.SchemaField; import org.apache.solr.search.DocList; import org.apache.solr.search.ReturnFields; -import org.apache.solr.util.DateFormatUtil; import org.apache.solr.util.FastWriter; /** Base class for text-oriented response writers. @@ -335,7 +334,7 @@ public abstract class TextResponseWriter { public void writeDate(String name, Date val) throws IOException { - writeDate(name, DateFormatUtil.formatExternal(val)); + writeDate(name, val.toInstant().toString()); } diff --git a/solr/core/src/java/org/apache/solr/response/transform/ValueAugmenterFactory.java b/solr/core/src/java/org/apache/solr/response/transform/ValueAugmenterFactory.java index 8279780155c..e4edbdfdb7f 100644 --- a/solr/core/src/java/org/apache/solr/response/transform/ValueAugmenterFactory.java +++ b/solr/core/src/java/org/apache/solr/response/transform/ValueAugmenterFactory.java @@ -20,9 +20,9 @@ import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.DateUtil; import org.apache.solr.common.util.NamedList; import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.util.DateMathParser; /** * @@ -48,7 +48,7 @@ public class ValueAugmenterFactory extends TransformerFactory if( "int".equals( type ) ) return Integer.valueOf( val ); if( "double".equals( type ) ) return Double.valueOf( val ); if( "float".equals( type ) ) return Float.valueOf( val ); - if( "date".equals( type ) ) return DateUtil.parseDate(val); + if( "date".equals( type ) ) return DateMathParser.parseMath(null, val ); } catch( Exception ex ) { throw new SolrException( ErrorCode.BAD_REQUEST, diff --git a/solr/core/src/java/org/apache/solr/rest/ManagedResource.java b/solr/core/src/java/org/apache/solr/rest/ManagedResource.java index 6b023643b87..8668c9cd904 100644 --- a/solr/core/src/java/org/apache/solr/rest/ManagedResource.java +++ b/solr/core/src/java/org/apache/solr/rest/ManagedResource.java @@ -27,7 +27,6 @@ import java.util.Set; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; -import org.apache.solr.common.util.DateUtil; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrResourceLoader; import org.apache.solr.rest.ManagedResourceStorage.StorageIO; @@ -282,16 +281,7 @@ public abstract class ManagedResource { * Returns this resource's initialization timestamp. */ public String getInitializedOn() { - if (initializedOn == null) - return null; - - StringBuilder dateBuf = new StringBuilder(); - try { - DateUtil.formatDate(initializedOn, null, dateBuf); - } catch (IOException e) { - // safe to ignore - } - return dateBuf.toString(); + return initializedOn == null ? null : initializedOn.toInstant().toString(); } /** @@ -299,17 +289,7 @@ public abstract class ManagedResource { * or null if this resource has not been updated since initialization. */ public String getUpdatedSinceInitialization() { - String dateStr = null; - if (lastUpdateSinceInitialization != null) { - StringBuilder dateBuf = new StringBuilder(); - try { - DateUtil.formatDate(lastUpdateSinceInitialization, null, dateBuf); - dateStr = dateBuf.toString(); - } catch (IOException e) { - // safe to ignore here - } - } - return dateStr; + return lastUpdateSinceInitialization == null ? null : lastUpdateSinceInitialization.toInstant().toString(); } /** diff --git a/solr/core/src/java/org/apache/solr/schema/DateRangeField.java b/solr/core/src/java/org/apache/solr/schema/DateRangeField.java index faf049bb5d5..f4070d38d30 100644 --- a/solr/core/src/java/org/apache/solr/schema/DateRangeField.java +++ b/solr/core/src/java/org/apache/solr/schema/DateRangeField.java @@ -22,8 +22,6 @@ import java.util.Date; import java.util.List; import java.util.Map; -import org.locationtech.spatial4j.shape.Shape; - import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.Query; import org.apache.lucene.spatial.prefix.NumberRangePrefixTreeStrategy; @@ -37,7 +35,8 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.request.SolrRequestInfo; import org.apache.solr.search.QParser; import org.apache.solr.search.SyntaxError; -import org.apache.solr.util.DateFormatUtil; +import org.apache.solr.util.DateMathParser; +import org.locationtech.spatial4j.shape.Shape; /** * A field for indexed dates and date ranges. It's mostly compatible with TrieDateField. @@ -75,7 +74,7 @@ public class DateRangeField extends AbstractSpatialPrefixTreeFieldType= 0) { //use Solr standard date format parsing rules. //TODO parse a Calendar instead of a Date, rounded according to DateMath syntax. - Date date = DateFormatUtil.parseMath(null, str); + Date date = DateMathParser.parseMath(null, str); Calendar cal = tree.newCal(); cal.setTime(date); return cal; @@ -115,9 +114,9 @@ public class DateRangeField extends AbstractSpatialPrefixTreeFieldType 0 && Character.isDigit(arg.charAt(0)))) { - return DateFormatUtil.parseMathLenient(null, arg, fp.req); + // check character index 1 to be a digit. Index 0 might be a +/-. + if (arg.startsWith("NOW") || (arg.length() > 1 && Character.isDigit(arg.charAt(1)))) { + Date now = null;//TODO pull from params? + return DateMathParser.parseMath(now, arg); } return null; } diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetRange.java b/solr/core/src/java/org/apache/solr/search/facet/FacetRange.java index b5abf27c4e2..8d3d0f5b9a8 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/FacetRange.java +++ b/solr/core/src/java/org/apache/solr/search/facet/FacetRange.java @@ -34,7 +34,6 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.schema.TrieDateField; import org.apache.solr.schema.TrieField; import org.apache.solr.search.DocSet; -import org.apache.solr.util.DateFormatUtil; import org.apache.solr.util.DateMathParser; public class FacetRange extends FacetRequest { @@ -546,11 +545,11 @@ class FacetRangeProcessor extends FacetProcessor { @Override public String formatValue(Comparable val) { - return DateFormatUtil.formatExternal( (Date)val ); + return ((Date)val).toInstant().toString(); } @Override protected Date parseStr(String rawval) { - return DateFormatUtil.parseMath(now, rawval); + return DateMathParser.parseMath(now, rawval); } @Override protected Object parseGap(final String rawval) { diff --git a/solr/core/src/java/org/apache/solr/update/processor/DocExpirationUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/DocExpirationUpdateProcessorFactory.java index 4608344dd0f..b8a4b5785bc 100644 --- a/solr/core/src/java/org/apache/solr/update/processor/DocExpirationUpdateProcessorFactory.java +++ b/solr/core/src/java/org/apache/solr/update/processor/DocExpirationUpdateProcessorFactory.java @@ -18,46 +18,42 @@ package org.apache.solr.update.processor; import java.io.IOException; import java.lang.invoke.MethodHandles; - -import org.apache.solr.common.SolrException; - -import static org.apache.solr.common.SolrException.ErrorCode.*; - -import org.apache.solr.common.util.ExecutorUtil; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.SolrInputDocument; -import org.apache.solr.common.cloud.Replica; -import org.apache.solr.common.cloud.Slice; -import org.apache.solr.core.CloseHook; -import org.apache.solr.core.SolrCore; -import org.apache.solr.core.CoreContainer; -import org.apache.solr.cloud.CloudDescriptor; -import org.apache.solr.cloud.ZkController; -import org.apache.solr.request.SolrRequestInfo; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.request.LocalSolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.update.AddUpdateCommand; -import org.apache.solr.update.CommitUpdateCommand; -import org.apache.solr.update.DeleteUpdateCommand; -import org.apache.solr.util.DateMathParser; -import org.apache.solr.util.DateFormatUtil; -import org.apache.solr.util.DefaultSolrThreadFactory; -import org.apache.solr.util.plugin.SolrCoreAware; - import java.text.ParseException; -import java.util.Comparator; import java.util.ArrayList; -import java.util.List; import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import org.apache.solr.cloud.CloudDescriptor; +import org.apache.solr.cloud.ZkController; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.cloud.Replica; +import org.apache.solr.common.cloud.Slice; +import org.apache.solr.common.util.ExecutorUtil; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.core.CloseHook; +import org.apache.solr.core.SolrCore; +import org.apache.solr.request.LocalSolrQueryRequest; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestInfo; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.update.AddUpdateCommand; +import org.apache.solr.update.CommitUpdateCommand; +import org.apache.solr.update.DeleteUpdateCommand; +import org.apache.solr.util.DateMathParser; +import org.apache.solr.util.DefaultSolrThreadFactory; +import org.apache.solr.util.plugin.SolrCoreAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.solr.common.SolrException.ErrorCode.BAD_REQUEST; +import static org.apache.solr.common.SolrException.ErrorCode.SERVER_ERROR; + /** *

* Update Processor Factory for managing automatic "expiration" of documents. @@ -404,7 +400,7 @@ public final class DocExpirationUpdateProcessorFactory try { DeleteUpdateCommand del = new DeleteUpdateCommand(req); del.setQuery("{!cache=false}" + expireField + ":[* TO " + - DateFormatUtil.formatExternal(SolrRequestInfo.getRequestInfo().getNOW()) + SolrRequestInfo.getRequestInfo().getNOW().toInstant() + "]"); proc.processDelete(del); diff --git a/solr/core/src/java/org/apache/solr/util/DateFormatUtil.java b/solr/core/src/java/org/apache/solr/util/DateFormatUtil.java deleted file mode 100644 index b4e33be60ec..00000000000 --- a/solr/core/src/java/org/apache/solr/util/DateFormatUtil.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF 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.apache.solr.util; - -import java.text.DateFormat; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.FieldPosition; -import java.text.NumberFormat; -import java.text.ParseException; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -import org.apache.solr.common.SolrException; -import org.apache.solr.common.util.DateUtil; -import org.apache.solr.request.SolrQueryRequest; - -public final class DateFormatUtil { - - public static final TimeZone UTC = TimeZone.getTimeZone("UTC"); - /** - * Fixed TimeZone (UTC) needed for parsing/formatting Dates in the - * canonical representation. - */ - public static final TimeZone CANONICAL_TZ = UTC; - /** - * Fixed Locale needed for parsing/formatting Milliseconds in the - * canonical representation. - */ - public static final Locale CANONICAL_LOCALE = Locale.ROOT; - public static final String NOW = "NOW"; - public static final char Z = 'Z'; - - private static final ISO8601CanonicalDateFormat FORMAT_PROTOTYPE = new ISO8601CanonicalDateFormat(); - - /** - * Thread safe DateFormat that can format in the canonical - * ISO8601 date format, not including the trailing "Z" (since it is - * left off in the internal indexed values) - */ - public final static ThreadLocal FORMAT_THREAD_LOCAL = ThreadLocal.withInitial(FORMAT_PROTOTYPE::clone); - - private DateFormatUtil() {} - - /** - * Parses a String which may be a date (in the standard format) - * followed by an optional math expression. - * @param now an optional fixed date to use as "NOW" in the DateMathParser - * @param val the string to parse - */ - public static Date parseMath(Date now, String val) { - String math; - final DateMathParser p = new DateMathParser(); - - if (null != now) p.setNow(now); - - if (val.startsWith(NOW)) { - math = val.substring(NOW.length()); - } else { - final int zz = val.indexOf(Z); - if (0 < zz) { - math = val.substring(zz+1); - try { - // p.setNow(toObject(val.substring(0,zz))); - p.setNow(parseDate(val.substring(0,zz+1))); - } catch (ParseException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - "Invalid Date in Date Math String:'" + val + '\'', e); - } - } else { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - "Invalid Date String:'" +val+'\''); - } - } - - if (null == math || math.equals("")) { - return p.getNow(); - } - - try { - return p.parseMath(math); - } catch (ParseException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - "Invalid Date Math String:'" +val+'\'',e); - } - } - - /** - * Return the standard human readable form of the date (with trailing 'Z') - */ - public static String formatExternal(Date d) { - return FORMAT_THREAD_LOCAL.get().format(d) + Z; - } - - /** - * Return the standard human readable form of the date - */ - public static String formatDate(Date d) { - return FORMAT_THREAD_LOCAL.get().format(d); - } - - /** - * Thread safe method that can be used to parse a Date - * without the trailing 'Z' - */ - public static Date parseDate(String s) throws ParseException { - return FORMAT_THREAD_LOCAL.get().parse(s); - } - - /** Parse a date string in the standard format, or any supported by DateUtil.parseDate */ - public static Date parseDateLenient(String s, SolrQueryRequest req) throws ParseException { - // request could define timezone in the future - try { - return DateFormatUtil.FORMAT_THREAD_LOCAL.get().parse(s); - } catch (Exception e) { - return DateUtil.parseDate(s); - } - } - - /** - * Parses a String which may be a date - * followed by an optional math expression. - * @param now an optional fixed date to use as "NOW" in the DateMathParser - * @param val the string to parse - */ - public static Date parseMathLenient(Date now, String val, SolrQueryRequest req) { - String math; - final DateMathParser p = new DateMathParser(); - - if (null != now) p.setNow(now); - - if (val.startsWith(DateFormatUtil.NOW)) { - math = val.substring(DateFormatUtil.NOW.length()); - } else { - final int zz = val.indexOf(DateFormatUtil.Z); - if (0 < zz) { - math = val.substring(zz+1); - try { - // p.setNow(toObject(val.substring(0,zz))); - p.setNow(parseDateLenient(val.substring(0,zz+1), req)); - } catch (ParseException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - "Invalid Date in Date Math String: '" + val + '\'', e); - } - } else { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - "Invalid Date String: '" +val+'\''); - } - } - - if (null == math || math.equals("")) { - return p.getNow(); - } - - try { - return p.parseMath(math); - } catch (ParseException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, - "Invalid Date Math String: '" +val+'\'',e); - } - } - - @SuppressWarnings("serial") - static class ISO8601CanonicalDateFormat extends SimpleDateFormat { - - protected NumberFormat millisParser - = NumberFormat.getIntegerInstance(CANONICAL_LOCALE); - - protected NumberFormat millisFormat = new DecimalFormat - (".###", new DecimalFormatSymbols(CANONICAL_LOCALE)); - - public ISO8601CanonicalDateFormat() { - super("yyyy-MM-dd'T'HH:mm:ss", CANONICAL_LOCALE); - this.setTimeZone(CANONICAL_TZ); - } - - @Override - public Date parse(String i, ParsePosition p) { - /* delegate to SimpleDateFormat for easy stuff */ - Date d = super.parse(i, p); - int milliIndex = p.getIndex(); - /* worry about the milliseconds ourselves */ - if (null != d && - -1 == p.getErrorIndex() && - milliIndex + 1 < i.length() && - '.' == i.charAt(milliIndex)) { - p.setIndex(++milliIndex); // NOTE: ++ to chomp '.' - Number millis = millisParser.parse(i, p); - if (-1 == p.getErrorIndex()) { - int endIndex = p.getIndex(); - d = new Date(d.getTime() - + (long)(millis.doubleValue() * Math.pow(10, (3 - endIndex + milliIndex)))); - } - } - return d; - } - - @Override - public StringBuffer format(Date d, StringBuffer toAppendTo, FieldPosition pos) { - /* delegate to SimpleDateFormat for easy stuff */ - super.format(d, toAppendTo, pos); - /* worry about the milliseconds ourselves */ - long millis = d.getTime() % 1000l; - if (0L == millis) { - return toAppendTo; - } - if (millis < 0L) { - // original date was prior to epoch - millis += 1000L; - } - int posBegin = toAppendTo.length(); - toAppendTo.append(millisFormat.format(millis / 1000d)); - if (DateFormat.MILLISECOND_FIELD == pos.getField()) { - pos.setBeginIndex(posBegin); - pos.setEndIndex(toAppendTo.length()); - } - return toAppendTo; - } - - @Override - public DateFormat clone() { - ISO8601CanonicalDateFormat c = (ISO8601CanonicalDateFormat)super.clone(); - c.millisParser = NumberFormat.getIntegerInstance(CANONICAL_LOCALE); - c.millisFormat = new DecimalFormat(".###", new DecimalFormatSymbols(CANONICAL_LOCALE)); - return c; - } - } -} diff --git a/solr/core/src/java/org/apache/solr/util/DateMathParser.java b/solr/core/src/java/org/apache/solr/util/DateMathParser.java index 9e750dcb9a1..489a5aad8ec 100644 --- a/solr/core/src/java/org/apache/solr/util/DateMathParser.java +++ b/solr/core/src/java/org/apache/solr/util/DateMathParser.java @@ -16,18 +16,23 @@ */ package org.apache.solr.util; -import org.apache.solr.request.SolrRequestInfo; -import org.apache.solr.common.params.CommonParams; //jdoc - -import java.util.Date; +import java.text.ParseException; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; import java.util.Calendar; -import java.util.TimeZone; +import java.util.Date; +import java.util.HashMap; import java.util.Locale; import java.util.Map; -import java.util.HashMap; -import java.text.ParseException; +import java.util.TimeZone; import java.util.regex.Pattern; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.request.SolrRequestInfo; + /** * A Simple Utility class for parsing "math" like strings relating to Dates. * @@ -92,14 +97,22 @@ import java.util.regex.Pattern; * @see SolrRequestInfo#getNOW */ public class DateMathParser { - - public static TimeZone UTC = TimeZone.getTimeZone("UTC"); + + public static final TimeZone UTC = TimeZone.getTimeZone("UTC"); /** Default TimeZone for DateMath rounding (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. + * @see #parseNoMath(String) + */ + public static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() + .parseCaseInsensitive().parseLenient().appendInstant().toFormatter(Locale.ROOT); + /** * A mapping from (uppercased) String labels idenyifying time units, * to the corresponding Calendar constant used to set/add/roll that unit @@ -115,6 +128,7 @@ public class DateMathParser { */ public static final Map CALENDAR_UNITS = makeUnitsMap(); + /** @see #CALENDAR_UNITS */ private static Map makeUnitsMap() { @@ -213,7 +227,60 @@ public class DateMathParser { } - + /** + * Parses a String which may be a date (in the standard ISO-8601 format) + * followed by an optional math expression. + * @param now an optional fixed date to use as "NOW" + * @param val the string to parse + */ + public static Date parseMath(Date now, String val) { + String math; + final DateMathParser p = new DateMathParser(); + + if (null != now) p.setNow(now); + + if (val.startsWith("NOW")) { + math = val.substring("NOW".length()); + } else { + final int zz = val.indexOf('Z'); + if (zz == -1) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, + "Invalid Date String:'" + val + '\''); + } + math = val.substring(zz+1); + try { + p.setNow(parseNoMath(val.substring(0, zz + 1))); + } catch (DateTimeParseException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, + "Invalid Date in Date Math String:'" + val + '\'', e); + } + } + + if (null == math || math.equals("")) { + return p.getNow(); + } + + try { + return p.parseMath(math); + } catch (ParseException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, + "Invalid Date Math String:'" +val+'\'',e); + } + } + + /** + * Parsing Solr dates without DateMath. + * This is the standard/pervasive ISO-8601 UTC format but is configured with some leniency. + * + * Callers should almost always call {@link #parseMath(Date, String)} instead. + * + * @throws DateTimeParseException if it can't parse + */ + private static Date parseNoMath(String val) { + //TODO write the equivalent of a Date::from; avoids Instant -> Date + return new Date(PARSER.parse(val, Instant::from).toEpochMilli()); + } + private TimeZone zone; private Locale loc; private Date now; @@ -227,7 +294,6 @@ public class DateMathParser { */ public DateMathParser() { this(null, DEFAULT_MATH_LOCALE); - } /** diff --git a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java index 25ea2c53beb..9d45a0dabc9 100644 --- a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java +++ b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java @@ -16,6 +16,8 @@ */ package org.apache.solr; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringWriter; @@ -24,8 +26,6 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; @@ -874,18 +874,19 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 { } - public void testDateRoundtrip() { - assertU(adoc("id", "99", "bday", "99-01-01T12:34:56.789Z")); - assertU(commit()); - assertQ("year should be canonicallized to 4 digits", - req("q", "id:99"), - "//date[@name='bday'][.='0099-01-01T12:34:56.789Z']"); - assertU(adoc("id", "99", "bday", "1999-01-01T12:34:56.900Z")); - assertU(commit()); - assertQ("millis should be canonicallized to no trailing zeros", - req("q", "id:99"), - "//date[@name='bday'][.='1999-01-01T12:34:56.9Z']"); - } + // commented after SOLR-8904; both are false +// public void testDateRoundtrip() { +// assertU(adoc("id", "99", "bday", "99-01-01T12:34:56.789Z")); +// assertU(commit()); +// assertQ("year should be canonicallized to 4 digits", +// req("q", "id:99"), +// "//date[@name='bday'][.='0099-01-01T12:34:56.789Z']"); +// assertU(adoc("id", "99", "bday", "1999-01-01T12:34:56.900Z")); +// assertU(commit()); +// assertQ("millis should be canonicallized to no trailing zeros", +// req("q", "id:99"), +// "//date[@name='bday'][.='1999-01-01T12:34:56.9Z']"); +// } @Test public void testPatternReplaceFilter() { diff --git a/solr/core/src/test/org/apache/solr/TestTrie.java b/solr/core/src/test/org/apache/solr/TestTrie.java index 0c1497feb20..07935b209bf 100644 --- a/solr/core/src/test/org/apache/solr/TestTrie.java +++ b/solr/core/src/test/org/apache/solr/TestTrie.java @@ -16,19 +16,18 @@ */ package org.apache.solr; +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.TimeZone; + import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.schema.FieldType; import org.apache.solr.schema.TrieField; import org.apache.solr.util.DateMathParser; -import org.apache.solr.util.DateFormatUtil; import org.junit.After; import org.junit.BeforeClass; import org.junit.Test; -import java.text.SimpleDateFormat; -import java.util.Locale; -import java.util.TimeZone; - /** * Tests for TrieField functionality * @@ -173,7 +172,7 @@ public class TestTrie extends SolrTestCaseJ4 { format.setTimeZone(TimeZone.getTimeZone("UTC")); assertU(delQ("*:*")); - DateMathParser dmp = new DateMathParser(DateFormatUtil.UTC, Locale.ROOT); + DateMathParser dmp = new DateMathParser(DateMathParser.UTC, Locale.ROOT); String largestDate = ""; for (int i = 0; i < 10; i++) { // index 10 days starting with today @@ -222,7 +221,7 @@ public class TestTrie extends SolrTestCaseJ4 { // For tdate tests SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT); format.setTimeZone(TimeZone.getTimeZone("UTC")); - DateMathParser dmp = new DateMathParser(DateFormatUtil.UTC, Locale.ROOT); + DateMathParser dmp = new DateMathParser(DateMathParser.UTC, Locale.ROOT); for (int i = 0; i < 10; i++) { long l = Integer.MAX_VALUE + i*1L; diff --git a/solr/core/src/test/org/apache/solr/cloud/SegmentTerminateEarlyTestState.java b/solr/core/src/test/org/apache/solr/cloud/SegmentTerminateEarlyTestState.java index e7de4d1d663..199423b5b53 100644 --- a/solr/core/src/test/org/apache/solr/cloud/SegmentTerminateEarlyTestState.java +++ b/solr/core/src/test/org/apache/solr/cloud/SegmentTerminateEarlyTestState.java @@ -17,6 +17,8 @@ package org.apache.solr.cloud; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -54,7 +56,7 @@ class SegmentTerminateEarlyTestState { final Integer docKey = new Integer(numDocs); SolrInputDocument doc = new SolrInputDocument(); doc.setField(keyField, ""+docKey); - final int MM = TestMiniSolrCloudCluster.random().nextInt(60); + final int MM = TestMiniSolrCloudCluster.random().nextInt(60); // minutes if (minTimestampMM == null || MM <= minTimestampMM.intValue()) { if (minTimestampMM != null && MM < minTimestampMM.intValue()) { minTimestampDocKeys.clear(); @@ -69,7 +71,7 @@ class SegmentTerminateEarlyTestState { maxTimestampMM = new Integer(MM); maxTimestampDocKeys.add(docKey); } - doc.setField(timestampField, "2016-01-01T00:"+MM+":00Z"); + doc.setField(timestampField, ZonedDateTime.of(2016, 1, 1, 0, MM, 0, 0, ZoneOffset.UTC).toInstant().toString()); doc.setField(oddField, ""+(numDocs % 2)); doc.setField(quadField, ""+(numDocs % 4)+1); cloudSolrClient.add(doc); diff --git a/solr/core/src/test/org/apache/solr/cloud/TestCloudPivotFacet.java b/solr/core/src/test/org/apache/solr/cloud/TestCloudPivotFacet.java index 833cadd923b..dc7e0b04c92 100644 --- a/solr/core/src/test/org/apache/solr/cloud/TestCloudPivotFacet.java +++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudPivotFacet.java @@ -16,6 +16,16 @@ */ package org.apache.solr.cloud; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.apache.commons.lang.StringUtils; import org.apache.lucene.util.TestUtil; import org.apache.solr.SolrTestCaseJ4.SuppressSSL; @@ -29,22 +39,11 @@ import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.params.StatsParams; import org.apache.solr.common.util.NamedList; -import org.apache.solr.util.DateFormatUtil; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import static org.apache.solr.common.params.FacetParams.FACET; import static org.apache.solr.common.params.FacetParams.FACET_LIMIT; import static org.apache.solr.common.params.FacetParams.FACET_MISSING; @@ -474,7 +473,7 @@ public class TestCloudPivotFacet extends AbstractFullDistribZkTestBase { // otherwise, build up a term filter... String prefix = "{!term f=" + constraint.getField() + "}"; if (value instanceof Date) { - return prefix + DateFormatUtil.formatExternal((Date)value); + return prefix + ((Date) value).toInstant(); } else { return prefix + value; } diff --git a/solr/core/src/test/org/apache/solr/handler/component/DistributedFacetPivotLargeTest.java b/solr/core/src/test/org/apache/solr/handler/component/DistributedFacetPivotLargeTest.java index 176b57f5e0b..0fa4e8c2e2f 100644 --- a/solr/core/src/test/org/apache/solr/handler/component/DistributedFacetPivotLargeTest.java +++ b/solr/core/src/test/org/apache/solr/handler/component/DistributedFacetPivotLargeTest.java @@ -834,6 +834,7 @@ public class DistributedFacetPivotLargeTest extends BaseDistributedSearchTestCas rfc = pf.getFacetRanges().get(0).getCounts(); for (RangeFacet.Count c : rfc) { + assertEquals(0, c.getCount()); // no docs in our ranges for this pivot drill down } @@ -927,7 +928,7 @@ public class DistributedFacetPivotLargeTest extends BaseDistributedSearchTestCas addPivotDoc(oneShard, "id", getDocNum(), "place_s", "cardiff", "company_t", "microsoft polecat","pay_i",5824,"hiredate_dt", "2012-11-01T12:30:00Z"); addPivotDoc(oneShard, "id", getDocNum(), "place_s", "cardiff", "company_t", "microsoft ","pay_i",6539,"hiredate_dt", "2012-11-01T12:30:00Z"); addPivotDoc(oneShard, "id", getDocNum(), "place_s", "medical staffing network holdings, inc.", "company_t", "microsoft ","pay_i",6539,"hiredate_dt", "2012-11-01T12:30:00Z", "special_s", "xxx"); - addPivotDoc(oneShard, "id", getDocNum(), "place_s", "cardiff", "company_t", "polecat","pay_i",4352,"hiredate_dt", "2012-1-01T12:30:00Z", "special_s", "xxx"); + addPivotDoc(oneShard, "id", getDocNum(), "place_s", "cardiff", "company_t", "polecat","pay_i",4352,"hiredate_dt", "2012-01-01T12:30:00Z", "special_s", "xxx"); addPivotDoc(oneShard, "id", getDocNum(), "place_s", "krakaw", "company_t", "polecat","pay_i",4352,"hiredate_dt", "2012-11-01T12:30:00Z", "special_s", SPECIAL); addPivotDoc(twoShard, "id", getDocNum(), "place_s", "cardiff", "company_t", "microsoft","pay_i",12,"hiredate_dt", "2012-11-01T12:30:00Z", "special_s", SPECIAL); diff --git a/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java index 3b99eb88692..1bb860e20a0 100644 --- a/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java +++ b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java @@ -36,7 +36,6 @@ import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.schema.SchemaField; -import org.apache.solr.util.DateFormatUtil; import org.apache.solr.util.TimeZoneUtils; import org.junit.BeforeClass; import org.junit.Ignore; @@ -790,8 +789,8 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 { //note: add_doc duplicates bday to bday_drf and a_tdt to a_drf (date range field) add_doc(i, "201", f, "1976-07-04T12:08:56.235Z", ff, "1900-01-01T"+ooo); add_doc(i, "202", f, "1976-07-05T00:00:00.000Z", ff, "1976-07-01T"+ooo); - add_doc(i, "203", f, "1976-07-15T00:07:67.890Z", ff, "1976-07-04T"+ooo); - add_doc(i, "204", f, "1976-07-21T00:07:67.890Z", ff, "1976-07-05T"+ooo); + add_doc(i, "203", f, "1976-07-15T00:07:57.890Z", ff, "1976-07-04T"+ooo); + add_doc(i, "204", f, "1976-07-21T00:07:57.890Z", ff, "1976-07-05T"+ooo); add_doc(i, "205", f, "1976-07-13T12:12:25.255Z", ff, "1976-07-05T"+xxx); add_doc(i, "206", f, "1976-07-03T17:01:23.456Z", ff, "1976-07-07T"+ooo); add_doc(i, "207", f, "1976-07-12T12:12:25.255Z", ff, "1976-07-13T"+ooo); @@ -3006,8 +3005,8 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 { } int gapNum = random().nextInt(100) + 1; - params.add(FacetParams.FACET_RANGE_START, DateFormatUtil.formatExternal(dates[0])); - params.add(FacetParams.FACET_RANGE_END, DateFormatUtil.formatExternal(dates[1])); + params.add(FacetParams.FACET_RANGE_START, dates[0].toInstant().toString()); + params.add(FacetParams.FACET_RANGE_END, dates[1].toInstant().toString()); params.add(FacetParams.FACET_RANGE_GAP, String.format(Locale.ROOT, "+%d%s", gapNum, gapUnit)); addCommonRandomRangeParams(params); params.add(FacetParams.FACET_RANGE, field); diff --git a/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java b/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java index c9b59d64fb6..d10ea71fbdb 100644 --- a/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java +++ b/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java @@ -16,16 +16,18 @@ */ package org.apache.solr.response; +import java.io.StringWriter; +import java.time.Instant; +import java.util.Arrays; +import java.util.Date; + import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.common.util.DateUtil; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.search.SolrReturnFields; -import org.junit.*; - -import java.io.StringWriter; -import java.util.Arrays; +import org.junit.BeforeClass; +import org.junit.Test; public class TestCSVResponseWriter extends SolrTestCaseJ4 { @BeforeClass @@ -123,7 +125,7 @@ public class TestCSVResponseWriter extends SolrTestCaseJ4 { d.addField("foo_b",false); d.addField("foo_f",1.414f); d.addField("foo_d",-1.0E300); - d.addField("foo_dt", DateUtil.parseDate("2000-01-02T03:04:05Z")); + d.addField("foo_dt", new Date(Instant.parse("2000-01-02T03:04:05Z").toEpochMilli())); d.addField("score", "2.718"); d = new SolrDocument(); diff --git a/solr/core/src/test/org/apache/solr/schema/DateFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DateFieldTest.java index 8b6ad1d8a3c..9e88b6e7cf6 100644 --- a/solr/core/src/test/org/apache/solr/schema/DateFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DateFieldTest.java @@ -18,24 +18,18 @@ package org.apache.solr.schema; import java.io.File; import java.nio.file.Paths; -import java.text.ParseException; import java.util.Collections; import java.util.Date; -import java.util.Locale; import org.apache.lucene.index.IndexableField; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.core.SolrConfig; import org.apache.solr.core.SolrResourceLoader; -import org.apache.solr.util.DateFormatUtil; -import org.apache.solr.util.DateMathParser; -import org.junit.Ignore; public class DateFieldTest extends SolrTestCaseJ4 { private final String testInstanceDir = TEST_HOME() + File.separator + "collection1"; private final String testConfHome = testInstanceDir + File.separator + "conf"+ File.separator; private TrieDateField f = null; - private DateMathParser p = new DateMathParser(DateFormatUtil.UTC, Locale.ROOT); @Override public void setUp() throws Exception { @@ -50,142 +44,20 @@ public class DateFieldTest extends SolrTestCaseJ4 { f.init(schema, Collections.emptyMap()); } - public void assertFormatParsed(String expected, String input) throws ParseException { - assertEquals("Input: " + input, expected, DateFormatUtil.formatDate(DateFormatUtil.parseMath(new Date(), input))); - } - - public void assertFormatDate(String expected, long input) { - assertEquals("Input: " + input, expected, DateFormatUtil.formatDate(new Date(input))); - } - - public void testToInternal() throws Exception { - assertFormatParsed("1995-12-31T23:59:59.999", "1995-12-31T23:59:59.999666Z"); - assertFormatParsed("1995-12-31T23:59:59.999", "1995-12-31T23:59:59.999Z"); - assertFormatParsed("1995-12-31T23:59:59.99", "1995-12-31T23:59:59.99Z"); - assertFormatParsed("1995-12-31T23:59:59.9", "1995-12-31T23:59:59.9Z"); - assertFormatParsed("1995-12-31T23:59:59", "1995-12-31T23:59:59Z"); - - // here the input isn't in the canonical form, but we should be forgiving - assertFormatParsed("1995-12-31T23:59:59.99", "1995-12-31T23:59:59.990Z"); - assertFormatParsed("1995-12-31T23:59:59.9", "1995-12-31T23:59:59.900Z"); - assertFormatParsed("1995-12-31T23:59:59.9", "1995-12-31T23:59:59.90Z"); - assertFormatParsed("1995-12-31T23:59:59", "1995-12-31T23:59:59.000Z"); - assertFormatParsed("1995-12-31T23:59:59", "1995-12-31T23:59:59.00Z"); - assertFormatParsed("1995-12-31T23:59:59", "1995-12-31T23:59:59.0Z"); - - // kind of kludgy, but we have other tests for the actual date math - assertFormatParsed(DateFormatUtil.formatDate(p.parseMath("/DAY")), "NOW/DAY"); - - // as of Solr 1.3 - assertFormatParsed("1995-12-31T00:00:00", "1995-12-31T23:59:59Z/DAY"); - assertFormatParsed("1995-12-31T00:00:00", "1995-12-31T23:59:59.123Z/DAY"); - assertFormatParsed("1995-12-31T00:00:00", "1995-12-31T23:59:59.123999Z/DAY"); - } - - public void testToInternalObj() throws Exception { - assertFormatDate("1995-12-31T23:59:59.999", 820454399999l); - assertFormatDate("1995-12-31T23:59:59.99", 820454399990l); - assertFormatDate("1995-12-31T23:59:59.9", 820454399900l); - assertFormatDate("1995-12-31T23:59:59", 820454399000l); - } - - public void assertParseMath(long expected, String input) { - Date d = new Date(0); - assertEquals("Input: "+input, expected, DateFormatUtil.parseMath(d, input).getTime()); - } - - // as of Solr1.3 - public void testParseMath() { - assertParseMath(820454699999l, "1995-12-31T23:59:59.999765Z+5MINUTES"); - assertParseMath(820454699999l, "1995-12-31T23:59:59.999Z+5MINUTES"); - assertParseMath(820454699990l, "1995-12-31T23:59:59.99Z+5MINUTES"); - assertParseMath(194918400000l, "1976-03-06T03:06:00Z/DAY"); - - // here the input isn't in the canonical form, but we should be forgiving - assertParseMath(820454699990l, "1995-12-31T23:59:59.990Z+5MINUTES"); - assertParseMath(194918400000l, "1976-03-06T03:06:00.0Z/DAY"); - assertParseMath(194918400000l, "1976-03-06T03:06:00.00Z/DAY"); - assertParseMath(194918400000l, "1976-03-06T03:06:00.000Z/DAY"); - } - - public void testFormatter() { - // just after epoch - assertFormat("1970-01-01T00:00:00.005", 5L); - assertFormat("1970-01-01T00:00:00", 0L); - assertFormat("1970-01-01T00:00:00.37", 370L); - assertFormat("1970-01-01T00:00:00.9", 900L); - - // well after epoch - assertFormat("1999-12-31T23:59:59.005", 946684799005L); - assertFormat("1999-12-31T23:59:59", 946684799000L); - assertFormat("1999-12-31T23:59:59.37", 946684799370L); - assertFormat("1999-12-31T23:59:59.9", 946684799900L); - - // waaaay after epoch - assertFormat("12345-12-31T23:59:59.005", 327434918399005L); - assertFormat("12345-12-31T23:59:59", 327434918399000L); - assertFormat("12345-12-31T23:59:59.37", 327434918399370L); - assertFormat("12345-12-31T23:59:59.9", 327434918399900L); - - // well before epoch - assertFormat("0299-12-31T23:59:59", -52700112001000L); - assertFormat("0299-12-31T23:59:59.123", -52700112000877L); - assertFormat("0299-12-31T23:59:59.09", -52700112000910L); - - } - - /** - * Using dates in the canonical format, verify that parsing+formating - * is an identify function - */ - public void testRoundTrip() throws Exception { - - // typical dates, various precision - assertRoundTrip("1995-12-31T23:59:59.987Z"); - assertRoundTrip("1995-12-31T23:59:59.98Z"); - assertRoundTrip("1995-12-31T23:59:59.9Z"); - assertRoundTrip("1995-12-31T23:59:59Z"); - assertRoundTrip("1976-03-06T03:06:00Z"); - - // dates with atypical years - assertRoundTrip("0001-01-01T01:01:01Z"); - assertRoundTrip("12021-12-01T03:03:03Z"); - } - - @Ignore("SOLR-2773: Non-Positive years don't work") - public void testRoundTripNonPositiveYear() throws Exception { - - // :TODO: ambiguity about year zero - // assertRoundTrip("0000-04-04T04:04:04Z"); - - // dates with negative years - assertRoundTrip("-0005-05-05T05:05:05Z"); - assertRoundTrip("-2021-12-01T04:04:04Z"); - assertRoundTrip("-12021-12-01T02:02:02Z"); - - // :TODO: assertFormat and assertToObject some negative years - - } - - protected void assertFormat(final String expected, final long millis) { - assertEquals(expected, DateFormatUtil.formatDate(new Date(millis))); - } - - protected void assertRoundTrip(String canonicalDate) throws Exception { - Date d = DateFormatUtil.parseDate(canonicalDate); - String result = DateFormatUtil.formatExternal(d); - assertEquals("d:" + d.getTime(), canonicalDate, result); - - } - + // NOTE: Many other tests were moved to DateMathParserTest public void testCreateField() { int props = FieldProperties.INDEXED ^ FieldProperties.STORED; SchemaField sf = new SchemaField( "test", f, props, null ); + // String IndexableField out = f.createField(sf, "1995-12-31T23:59:59Z", 1.0f ); - assertEquals(820454399000l, f.toObject( out ).getTime() ); - - out = f.createField(sf, new Date(820454399000l), 1.0f ); - assertEquals(820454399000l, f.toObject( out ).getTime() ); + assertEquals(820454399000L, f.toObject( out ).getTime() ); + // Date obj + out = f.createField(sf, new Date(820454399000L), 1.0f ); + assertEquals(820454399000L, f.toObject( out ).getTime() ); + // Date math + out = f.createField(sf, "1995-12-31T23:59:59.99Z+5MINUTES", 1.0f); + assertEquals(820454699990L, f.toObject( out ).getTime() ); } + } diff --git a/solr/core/src/test/org/apache/solr/schema/TestUseDocValuesAsStored.java b/solr/core/src/test/org/apache/solr/schema/TestUseDocValuesAsStored.java index 73000b03f3e..48a3f2266bc 100644 --- a/solr/core/src/test/org/apache/solr/schema/TestUseDocValuesAsStored.java +++ b/solr/core/src/test/org/apache/solr/schema/TestUseDocValuesAsStored.java @@ -22,14 +22,12 @@ import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import java.io.File; - import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDateTime; import java.time.Month; import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -71,9 +69,9 @@ public class TestUseDocValuesAsStored extends AbstractBadConfigTestBase { private static final Pattern STORED_FIELD_NAME_PATTERN = Pattern.compile("_dv$"); static { - START_RANDOM_EPOCH_MILLIS = LocalDateTime.of(1970, Month.JANUARY, 1, 0, 0) + START_RANDOM_EPOCH_MILLIS = LocalDateTime.of(-11000, Month.JANUARY, 1, 0, 0)// BC .toInstant(ZoneOffset.UTC).toEpochMilli(); - END_RANDOM_EPOCH_MILLIS = LocalDateTime.of(2030, Month.DECEMBER, 31, 23, 59, 59, 999_000_000) + END_RANDOM_EPOCH_MILLIS = LocalDateTime.of(11000, Month.DECEMBER, 31, 23, 59, 59, 999_000_000) // AD, 5 digit year .toInstant(ZoneOffset.UTC).toEpochMilli(); try { DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); @@ -216,8 +214,7 @@ public class TestUseDocValuesAsStored extends AbstractBadConfigTestBase { } case "date": { long epochMillis = TestUtil.nextLong(random(), START_RANDOM_EPOCH_MILLIS, END_RANDOM_EPOCH_MILLIS); - LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), ZoneOffset.UTC); - values[i] = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + 'Z'; + values[i] = Instant.ofEpochMilli(epochMillis).toString(); break; } default: throw new Exception("unknown type '" + valueType + "'"); diff --git a/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java b/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java index 09fa721eb59..e1726f82a4b 100644 --- a/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java +++ b/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java @@ -24,7 +24,7 @@ import com.google.common.collect.ImmutableMap; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.schema.TrieDateField; -import org.apache.solr.util.DateFormatUtil; +import org.apache.solr.util.DateMathParser; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; @@ -597,45 +597,45 @@ public class AtomicUpdatesTest extends SolrTestCaseJ4 { doc = new SolrInputDocument(); doc.setField("id", "10001"); TrieDateField trieDF = new TrieDateField(); - Date tempDate = DateFormatUtil.parseMath(null, "2014-02-01T12:00:00Z"); - doc.setField("dateRemove", new Date[]{DateFormatUtil.parseMath(null, "2014-02-01T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-07-02T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-03T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-03T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-04T12:00:00Z") + Date tempDate = DateMathParser.parseMath(null, "2014-02-01T12:00:00Z"); + doc.setField("dateRemove", new Date[]{DateMathParser.parseMath(null, "2014-02-01T12:00:00Z"), + DateMathParser.parseMath(null, "2014-07-02T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-03T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-03T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-04T12:00:00Z") }); assertU(adoc(doc)); doc = new SolrInputDocument(); doc.setField("id", "10002"); - doc.setField("dateRemove", new Date[]{DateFormatUtil.parseMath(null, "2014-02-01T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-07-02T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-02T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-03T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-04T12:00:00Z") + doc.setField("dateRemove", new Date[]{DateMathParser.parseMath(null, "2014-02-01T12:00:00Z"), + DateMathParser.parseMath(null, "2014-07-02T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-02T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-03T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-04T12:00:00Z") }); assertU(adoc(doc)); doc = new SolrInputDocument(); doc.setField("id", "10020"); - doc.setField("dateRemove", new Date[]{DateFormatUtil.parseMath(null, "2014-02-01T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-03T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-04T12:00:00Z") + doc.setField("dateRemove", new Date[]{DateMathParser.parseMath(null, "2014-02-01T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-03T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-04T12:00:00Z") }); assertU(adoc(doc)); doc = new SolrInputDocument(); doc.setField("id", "10021"); - doc.setField("dateRemove", new Date[]{DateFormatUtil.parseMath(null, "2014-02-01T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-02T12:00:00Z"), - DateFormatUtil.parseMath(null, "2014-02-04T12:00:00Z") + doc.setField("dateRemove", new Date[]{DateMathParser.parseMath(null, "2014-02-01T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-02T12:00:00Z"), + DateMathParser.parseMath(null, "2014-02-04T12:00:00Z") }); assertU(adoc(doc)); assertU(commit()); assertQ(req("q", "dateRemove:*", "indent", "true"), "//result[@numFound = '4']"); - String dateString = DateFormatUtil.parseMath(null, "2014-02-02T12:00:00Z").toString(); + String dateString = DateMathParser.parseMath(null, "2014-02-02T12:00:00Z").toString(); // assertQ(req("q", "dateRemove:"+URLEncoder.encode(dateString, "UTF-8"), "indent", "true"), "//result[@numFound = '3']"); // assertQ(req("q", "dateRemove:\"2014-09-02T12:00:00Z\"", "indent", "true"), "//result[@numFound = '3']"); // assertQ(req("q", "dateRemove:"+dateString, "indent", "true"), "//result[@numFound = '3']"); //Sun Feb 02 10:00:00 FNT 2014 @@ -645,8 +645,8 @@ public class AtomicUpdatesTest extends SolrTestCaseJ4 { doc = new SolrInputDocument(); doc.setField("id", "10001"); List removeList = new ArrayList(); - removeList.add(DateFormatUtil.parseMath(null, "2014-09-02T12:00:00Z")); - removeList.add(DateFormatUtil.parseMath(null, "2014-09-03T12:00:00Z")); + removeList.add(DateMathParser.parseMath(null, "2014-09-02T12:00:00Z")); + removeList.add(DateMathParser.parseMath(null, "2014-09-03T12:00:00Z")); doc.setField("dateRemove", ImmutableMap.of("remove", removeList)); //behavior when hitting Solr through ZK assertU(adoc(doc)); @@ -658,8 +658,8 @@ public class AtomicUpdatesTest extends SolrTestCaseJ4 { doc = new SolrInputDocument(); doc.setField("id", "10021"); removeList = new ArrayList(); - removeList.add(DateFormatUtil.parseMath(null, "2014-09-02T12:00:00Z")); - removeList.add(DateFormatUtil.parseMath(null, "2014-09-03T12:00:00Z")); + removeList.add(DateMathParser.parseMath(null, "2014-09-02T12:00:00Z")); + removeList.add(DateMathParser.parseMath(null, "2014-09-03T12:00:00Z")); doc.setField("dateRemove", ImmutableMap.of("remove", removeList)); //behavior when hitting Solr through ZK assertU(adoc(doc)); assertU(commit()); @@ -669,7 +669,7 @@ public class AtomicUpdatesTest extends SolrTestCaseJ4 { doc = new SolrInputDocument(); doc.setField("id", "10001"); - doc.setField("dateRemove", ImmutableMap.of("remove", DateFormatUtil.parseMath(null, "2014-09-01T12:00:00Z"))); //behavior when hitting Solr directly + doc.setField("dateRemove", ImmutableMap.of("remove", DateMathParser.parseMath(null, "2014-09-01T12:00:00Z"))); //behavior when hitting Solr directly assertU(adoc(doc)); assertU(commit()); diff --git a/solr/core/src/test/org/apache/solr/util/DateMathParserTest.java b/solr/core/src/test/org/apache/solr/util/DateMathParserTest.java index c3623e8919e..726c71c9617 100644 --- a/solr/core/src/test/org/apache/solr/util/DateMathParserTest.java +++ b/solr/core/src/test/org/apache/solr/util/DateMathParserTest.java @@ -16,20 +16,20 @@ */ package org.apache.solr.util; -import static org.apache.solr.util.DateFormatUtil.UTC; - -import org.apache.lucene.util.LuceneTestCase; -import org.apache.solr.util.DateMathParser; - -import java.text.SimpleDateFormat; import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.Instant; import java.util.Calendar; import java.util.Date; -import java.util.TimeZone; +import java.util.HashMap; import java.util.Locale; import java.util.Map; -import java.util.HashMap; -import java.text.ParseException; +import java.util.TimeZone; + +import org.apache.lucene.util.LuceneTestCase; + +import static org.apache.solr.util.DateMathParser.UTC; /** * Tests that the functions in DateMathParser @@ -333,6 +333,111 @@ public class DateMathParserTest extends LuceneTestCase { } } - + + /* + PARSING / FORMATTING (without date math) Formerly in DateFieldTest. + */ + + + public void testFormatter() { + assertFormat("1995-12-31T23:59:59.999Z", 820454399999l); + assertFormat("1995-12-31T23:59:59.990Z", 820454399990l); + assertFormat("1995-12-31T23:59:59.900Z", 820454399900l); + assertFormat("1995-12-31T23:59:59Z", 820454399000l); + + // just after epoch + assertFormat("1970-01-01T00:00:00.005Z", 5L); + assertFormat("1970-01-01T00:00:00Z", 0L); + assertFormat("1970-01-01T00:00:00.370Z", 370L); + assertFormat("1970-01-01T00:00:00.900Z", 900L); + + // well after epoch + assertFormat("1999-12-31T23:59:59.005Z", 946684799005L); + assertFormat("1999-12-31T23:59:59Z", 946684799000L); + assertFormat("1999-12-31T23:59:59.370Z", 946684799370L); + assertFormat("1999-12-31T23:59:59.900Z", 946684799900L); + + // waaaay after epoch ('+' is required for more than 4 digits in a year) + assertFormat("+12345-12-31T23:59:59.005Z", 327434918399005L); + assertFormat("+12345-12-31T23:59:59Z", 327434918399000L); + assertFormat("+12345-12-31T23:59:59.370Z", 327434918399370L); + assertFormat("+12345-12-31T23:59:59.900Z", 327434918399900L); + + // well before epoch + assertFormat("0299-12-31T23:59:59Z", -52700112001000L); + assertFormat("0299-12-31T23:59:59.123Z", -52700112000877L); + assertFormat("0299-12-31T23:59:59.090Z", -52700112000910L); + + // BC (negative years) + assertFormat("-12021-12-01T02:02:02Z", Instant.parse("-12021-12-01T02:02:02Z").toEpochMilli()); + } + + private void assertFormat(final String expected, final long millis) { + assertEquals(expected, Instant.ofEpochMilli(millis).toString()); + } + + /** + * Using dates in the canonical format, verify that parsing+formatting + * is an identify function + */ + public void testRoundTrip() throws Exception { + // NOTE: the 2nd arg is what the round trip result looks like (may be null if same as input) + + assertParseFormatEquals("1995-12-31T23:59:59.999666Z", "1995-12-31T23:59:59.999Z"); // beyond millis is truncated + assertParseFormatEquals("1995-12-31T23:59:59.999Z", "1995-12-31T23:59:59.999Z"); + assertParseFormatEquals("1995-12-31T23:59:59.99Z", "1995-12-31T23:59:59.990Z"); + assertParseFormatEquals("1995-12-31T23:59:59.9Z", "1995-12-31T23:59:59.900Z"); + assertParseFormatEquals("1995-12-31T23:59:59Z", "1995-12-31T23:59:59Z"); + + // here the input isn't in the canonical form, but we should be forgiving + assertParseFormatEquals("1995-12-31T23:59:59.990Z", "1995-12-31T23:59:59.990Z"); + assertParseFormatEquals("1995-12-31T23:59:59.900Z", "1995-12-31T23:59:59.900Z"); + assertParseFormatEquals("1995-12-31T23:59:59.90Z", "1995-12-31T23:59:59.900Z"); + assertParseFormatEquals("1995-12-31T23:59:59.000Z", "1995-12-31T23:59:59Z"); + assertParseFormatEquals("1995-12-31T23:59:59.00Z", "1995-12-31T23:59:59Z"); + assertParseFormatEquals("1995-12-31T23:59:59.0Z", "1995-12-31T23:59:59Z"); + + // kind of kludgy, but we have other tests for the actual date math + //assertParseFormatEquals("NOW/DAY", p.parseMath("/DAY").toInstant().toString()); + + // as of Solr 1.3 + assertParseFormatEquals("1995-12-31T23:59:59Z/DAY", "1995-12-31T00:00:00Z"); + assertParseFormatEquals("1995-12-31T23:59:59.123Z/DAY", "1995-12-31T00:00:00Z"); + assertParseFormatEquals("1995-12-31T23:59:59.123999Z/DAY", "1995-12-31T00:00:00Z"); + + // typical dates, various precision (0,1,2,3 digits of millis) + assertParseFormatEquals("1995-12-31T23:59:59.987Z", null); + assertParseFormatEquals("1995-12-31T23:59:59.98Z", "1995-12-31T23:59:59.980Z");//add 0 ms + assertParseFormatEquals("1995-12-31T23:59:59.9Z", "1995-12-31T23:59:59.900Z");//add 00 ms + assertParseFormatEquals("1995-12-31T23:59:59Z", null); + assertParseFormatEquals("1976-03-06T03:06:00Z", null); + assertParseFormatEquals("1995-12-31T23:59:59.987654Z", "1995-12-31T23:59:59.987Z");//truncate nanoseconds off + + // dates with atypical years + assertParseFormatEquals("0001-01-01T01:01:01Z", null); + assertParseFormatEquals("+12021-12-01T03:03:03Z", null); + + assertParseFormatEquals("0000-04-04T04:04:04Z", null); // note: 0 AD is also known as 1 BC + + // dates with negative years (BC) + assertParseFormatEquals("-0005-05-05T05:05:05Z", null); + assertParseFormatEquals("-2021-12-01T04:04:04Z", null); + assertParseFormatEquals("-12021-12-01T02:02:02Z", null); + } + + public void testParseLenient() throws Exception { + // dates that only parse thanks to lenient mode of DateTimeFormatter + assertParseFormatEquals("10995-12-31T23:59:59.990Z", "+10995-12-31T23:59:59.990Z"); // missing '+' 5 digit year + assertParseFormatEquals("995-1-2T3:4:5Z", "0995-01-02T03:04:05Z"); // wasn't 0 padded + } + + private void assertParseFormatEquals(String inputStr, String expectedStr) { + if (expectedStr == null) { + expectedStr = inputStr; + } + Date inputDate = DateMathParser.parseMath(null, inputStr); + String resultStr = inputDate.toInstant().toString(); + assertEquals("d:" + inputDate.getTime(), expectedStr, resultStr); + } } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrQuery.java b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrQuery.java index d41673dbb58..be7021075da 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrQuery.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrQuery.java @@ -16,14 +16,6 @@ */ package org.apache.solr.client.solrj; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.FacetParams; -import org.apache.solr.common.params.HighlightParams; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.StatsParams; -import org.apache.solr.common.params.TermsParams; -import org.apache.solr.common.util.DateUtil; - import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -31,6 +23,13 @@ import java.util.List; import java.util.Locale; import java.util.regex.Pattern; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.FacetParams; +import org.apache.solr.common.params.HighlightParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.StatsParams; +import org.apache.solr.common.params.TermsParams; + /** * This is an augmented SolrParams with get/set/add fields for common fields used @@ -266,9 +265,9 @@ public class SolrQuery extends ModifiableSolrParams */ public SolrQuery addDateRangeFacet(String field, Date start, Date end, String gap) { add(FacetParams.FACET_RANGE, field); - add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_START), DateUtil.getThreadLocalDateFormat().format(start)); - add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_END), DateUtil.getThreadLocalDateFormat().format(end)); - add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_GAP), gap); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_START), start.toInstant().toString()); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_END), end.toInstant().toString()); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_GAP), gap); this.set(FacetParams.FACET, true); return this; } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java index e794dab5164..272335ab643 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java @@ -16,31 +16,30 @@ */ package org.apache.solr.client.solrj.impl; -import org.apache.solr.client.solrj.ResponseParser; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.util.DateUtil; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.common.util.XMLErrorLogger; -import org.apache.solr.common.EmptyEntityResolver; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; - import java.io.InputStream; import java.io.Reader; import java.lang.invoke.MethodHandles; +import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; +import org.apache.solr.client.solrj.ResponseParser; +import org.apache.solr.common.EmptyEntityResolver; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.common.util.XMLErrorLogger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * @@ -173,7 +172,7 @@ public class XMLResponseParser extends ResponseParser @Override public Date read( String txt ) { try { - return DateUtil.parseDate(txt); + return new Date(Instant.parse(txt).toEpochMilli()); } catch( Exception ex ) { ex.printStackTrace(); diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java b/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java index 31d54a89fa0..beed40eec9a 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java @@ -16,16 +16,6 @@ */ package org.apache.solr.client.solrj.util; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.common.SolrInputDocument; -import org.apache.solr.common.SolrInputField; -import org.apache.solr.common.cloud.Slice; -import org.apache.solr.common.util.Base64; -import org.apache.solr.common.util.ContentStream; -import org.apache.solr.common.util.ContentStreamBase; -import org.apache.solr.common.util.DateUtil; -import org.apache.solr.common.util.XML; - import java.io.IOException; import java.io.StringWriter; import java.io.Writer; @@ -36,6 +26,14 @@ import java.util.Date; import java.util.Map; import java.util.Map.Entry; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.SolrInputField; +import org.apache.solr.common.cloud.Slice; +import org.apache.solr.common.util.Base64; +import org.apache.solr.common.util.ContentStream; +import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.XML; + /** * @@ -111,7 +109,7 @@ public class ClientUtils private static void writeVal(Writer writer, float boost, String name, Object v, String update) throws IOException { if (v instanceof Date) { - v = DateUtil.getThreadLocalDateFormat().format( (Date)v ); + v = ((Date)v).toInstant().toString(); } else if (v instanceof byte[]) { byte[] bytes = (byte[]) v; v = Base64.byteArrayToBase64(bytes, 0, bytes.length); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrQueryTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrQueryTest.java index 43fdff4d095..816a2cca18a 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrQueryTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrQueryTest.java @@ -16,14 +16,6 @@ */ package org.apache.solr.client.solrj; -import org.apache.lucene.util.LuceneTestCase; -import org.apache.solr.client.solrj.SolrQuery.SortClause; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.params.FacetParams; - -import junit.framework.Assert; -import org.apache.solr.common.util.DateUtil; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; @@ -33,6 +25,12 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; +import junit.framework.Assert; +import org.apache.lucene.util.LuceneTestCase; +import org.apache.solr.client.solrj.SolrQuery.SortClause; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.FacetParams; + /** * * @@ -301,8 +299,8 @@ public class SolrQueryTest extends LuceneTestCase { q.addDateRangeFacet("field", start, end, "+1MONTH"); assertEquals("true", q.get(FacetParams.FACET)); assertEquals("field", q.get(FacetParams.FACET_RANGE)); - assertEquals(DateUtil.getThreadLocalDateFormat().format(start), q.get("f.field." + FacetParams.FACET_RANGE_START)); - assertEquals(DateUtil.getThreadLocalDateFormat().format(end), q.get("f.field." + FacetParams.FACET_RANGE_END)); + assertEquals(start.toInstant().toString(), q.get("f.field." + FacetParams.FACET_RANGE_START)); + assertEquals(end.toInstant().toString(), q.get("f.field." + FacetParams.FACET_RANGE_END)); assertEquals("+1MONTH", q.get("f.field." + FacetParams.FACET_RANGE_GAP)); } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/response/QueryResponseTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/response/QueryResponseTest.java index 1eea4541914..7a69815c7bf 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/response/QueryResponseTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/response/QueryResponseTest.java @@ -20,6 +20,8 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Date; import java.util.List; import junit.framework.Assert; @@ -27,7 +29,6 @@ import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.TestRuleLimitSysouts.Limit; import org.apache.solr.client.solrj.impl.XMLResponseParser; import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.common.util.DateUtil; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrResourceLoader; import org.junit.Test; @@ -82,8 +83,8 @@ public class QueryResponseTest extends LuceneTestCase { assertEquals("4.0", price.getCounts().get(4).getValue()); assertEquals(0, price.getCounts().get(4).getCount()); - assertEquals(DateUtil.parseDate("2005-02-13T15:26:37Z"), manufacturedateDt.getStart()); - assertEquals(DateUtil.parseDate("2008-02-13T15:26:37Z"), manufacturedateDt.getEnd()); + assertEquals(new Date(Instant.parse("2005-02-13T15:26:37Z").toEpochMilli()), manufacturedateDt.getStart()); + assertEquals(new Date(Instant.parse("2008-02-13T15:26:37Z").toEpochMilli()), manufacturedateDt.getEnd()); assertEquals("+1YEAR", manufacturedateDt.getGap()); assertEquals("2005-02-13T15:26:37Z", manufacturedateDt.getCounts().get(0).getValue()); assertEquals(4, manufacturedateDt.getCounts().get(0).getCount()); diff --git a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java index 812663eb449..a751459e596 100644 --- a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java @@ -16,40 +16,7 @@ */ package org.apache.solr; -import junit.framework.Assert; - -import org.apache.commons.io.FileUtils; -import org.apache.lucene.util.Constants; -import org.apache.lucene.util.TestUtil; -import org.apache.solr.client.solrj.SolrClient; -import org.apache.solr.client.solrj.SolrResponse; -import org.apache.solr.client.solrj.SolrServerException; -import org.apache.solr.client.solrj.embedded.JettyConfig; -import org.apache.solr.client.solrj.embedded.JettySolrRunner; -import org.apache.solr.client.solrj.impl.HttpClientUtil; -import org.apache.solr.client.solrj.impl.HttpSolrClient; -import org.apache.solr.client.solrj.request.UpdateRequest; -import org.apache.solr.client.solrj.response.QueryResponse; -import org.apache.solr.client.solrj.response.UpdateResponse; -import org.apache.solr.common.SolrDocument; -import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.common.SolrInputDocument; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.util.DateFormatUtil; -import org.eclipse.jetty.servlet.ServletHolder; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import javax.servlet.Filter; - import java.io.File; import java.io.IOException; import java.lang.annotation.ElementType; @@ -73,6 +40,35 @@ import java.util.Set; import java.util.SortedMap; import java.util.concurrent.atomic.AtomicInteger; +import junit.framework.Assert; +import org.apache.commons.io.FileUtils; +import org.apache.lucene.util.Constants; +import org.apache.lucene.util.TestUtil; +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.SolrResponse; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.embedded.JettyConfig; +import org.apache.solr.client.solrj.embedded.JettySolrRunner; +import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.request.UpdateRequest; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.response.UpdateResponse; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Helper base class for distributed search test cases * @@ -1086,7 +1082,7 @@ public abstract class BaseDistributedSearchTestCase extends SolrTestCaseJ4 { public Object val() { long v = r.nextLong(); Date d = new Date(v); - return DateFormatUtil.formatExternal(d); + return d.toInstant().toString(); } } diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java index 6e511d7e4d6..3ce252f9b4d 100644 --- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java +++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java @@ -37,7 +37,18 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.*; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; import java.util.logging.Level; import com.carrotsearch.randomizedtesting.RandomizedContext; @@ -86,7 +97,6 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.servlet.DirectSolrConnection; import org.apache.solr.util.AbstractSolrTestCase; -import org.apache.solr.util.DateFormatUtil; import org.apache.solr.util.RefCounted; import org.apache.solr.util.RevertDefaultThreadHandlerRule; import org.apache.solr.util.SSLTestConfig; @@ -2036,7 +2046,7 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase { * @see #randomSkewedDate */ public static String randomDate() { - return DateFormatUtil.formatExternal(new Date(random().nextLong())); + return Instant.ofEpochMilli(random().nextLong()).toString(); } /**