diff --git a/src/main/java/org/alfresco/util/SearchDateConversion.java b/src/main/java/org/alfresco/util/SearchDateConversion.java
new file mode 100644
index 0000000000..1829827438
--- /dev/null
+++ b/src/main/java/org/alfresco/util/SearchDateConversion.java
@@ -0,0 +1,177 @@
+/*
+ * #%L
+ * Alfresco Data model classes
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.util;
+
+import org.alfresco.service.cmr.search.IntervalSet;
+import org.springframework.extensions.surf.util.I18NUtil;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Moved from Solr4QueryParser
+ */
+public class SearchDateConversion
+{
+
+ public static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC");
+ /**
+ *
+ * @param dateString
+ * @return Pair
+ */
+ public static Pair parseDateString(String dateString)
+ {
+ try
+ {
+ Pair result = CachingDateFormat.lenientParse(dateString, Calendar.YEAR);
+ return result;
+ } catch (java.text.ParseException e)
+ {
+ SimpleDateFormat oldDf = CachingDateFormat.getDateFormat();
+ try
+ {
+ Date date = oldDf.parse(dateString);
+ return new Pair(date, Calendar.SECOND);
+ } catch (java.text.ParseException ee)
+ {
+ if (dateString.equalsIgnoreCase("min"))
+ {
+ Calendar cal = Calendar.getInstance(I18NUtil.getLocale());
+ cal.set(Calendar.YEAR, cal.getMinimum(Calendar.YEAR));
+ cal.set(Calendar.DAY_OF_YEAR, cal.getMinimum(Calendar.DAY_OF_YEAR));
+ cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY));
+ cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE));
+ cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND));
+ cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND));
+ return new Pair(cal.getTime(), Calendar.MILLISECOND);
+ } else if (dateString.equalsIgnoreCase("now"))
+ {
+ return new Pair(new Date(), Calendar.MILLISECOND);
+ } else if (dateString.equalsIgnoreCase("today"))
+ {
+ Calendar cal = Calendar.getInstance(I18NUtil.getLocale());
+ cal.setTime(new Date());
+ cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY));
+ cal.set(Calendar.MINUTE, cal.getMinimum(Calendar.MINUTE));
+ cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND));
+ cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND));
+ return new Pair(cal.getTime(), Calendar.DAY_OF_MONTH);
+ } else if (dateString.equalsIgnoreCase("max"))
+ {
+ Calendar cal = Calendar.getInstance(I18NUtil.getLocale());
+ cal.set(Calendar.YEAR, cal.getMaximum(Calendar.YEAR));
+ cal.set(Calendar.DAY_OF_YEAR, cal.getMaximum(Calendar.DAY_OF_YEAR));
+ cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY));
+ cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE));
+ cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND));
+ cal.set(Calendar.MILLISECOND, cal.getMaximum(Calendar.MILLISECOND));
+ return new Pair(cal.getTime(), Calendar.MILLISECOND);
+ } else
+ {
+ return null; // delegate to SOLR date parsing
+ }
+ }
+ }
+ }
+
+ /**
+ * @param dateAndResolution
+ * @return String date
+ */
+ public static String getDateEnd(Pair dateAndResolution)
+ {
+ Calendar cal = Calendar.getInstance(I18NUtil.getLocale());
+ cal.setTime(dateAndResolution.getFirst());
+ switch (dateAndResolution.getSecond())
+ {
+ case Calendar.YEAR:
+ cal.set(Calendar.MONTH, cal.getActualMaximum(Calendar.MONTH));
+ case Calendar.MONTH:
+ cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+ case Calendar.DAY_OF_MONTH:
+ cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY));
+ case Calendar.HOUR_OF_DAY:
+ cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE));
+ case Calendar.MINUTE:
+ cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND));
+ case Calendar.SECOND:
+ cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND));
+ case Calendar.MILLISECOND:
+ default:
+ }
+ SimpleDateFormat formatter = CachingDateFormat.getSolrDatetimeFormat();
+ formatter.setTimeZone(UTC_TIMEZONE);
+ return formatter.format(cal.getTime());
+ }
+
+ /**
+ * @param dateAndResolution
+ * @return String date
+ */
+ public static String getDateStart(Pair dateAndResolution)
+ {
+ Calendar cal = Calendar.getInstance(I18NUtil.getLocale());
+ cal.setTime(dateAndResolution.getFirst());
+ switch (dateAndResolution.getSecond())
+ {
+ case Calendar.YEAR:
+ cal.set(Calendar.MONTH, cal.getActualMinimum(Calendar.MONTH));
+ case Calendar.MONTH:
+ cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
+ case Calendar.DAY_OF_MONTH:
+ cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
+ case Calendar.HOUR_OF_DAY:
+ cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE));
+ case Calendar.MINUTE:
+ cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND));
+ case Calendar.SECOND:
+ cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND));
+ case Calendar.MILLISECOND:
+ default:
+ }
+ SimpleDateFormat formatter = CachingDateFormat.getSolrDatetimeFormat();
+ formatter.setTimeZone(UTC_TIMEZONE);
+ return formatter.format(cal.getTime());
+ }
+
+ public static IntervalSet parseDateInterval(IntervalSet theSet, boolean isDate)
+ {
+ if (isDate)
+ {
+ Pair dateAndResolution1 = parseDateString(theSet.getStart());
+ Pair dateAndResolution2 = parseDateString(theSet.getEnd());
+ String start = dateAndResolution1 == null ? theSet.getStart()
+ : (theSet.isStartInclusive() ? getDateStart(dateAndResolution1) : getDateEnd(dateAndResolution1));
+ String end = dateAndResolution2 == null ? theSet.getEnd()
+ : (theSet.isEndInclusive() ? getDateEnd(dateAndResolution2) : getDateStart(dateAndResolution2));
+ return new IntervalSet(start, end, theSet.getLabel(), false, true);
+ }
+ return theSet;
+ }
+}
diff --git a/src/test/java/org/alfresco/util/SearchDateConversionTest.java b/src/test/java/org/alfresco/util/SearchDateConversionTest.java
new file mode 100644
index 0000000000..246522381d
--- /dev/null
+++ b/src/test/java/org/alfresco/util/SearchDateConversionTest.java
@@ -0,0 +1,170 @@
+/*
+ * #%L
+ * Alfresco Data model classes
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.util;
+
+import static org.junit.Assert.*;
+import org.alfresco.service.cmr.search.IntervalSet;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.springframework.extensions.surf.util.I18NUtil;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Basic calls
+ */
+public class SearchDateConversionTest
+{
+ SearchDateConversion subject = new SearchDateConversion();
+
+ @Test
+ public void parseDateString() throws Exception
+ {
+ Pair result = subject.parseDateString("2017");
+ assertEquals(Calendar.YEAR, result.getSecond().intValue());
+ assertEquals(1483225200000l, result.getFirst().getTime());
+
+ result = subject.parseDateString("2017-12");
+ assertEquals(Calendar.MONTH, result.getSecond().intValue());
+ assertEquals(1512082800000l, result.getFirst().getTime());
+
+ result = subject.parseDateString("2017-12-12");
+ assertEquals(Calendar.DAY_OF_MONTH, result.getSecond().intValue());
+ assertEquals(1513033200000l, result.getFirst().getTime());
+
+ result = subject.parseDateString("NOW");
+ assertEquals(Calendar.MILLISECOND, result.getSecond().intValue());
+
+ result = subject.parseDateString("MIN");
+ assertEquals(Calendar.MILLISECOND, result.getSecond().intValue());
+
+ result = subject.parseDateString("TODAY");
+ assertEquals(Calendar.DAY_OF_MONTH, result.getSecond().intValue());
+
+ result = subject.parseDateString("MAX");
+ assertEquals(Calendar.MILLISECOND, result.getSecond().intValue());
+
+ result = subject.parseDateString("NONSENSE");
+ assertNull(result);
+
+ result = subject.parseDateString("NOW/YEAR");
+ assertNull(result);
+
+ result = subject.parseDateString("*");
+ assertNull(result);
+ }
+
+ @Test
+ public void getDateEnd() throws Exception
+ {
+ setLocale(Locale.UK);
+ Pair result = subject.parseDateString("2017-12");
+ assertEquals("2017-12-31T22:59:59.999Z", subject.getDateEnd(result));
+
+ result = subject.parseDateString("2017-12-12");
+ assertEquals("2017-12-12T22:59:59.999Z", subject.getDateEnd(result));
+
+ result = subject.parseDateString("2017");
+ assertEquals("2017-12-31T22:59:59.999Z", subject.getDateEnd(result));
+ }
+
+ @Test
+ public void getDateStart() throws Exception
+ {
+ setLocale(Locale.UK);
+ Pair result = subject.parseDateString("2017-12");
+ assertEquals("2017-11-30T23:00:00.000Z", subject.getDateStart(result));
+
+ result = subject.parseDateString("2017-12-12");
+ assertEquals("2017-12-11T23:00:00.000Z", subject.getDateStart(result));
+
+ result = subject.parseDateString("2017");
+ assertEquals("2016-12-31T23:00:00.000Z", subject.getDateStart(result));
+ }
+
+ @Test
+ public void testIntervalDates() throws UnsupportedEncodingException
+ {
+ setLocale(Locale.UK);
+ IntervalSet intervalSet = new IntervalSet("1", "10", "just numbers", false, true);
+ IntervalSet validated = subject.parseDateInterval(intervalSet, false);
+ assertEquals(intervalSet, validated);
+
+ intervalSet = new IntervalSet("2006", "2010", "years", true, true);
+ validated = subject.parseDateInterval(intervalSet, true);
+ assertEquals("2005-12-31T23:00:00.000Z", validated.getStart());
+ assertFalse(validated.isStartInclusive());
+ assertEquals("2010-12-31T22:59:59.999Z", validated.getEnd());
+ assertTrue(validated.isEndInclusive());
+
+ intervalSet = new IntervalSet("2006", "2010", "years", false, false);
+ validated = subject.parseDateInterval(intervalSet, true);
+ assertEquals("2006-12-31T22:59:59.999Z", validated.getStart());
+ assertFalse(validated.isStartInclusive());
+ assertEquals("2009-12-31T23:00:00.000Z", validated.getEnd());
+ assertTrue(validated.isEndInclusive());
+
+ intervalSet = new IntervalSet("2006-09", "2010-03", "months", true, true);
+ validated = subject.parseDateInterval(intervalSet, true);
+ assertEquals("2006-08-31T22:00:00.000Z", validated.getStart());
+ assertFalse(validated.isStartInclusive());
+ assertEquals("2010-03-31T21:59:59.999Z", validated.getEnd());
+ assertTrue(validated.isEndInclusive());
+
+ intervalSet = new IntervalSet("2006-09", "2010-03", "months", false, false);
+ validated = subject.parseDateInterval(intervalSet, true);
+ assertEquals("2006-09-30T21:59:59.999Z", validated.getStart());
+ assertFalse(validated.isStartInclusive());
+ assertEquals("2010-02-28T23:00:00.000Z", validated.getEnd());
+ assertTrue(validated.isEndInclusive());
+
+ intervalSet = new IntervalSet("2017-09-01", "2017-09-30", "sept", true, true);
+ validated = subject.parseDateInterval(intervalSet, true);
+ assertEquals("2017-08-31T22:00:00.000Z", validated.getStart());
+ assertFalse(validated.isStartInclusive());
+ assertEquals("2017-09-30T21:59:59.999Z", validated.getEnd());
+ assertTrue(validated.isEndInclusive());
+
+ intervalSet = new IntervalSet("2017-08-31", "2017-10-01", "sept", false, false);
+ validated = subject.parseDateInterval(intervalSet, true);
+ assertEquals("2017-08-31T21:59:59.999Z", validated.getStart());
+ assertFalse(validated.isStartInclusive());
+ assertEquals("2017-09-30T22:00:00.000Z", validated.getEnd());
+ assertTrue(validated.isEndInclusive());
+ }
+
+
+ private void setLocale(Locale locale)
+ {
+ I18NUtil.setLocale(locale);
+ }
+
+
+}
\ No newline at end of file