diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/DatePartEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/DatePartEvaluator.java index dc0e98692a3..c3c5a61a7f5 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/DatePartEvaluator.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/DatePartEvaluator.java @@ -22,7 +22,10 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; import java.time.temporal.IsoFields; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Arrays; import java.util.Date; import java.util.Locale; @@ -62,7 +65,7 @@ public class DatePartEvaluator extends NumberEvaluator { public Number evaluate(Tuple tuple) throws IOException { Instant instant = null; - LocalDateTime date = null; + TemporalAccessor date = null; //First evaluate the parameter StreamEvaluator streamEvaluator = subEvaluators.get(0); @@ -76,8 +79,8 @@ public class DatePartEvaluator extends NumberEvaluator { instant = (Instant) tupleValue; } else if (tupleValue instanceof Date) { instant = ((Date) tupleValue).toInstant(); - } else if (tupleValue instanceof LocalDateTime) { - date = ((LocalDateTime) tupleValue); + } else if (tupleValue instanceof TemporalAccessor) { + date = ((TemporalAccessor) tupleValue); } if (instant != null) { @@ -110,32 +113,38 @@ public class DatePartEvaluator extends NumberEvaluator { * @param date * @return the evaluated value */ - private Number evaluate(LocalDateTime date) throws IOException { - switch (function) { - case year: - return date.getYear(); - case month: - return date.getMonthValue(); - case day: - return date.getDayOfMonth(); - case dayofyear: - return date.getDayOfYear(); - case hour: - return date.getHour(); - case minute: - return date.getMinute(); - case second: - return date.getSecond(); - case dayofquarter: - return date.get(IsoFields.DAY_OF_QUARTER); - case quarter: - return date.get(IsoFields.QUARTER_OF_YEAR); - case week: - return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); - case epoch: - return date.atZone(ZoneOffset.UTC).toInstant().toEpochMilli(); + private Number evaluate(TemporalAccessor date) throws IOException { + try { + switch (function) { + case year: + return date.get(ChronoField.YEAR); + case month: + return date.get(ChronoField.MONTH_OF_YEAR); + case day: + return date.get(ChronoField.DAY_OF_MONTH); + case dayofyear: + return date.get(ChronoField.DAY_OF_YEAR); + case hour: + return date.get(ChronoField.HOUR_OF_DAY); + case minute: + return date.get(ChronoField.MINUTE_OF_HOUR); + case second: + return date.get(ChronoField.SECOND_OF_MINUTE); + case dayofquarter: + return date.get(IsoFields.DAY_OF_QUARTER); + case quarter: + return date.get(IsoFields.QUARTER_OF_YEAR); + case week: + return date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); + case epoch: + if (date instanceof LocalDateTime) { + return ((LocalDateTime)date).atZone(ZoneOffset.UTC).toInstant().toEpochMilli(); + } + } + } catch (UnsupportedTemporalTypeException utte) { + throw new IOException(String.format(Locale.ROOT, "It is not possible to call '%s' function on %s", function, date.getClass().getName())); } - throw new IOException(String.format(Locale.ROOT, "Unsupported function %s called on LocalDateTime %s", function, date.toString())); + throw new IOException(String.format(Locale.ROOT, "Unsupported function '%s' called on %s", function, date.toString())); } @Override diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/DatePartEvaluatorTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/DatePartEvaluatorTest.java index 703b015756f..2e5c4b0e86d 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/DatePartEvaluatorTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/DatePartEvaluatorTest.java @@ -20,6 +20,8 @@ package org.apache.solr.client.solrj.io.stream.eval; import java.io.IOException; import java.time.Instant; import java.time.LocalDateTime; +import java.time.MonthDay; +import java.time.YearMonth; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; @@ -72,7 +74,7 @@ public class DatePartEvaluatorTest { evaluator = factory.constructEvaluator("nope(a)"); evaluator.evaluate(new Tuple(null)); assertTrue(false); - } catch (Exception e) { + } catch (IOException e) { assertTrue(e.getCause().getCause().getMessage().contains("Invalid date expression nope")); assertTrue(e.getCause().getCause().getMessage().contains("expecting one of [year, month, day")); } @@ -80,21 +82,21 @@ public class DatePartEvaluatorTest { try { evaluator = factory.constructEvaluator("week()"); assertTrue(false); - } catch (Exception e) { + } catch (IOException e) { assertTrue(e.getCause().getCause().getMessage().contains("Invalid expression week()")); } try { evaluator = factory.constructEvaluator("week(a, b)"); assertTrue(false); - } catch (Exception e) { + } catch (IOException e) { assertTrue(e.getCause().getCause().getMessage().contains("expecting one value but found 2")); } try { evaluator = factory.constructEvaluator("Week()"); assertTrue(false); - } catch (Exception e) { + } catch (IOException e) { assertTrue(e.getMessage().contains("Invalid evaluator expression Week() - function 'Week' is unknown")); } } @@ -109,7 +111,7 @@ public class DatePartEvaluatorTest { values.put("a", 12); Object result = evaluator.evaluate(new Tuple(values)); assertTrue(false); - } catch (Exception e) { + } catch (IOException e) { assertEquals("Invalid parameter 12 - The parameter must be a string formatted ISO_INSTANT or of type Instant,Date or LocalDateTime.", e.getMessage()); } @@ -118,7 +120,7 @@ public class DatePartEvaluatorTest { values.put("a", "1995-12-31"); Object result = evaluator.evaluate(new Tuple(values)); assertTrue(false); - } catch (Exception e) { + } catch (IOException e) { assertEquals("Invalid parameter 1995-12-31 - The String must be formatted in the ISO_INSTANT date format.", e.getMessage()); } @@ -127,7 +129,7 @@ public class DatePartEvaluatorTest { values.put("a", ""); Object result = evaluator.evaluate(new Tuple(values)); assertTrue(false); - } catch (Exception e) { + } catch (IOException e) { assertEquals("Invalid parameter - The parameter must be a string formatted ISO_INSTANT or of type Instant,Date or LocalDateTime.", e.getMessage()); } @@ -220,6 +222,34 @@ public class DatePartEvaluatorTest { testFunction("epoch(a)", localDateTime, aDate.getTime()); } + @Test + public void testLimitedFunctions() throws Exception { + + MonthDay monthDay = MonthDay.of(12,5); + testFunction("month(a)", monthDay, 12); + testFunction("day(a)", monthDay, 5); + + try { + testFunction("year(a)", monthDay, 2017); + assertTrue(false); + } catch (IOException e) { + assertEquals("It is not possible to call 'year' function on java.time.MonthDay", e.getMessage()); + } + + YearMonth yearMonth = YearMonth.of(2018, 4); + testFunction("month(a)", yearMonth, 4); + testFunction("year(a)", yearMonth, 2018); + + try { + testFunction("day(a)", yearMonth, 5); + assertTrue(false); + } catch (IOException e) { + assertEquals("It is not possible to call 'day' function on java.time.YearMonth", e.getMessage()); + } + + } + + public void testFunction(String expression, Object value, Number expected) throws Exception { StreamEvaluator evaluator = factory.constructEvaluator(expression); values.clear();