diff --git a/src/java/org/apache/lucene/document/DateField.java b/src/java/org/apache/lucene/document/DateField.java index 5cecac44a95..20ef345cd18 100644 --- a/src/java/org/apache/lucene/document/DateField.java +++ b/src/java/org/apache/lucene/document/DateField.java @@ -18,26 +18,27 @@ package org.apache.lucene.document; import java.util.Date; +import org.apache.lucene.search.PrefixQuery; // for javadoc +import org.apache.lucene.search.RangeQuery; // for javadoc + /** * Provides support for converting dates to strings and vice-versa. * The strings are structured so that lexicographic sorting orders by date, * which makes them suitable for use as field values and search terms. * - *

- * Note that you do not have to use this class, you can just save your - * dates as strings if lexicographic sorting orders them by date. This is - * the case for example for dates like yyyy-mm-dd hh:mm:ss - * (of course you can leave out the delimiter characters to save some space). - * The advantage with using such a format is that you can easily save dates - * with the required granularity, e.g. leaving out seconds. This saves memory - * when searching with a RangeQuery or PrefixQuery, as Lucene - * expands these queries to a BooleanQuery with potentially very many terms. + *

Note that this class saves dates with millisecond granularity, + * which is bad for {@link RangeQuery} and {@link PrefixQuery}, as those + * queries are expanded to a BooleanQuery with a potentially large number + * of terms when searching. Thus you might want to use + * {@link DateTools} instead. * *

