/** * 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 org.apache.lucene.util.LocalizedTestCase; import org.apache.solr.util.DateMathParser; import java.text.SimpleDateFormat; import java.text.DateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import java.util.Locale; import java.util.Map; import java.util.HashMap; import java.text.ParseException; /** * Tests that the functions in DateMathParser */ public class DateMathParserTest extends LocalizedTestCase { public static TimeZone UTC = TimeZone.getTimeZone("UTC"); /** * A formatter for specifying every last nuance of a Date for easy * refernece in assertion statements */ private DateFormat fmt; /** * A parser for reading in explicit dates that are convinient to type * in a test */ private DateFormat parser; public DateMathParserTest() { super(); fmt = new SimpleDateFormat ("G yyyyy MM ww WW DD dd F E aa HH hh mm ss SSS z Z",Locale.US); fmt.setTimeZone(UTC); parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS",Locale.US); parser.setTimeZone(UTC); } /** MACRO: Round: parses s, rounds with u, fmts */ protected String r(String s, String u) throws Exception { Date d = parser.parse(s); Calendar c = Calendar.getInstance(UTC, Locale.US); c.setTime(d); DateMathParser.round(c, u); return fmt.format(c.getTime()); } /** MACRO: Add: parses s, adds v u, fmts */ protected String a(String s, int v, String u) throws Exception { Date d = parser.parse(s); Calendar c = Calendar.getInstance(UTC, Locale.US); c.setTime(d); DateMathParser.add(c, v, u); return fmt.format(c.getTime()); } /** MACRO: Expected: parses s, fmts */ protected String e(String s) throws Exception { return fmt.format(parser.parse(s)); } protected void assertRound(String e, String i, String u) throws Exception { String ee = e(e); String rr = r(i,u); assertEquals(ee + " != " + rr + " round:" + i + ":" + u, ee, rr); } protected void assertAdd(String e, String i, int v, String u) throws Exception { String ee = e(e); String aa = a(i,v,u); assertEquals(ee + " != " + aa + " add:" + i + "+" + v + ":" + u, ee, aa); } protected void assertMath(String e, DateMathParser p, String i) throws Exception { String ee = e(e); String aa = fmt.format(p.parseMath(i)); assertEquals(ee + " != " + aa + " math:" + parser.format(p.getNow()) + ":" + i, ee, aa); } public void testCalendarUnitsConsistency() throws Exception { String input = "2001-07-04T12:08:56.235"; for (String u : DateMathParser.CALENDAR_UNITS.keySet()) { try { r(input, u); } catch (IllegalStateException e) { assertNotNull("no logic for rounding: " + u, e); } try { a(input, 1, u); } catch (IllegalStateException e) { assertNotNull("no logic for rounding: " + u, e); } } } public void testRound() throws Exception { String input = "2001-07-04T12:08:56.235"; assertRound("2001-07-04T12:08:56.000", input, "SECOND"); assertRound("2001-07-04T12:08:00.000", input, "MINUTE"); assertRound("2001-07-04T12:00:00.000", input, "HOUR"); assertRound("2001-07-04T00:00:00.000", input, "DAY"); assertRound("2001-07-01T00:00:00.000", input, "MONTH"); assertRound("2001-01-01T00:00:00.000", input, "YEAR"); } public void testAddZero() throws Exception { String input = "2001-07-04T12:08:56.235"; for (String u : DateMathParser.CALENDAR_UNITS.keySet()) { assertAdd(input, input, 0, u); } } public void testAdd() throws Exception { String input = "2001-07-04T12:08:56.235"; assertAdd("2001-07-04T12:08:56.236", input, 1, "MILLISECOND"); assertAdd("2001-07-04T12:08:57.235", input, 1, "SECOND"); assertAdd("2001-07-04T12:09:56.235", input, 1, "MINUTE"); assertAdd("2001-07-04T13:08:56.235", input, 1, "HOUR"); assertAdd("2001-07-05T12:08:56.235", input, 1, "DAY"); assertAdd("2001-08-04T12:08:56.235", input, 1, "MONTH"); assertAdd("2002-07-04T12:08:56.235", input, 1, "YEAR"); } public void testParseStatelessness() throws Exception { DateMathParser p = new DateMathParser(UTC, Locale.US); p.setNow(parser.parse("2001-07-04T12:08:56.235")); String e = fmt.format(p.parseMath("")); Date trash = p.parseMath("+7YEARS"); trash = p.parseMath("/MONTH"); trash = p.parseMath("-5DAYS+20MINUTES"); Thread.currentThread().sleep(5); String a = fmt.format(p.parseMath("")); assertEquals("State of DateMathParser changed", e, a); } public void testParseMath() throws Exception { DateMathParser p = new DateMathParser(UTC, Locale.US); p.setNow(parser.parse("2001-07-04T12:08:56.235")); // No-Op assertMath("2001-07-04T12:08:56.235", p, ""); // simple round assertMath("2001-07-04T12:08:56.000", p, "/SECOND"); assertMath("2001-07-04T12:08:00.000", p, "/MINUTE"); assertMath("2001-07-04T12:00:00.000", p, "/HOUR"); assertMath("2001-07-04T00:00:00.000", p, "/DAY"); assertMath("2001-07-01T00:00:00.000", p, "/MONTH"); assertMath("2001-01-01T00:00:00.000", p, "/YEAR"); // simple addition assertMath("2001-07-04T12:08:56.236", p, "+1MILLISECOND"); assertMath("2001-07-04T12:08:57.235", p, "+1SECOND"); assertMath("2001-07-04T12:09:56.235", p, "+1MINUTE"); assertMath("2001-07-04T13:08:56.235", p, "+1HOUR"); assertMath("2001-07-05T12:08:56.235", p, "+1DAY"); assertMath("2001-08-04T12:08:56.235", p, "+1MONTH"); assertMath("2002-07-04T12:08:56.235", p, "+1YEAR"); // simple subtraction assertMath("2001-07-04T12:08:56.234", p, "-1MILLISECOND"); assertMath("2001-07-04T12:08:55.235", p, "-1SECOND"); assertMath("2001-07-04T12:07:56.235", p, "-1MINUTE"); assertMath("2001-07-04T11:08:56.235", p, "-1HOUR"); assertMath("2001-07-03T12:08:56.235", p, "-1DAY"); assertMath("2001-06-04T12:08:56.235", p, "-1MONTH"); assertMath("2000-07-04T12:08:56.235", p, "-1YEAR"); // simple '+/-' assertMath("2001-07-04T12:08:56.235", p, "+1MILLISECOND-1MILLISECOND"); assertMath("2001-07-04T12:08:56.235", p, "+1SECOND-1SECOND"); assertMath("2001-07-04T12:08:56.235", p, "+1MINUTE-1MINUTE"); assertMath("2001-07-04T12:08:56.235", p, "+1HOUR-1HOUR"); assertMath("2001-07-04T12:08:56.235", p, "+1DAY-1DAY"); assertMath("2001-07-04T12:08:56.235", p, "+1MONTH-1MONTH"); assertMath("2001-07-04T12:08:56.235", p, "+1YEAR-1YEAR"); // simple '-/+' assertMath("2001-07-04T12:08:56.235", p, "-1MILLISECOND+1MILLISECOND"); assertMath("2001-07-04T12:08:56.235", p, "-1SECOND+1SECOND"); assertMath("2001-07-04T12:08:56.235", p, "-1MINUTE+1MINUTE"); assertMath("2001-07-04T12:08:56.235", p, "-1HOUR+1HOUR"); assertMath("2001-07-04T12:08:56.235", p, "-1DAY+1DAY"); assertMath("2001-07-04T12:08:56.235", p, "-1MONTH+1MONTH"); assertMath("2001-07-04T12:08:56.235", p, "-1YEAR+1YEAR"); // more complex stuff assertMath("2000-07-04T12:08:56.236", p, "+1MILLISECOND-1YEAR"); assertMath("2000-07-04T12:08:57.235", p, "+1SECOND-1YEAR"); assertMath("2000-07-04T12:09:56.235", p, "+1MINUTE-1YEAR"); assertMath("2000-07-04T13:08:56.235", p, "+1HOUR-1YEAR"); assertMath("2000-07-05T12:08:56.235", p, "+1DAY-1YEAR"); assertMath("2000-08-04T12:08:56.235", p, "+1MONTH-1YEAR"); assertMath("2000-07-04T12:08:56.236", p, "-1YEAR+1MILLISECOND"); assertMath("2000-07-04T12:08:57.235", p, "-1YEAR+1SECOND"); assertMath("2000-07-04T12:09:56.235", p, "-1YEAR+1MINUTE"); assertMath("2000-07-04T13:08:56.235", p, "-1YEAR+1HOUR"); assertMath("2000-07-05T12:08:56.235", p, "-1YEAR+1DAY"); assertMath("2000-08-04T12:08:56.235", p, "-1YEAR+1MONTH"); assertMath("2000-07-01T00:00:00.000", p, "-1YEAR+1MILLISECOND/MONTH"); assertMath("2000-07-04T00:00:00.000", p, "-1YEAR+1SECOND/DAY"); assertMath("2000-07-04T00:00:00.000", p, "-1YEAR+1MINUTE/DAY"); assertMath("2000-07-04T13:00:00.000", p, "-1YEAR+1HOUR/HOUR"); assertMath("2000-07-05T12:08:56.000", p, "-1YEAR+1DAY/SECOND"); assertMath("2000-08-04T12:08:56.000", p, "-1YEAR+1MONTH/SECOND"); // "tricky" cases p.setNow(parser.parse("2006-01-31T17:09:59.999")); assertMath("2006-02-28T17:09:59.999", p, "+1MONTH"); assertMath("2008-02-29T17:09:59.999", p, "+25MONTH"); assertMath("2006-02-01T00:00:00.000", p, "/MONTH+35DAYS/MONTH"); assertMath("2006-01-31T17:10:00.000", p, "+3MILLIS/MINUTE"); } public void testParseMathExceptions() throws Exception { DateMathParser p = new DateMathParser(UTC, Locale.US); p.setNow(parser.parse("2001-07-04T12:08:56.235")); Map<String,Integer> badCommands = new HashMap<String,Integer>(); badCommands.put("/", 1); badCommands.put("+", 1); badCommands.put("-", 1); badCommands.put("/BOB", 1); badCommands.put("+SECOND", 1); badCommands.put("-2MILLI/", 4); badCommands.put(" +BOB", 0); badCommands.put("+2SECONDS ", 3); badCommands.put("/4", 1); badCommands.put("?SECONDS", 0); for (String command : badCommands.keySet()) { try { Date out = p.parseMath(command); fail("Didn't generate ParseException for: " + command); } catch (ParseException e) { assertEquals("Wrong pos for: " + command + " => " + e.getMessage(), badCommands.get(command).intValue(), e.getErrorOffset()); } } } }