* Note: dates before 1970 cannot be used, and therefore cannot be - * indexed when using this class. + * indexed when using this class. See {@link DateTools} for an + * alternative without such a limitation. */ public class DateField { + private DateField() {} // make date strings long enough to last a millenium diff --git a/src/java/org/apache/lucene/document/DateTools.java b/src/java/org/apache/lucene/document/DateTools.java new file mode 100644 index 00000000000..da1ae669ed9 --- /dev/null +++ b/src/java/org/apache/lucene/document/DateTools.java @@ -0,0 +1,224 @@ +package org.apache.lucene.document; + +/** + * Copyright 2004 The Apache Software Foundation + * + * Licensed 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. + */ + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +/** + * Provides support for converting dates to strings and vice-versa. + * The strings are structured so that lexicographic sorting orders + * them by date, which makes them suitable for use as field values + * and search terms. + * + *

This class also helps you to limit the resolution of your dates. Do not + * save dates with a finer resolution than you really need, as then + * RangeQuery and PrefixQuery will require more memory and become slower. + * + *

Compared to {@link DateField} the strings generated by the methods + * in this class take slightly more space, unless your selected resolution + * is set to Resolution.DAY or lower. + */ +public class DateTools { + + private DateTools() {} + + /** + * Converts a Date to a string suitable for indexing. + * + * @param date the date to be converted + * @param resolution the desired resolution, see + * {@link #round(Date, DateTools.Resolution)} + * @return a string in format yyyyMMddHHmmssSSS or shorter, + * depeding on resolution + */ + public static String dateToString(Date date, Resolution resolution) { + return timeToString(date.getTime(), resolution); + } + + /** + * Converts a millisecond time to a string suitable for indexing. + * + * @param time the date expressed as milliseconds since January 1, 1970, 00:00:00 GMT + * @param resolution the desired resolution, see + * {@link #round(long, DateTools.Resolution)} + * @return a string in format yyyyMMddHHmmssSSS or shorter, + * depeding on resolution + */ + public static String timeToString(long time, Resolution resolution) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(round(time, resolution)); + + SimpleDateFormat sdf = new SimpleDateFormat(); + String pattern = null; + if (resolution == Resolution.YEAR) { + pattern = "yyyy"; + } else if (resolution == Resolution.MONTH) { + pattern = "yyyyMM"; + } else if (resolution == Resolution.DAY) { + pattern = "yyyyMMdd"; + } else if (resolution == Resolution.HOUR) { + pattern = "yyyyMMddHH"; + } else if (resolution == Resolution.MINUTE) { + pattern = "yyyyMMddHHmm"; + } else if (resolution == Resolution.SECOND) { + pattern = "yyyyMMddHHmmss"; + } else if (resolution == Resolution.MILLISECOND) { + pattern = "yyyyMMddHHmmssSSS"; + } else { + throw new IllegalArgumentException("unknown resolution " + resolution); + } + sdf.applyPattern(pattern); + return sdf.format(cal.getTime()); + } + + /** + * Converts a string produced by timeToString or + * dateToString back to a time, represented as the + * number of milliseconds since January 1, 1970, 00:00:00 GMT. + * + * @param dateString the date string to be converted + * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT + * @throws ParseException if dateString is not in the + * expected format + */ + public static long stringToTime(String dateString) throws ParseException { + return stringToDate(dateString).getTime(); + } + + /** + * Converts a string produced by timeToString or + * dateToString back to a time, represented as a + * Date object. + * + * @param dateString the date string to be converted + * @return the parsed time as a Date object + * @throws ParseException if dateString is not in the + * expected format + */ + public static Date stringToDate(String dateString) throws ParseException { + String pattern = null; + if (dateString.length() == 4 ) + pattern = "yyyy"; + else if (dateString.length() == 6 ) + pattern = "yyyyMM"; + else if (dateString.length() == 8 ) + pattern = "yyyyMMdd"; + else if (dateString.length() == 10 ) + pattern = "yyyyMMddHH"; + else if (dateString.length() == 12 ) + pattern = "yyyyMMddHHmm"; + else if (dateString.length() == 14 ) + pattern = "yyyyMMddHHmmss"; + else if (dateString.length() == 17 ) + pattern = "yyyyMMddHHmmssSSS"; + else + throw new ParseException("Input is not valid date string: " + dateString, 0); + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + Date date = sdf.parse(dateString); + return date; + } + + /** + * Limit a date's resolution. For example, the date 2004-09-21 13:50:11 + * will be changed to 2004-09-01 00:00:00 when using + * Resolution.MONTH. + * + * @param resolution The desired resolution of the date to be returned + * @return the date with all values more precise than resolution + * set to 0 or 1 + */ + public static Date round(Date date, Resolution resolution) { + return new Date(round(date.getTime(), resolution)); + } + + /** + * Limit a date's resolution. For example, the date 1095767411000 + * (which represents 2004-09-21 13:50:11) will be changed to + * 1093989600000 (2004-09-01 00:00:00) when using + * Resolution.MONTH. + * + * @param resolution The desired resolution of the date to be returned + * @return the date with all values more precise than resolution + * set to 0 or 1, expressed as milliseconds since January 1, 1970, 00:00:00 GMT + */ + public static long round(long time, Resolution resolution) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + if (resolution == Resolution.YEAR) { + cal.set(Calendar.MONTH, 0); + cal.set(Calendar.DAY_OF_MONTH, 1); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + } else if (resolution == Resolution.MONTH) { + cal.set(Calendar.DAY_OF_MONTH, 1); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + } else if (resolution == Resolution.DAY) { + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + } else if (resolution == Resolution.HOUR) { + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + } else if (resolution == Resolution.MINUTE) { + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + } else if (resolution == Resolution.SECOND) { + cal.set(Calendar.MILLISECOND, 0); + } else if (resolution == Resolution.MILLISECOND) { + // don't cut off anything + } else { + throw new IllegalArgumentException("unknown resolution " + resolution); + } + return cal.getTime().getTime(); + } + + public static class Resolution { + + public static final Resolution YEAR = new Resolution("year"); + public static final Resolution MONTH = new Resolution("month"); + public static final Resolution DAY = new Resolution("day"); + public static final Resolution HOUR = new Resolution("hour"); + public static final Resolution MINUTE = new Resolution("minute"); + public static final Resolution SECOND = new Resolution("second"); + public static final Resolution MILLISECOND = new Resolution("millisecond"); + + private String resolution; + + private Resolution() { + } + + private Resolution(String resolution) { + this.resolution = resolution; + } + + public String toString() { + return resolution; + } + + } + +}