    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.validator.routines;
    018
    019import java.text.DateFormat;
    020import java.text.Format;
    021import java.util.Calendar;
    022import java.util.Date;
    023import java.util.Locale;
    024import java.util.TimeZone;
    025
    026/**
    027 * <p><b>Date Validation</b> and Conversion routines (<code>java.util.Date</code>).</p>
    028 *
    029 * <p>This validator provides a number of methods for validating/converting
    030 *    a <code>String</code> date value to a <code>java.util.Date</code> using
    031 *    <code>java.text.DateFormat</code> to parse either:</p>
    032 *    <ul>
    033 *       <li>using the default format for the default <code>Locale</code></li>
    034 *       <li>using a specified pattern with the default <code>Locale</code></li>
    035 *       <li>using the default format for a specified <code>Locale</code></li>
    036 *       <li>using a specified pattern with a specified <code>Locale</code></li>
    037 *    </ul>
    038 *
    039 * <p>For each of the above mechanisms, conversion method (i.e the
    040 *    <code>validate</code> methods) implementations are provided which
    041 *    either use the default <code>TimeZone</code> or allow the
    042 *    <code>TimeZone</code> to be specified.</p>
    043 *
    044 * <p>Use one of the <code>isValid()</code> methods to just validate or
    045 *    one of the <code>validate()</code> methods to validate and receive a
    046 *    <i>converted</i> <code>Date</code> value.</p>
    047 *
    048 * <p>Implementations of the <code>validate()</code> method are provided
    049 *    to create <code>Date</code> objects for different <i>time zones</i>
    050 *    if the system default is not appropriate.</p>
    051 *
    052 * <p>Once a value has been successfully converted the following
    053 *    methods can be used to perform various date comparison checks:</p>
    054 *    <ul>
    055 *       <li><code>compareDates()</code> compares the day, month and
    056 *           year of two dates, returning 0, -1 or +1 indicating
    057 *           whether the first date is equal, before or after the second.</li>
    058 *       <li><code>compareWeeks()</code> compares the week and
    059 *           year of two dates, returning 0, -1 or +1 indicating
    060 *           whether the first week is equal, before or after the second.</li>
    061 *       <li><code>compareMonths()</code> compares the month and
    062 *           year of two dates, returning 0, -1 or +1 indicating
    063 *           whether the first month is equal, before or after the second.</li>
    064 *       <li><code>compareQuarters()</code> compares the quarter and
    065 *           year of two dates, returning 0, -1 or +1 indicating
    066 *           whether the first quarter is equal, before or after the second.</li>
    067 *       <li><code>compareYears()</code> compares the
    068 *           year of two dates, returning 0, -1 or +1 indicating
    069 *           whether the first year is equal, before or after the second.</li>
    070 *    </ul>
    071 *
    072 * <p>So that the same mechanism used for parsing an <i>input</i> value
    073 *    for validation can be used to format <i>output</i>, corresponding
    074 *    <code>format()</code> methods are also provided. That is you can
    075 *    format either:</p>
    076 *    <ul>
    077 *       <li>using a specified pattern</li>
    078 *       <li>using the format for a specified <code>Locale</code></li>
    079 *       <li>using the format for the <i>default</i> <code>Locale</code></li>
    080 *    </ul>
    081 *
    082 * @version $Revision$
    083 * @since Validator 1.3.0
    084 */
    085public class DateValidator extends AbstractCalendarValidator {
    086
    087    private static final long serialVersionUID = -3966328400469953190L;
    088
    089    private static final DateValidator VALIDATOR = new DateValidator();
    090
    091    /**
    092     * Return a singleton instance of this validator.
    093     * @return A singleton instance of the DateValidator.
    094     */
    095    public static DateValidator getInstance() {
    096        return VALIDATOR;
    097    }
    098
    099    /**
    100     * Construct a <i>strict</i> instance with <i>short</i>
    101     * date style.
    102     */
    103    public DateValidator() {
    104        this(true, DateFormat.SHORT);
    105    }
    106
    107    /**
    108     * Construct an instance with the specified <i>strict</i>
    109     * and <i>date style</i> parameters.
    110     *
    111     * @param strict <code>true</code> if strict
    112     *        <code>Format</code> parsing should be used.
    113     * @param dateStyle the date style to use for Locale validation.
    114     */
    115    public DateValidator(boolean strict, int dateStyle) {
    116        super(strict, dateStyle, -1);
    117    }
    118
    119    /**
    120     * <p>Validate/convert a <code>Date</code> using the default
    121     *    <code>Locale</code> and <code>TimeZone</code>.
    122     *
    123     * @param value The value validation is being performed on.
    124     * @return The parsed <code>Date</code> if valid or <code>null</code>
    125     *  if invalid.
    126     */
    127    public Date validate(String value) {
    128        return (Date)parse(value, (String)null, (Locale)null, (TimeZone)null);
    129    }
    130
    131    /**
    132     * <p>Validate/convert a <code>Date</code> using the specified
    133     *    <code>TimeZone</code> and default <code>Locale</code>.
    134     *
    135     * @param value The value validation is being performed on.
    136     * @param timeZone The Time Zone used to parse the date, system default if null.
    137     * @return The parsed <code>Date</code> if valid or <code>null</code> if invalid.
    138     */
    139    public Date validate(String value, TimeZone timeZone) {
    140        return (Date)parse(value, (String)null, (Locale)null, timeZone);
    141    }
    142
    143    /**
    144     * <p>Validate/convert a <code>Date</code> using the specified
    145     *    <i>pattern</i> and default <code>TimeZone</code>.
    146     *
    147     * @param value The value validation is being performed on.
    148     * @param pattern The pattern used to validate the value against, or the
    149     *        default for the <code>Locale</code> if <code>null</code>.
    150     * @return The parsed <code>Date</code> if valid or <code>null</code> if invalid.
    151     */
    152    public Date validate(String value, String pattern) {
    153        return (Date)parse(value, pattern, (Locale)null, (TimeZone)null);
    154    }
    155
    156    /**
    157     * <p>Validate/convert a <code>Date</code> using the specified
    158     *    <i>pattern</i> and <code>TimeZone</code>.
    159     *
    160     * @param value The value validation is being performed on.
    161     * @param pattern The pattern used to validate the value against, or the
    162     *        default for the <code>Locale</code> if <code>null</code>.
    163     * @param timeZone The Time Zone used to parse the date, system default if null.
    164     * @return The parsed <code>Date</code> if valid or <code>null</code> if invalid.
    165     */
    166    public Date validate(String value, String pattern, TimeZone timeZone) {
    167        return (Date)parse(value, pattern, (Locale)null, timeZone);
    168    }
    169
    170    /**
    171     * <p>Validate/convert a <code>Date</code> using the specified
    172     *    <code>Locale</code> and default <code>TimeZone</code>.
    173     *
    174     * @param value The value validation is being performed on.
    175     * @param locale The locale to use for the date format, system default if null.
    176     * @return The parsed <code>Date</code> if valid or <code>null</code> if invalid.
    177     */
    178    public Date validate(String value, Locale locale) {
    179        return (Date)parse(value, (String)null, locale, (TimeZone)null);
    180    }
    181
    182    /**
    183     * <p>Validate/convert a <code>Date</code> using the specified
    184     *    <code>Locale</code> and <code>TimeZone</code>.
    185     *
    186     * @param value The value validation is being performed on.
    187     * @param locale The locale to use for the date format, system default if null.
    188     * @param timeZone The Time Zone used to parse the date, system default if null.
    189     * @return The parsed <code>Date</code> if valid or <code>null</code> if invalid.
    190     */
    191    public Date validate(String value, Locale locale, TimeZone timeZone) {
    192        return (Date)parse(value, (String)null, locale, timeZone);
    193    }
    194
    195    /**
    196     * <p>Validate/convert a <code>Date</code> using the specified pattern
    197     *    and <code>Locale</code> and the default <code>TimeZone</code>.
    198     *
    199     * @param value The value validation is being performed on.
    200     * @param pattern The pattern used to validate the value against, or the
    201     *        default for the <code>Locale</code> if <code>null</code>.
    202     * @param locale The locale to use for the date format, system default if null.
    203     * @return The parsed <code>Date</code> if valid or <code>null</code> if invalid.
    204     */
    205    public Date validate(String value, String pattern, Locale locale) {
    206        return (Date)parse(value, pattern, locale, (TimeZone)null);
    207    }
    208
    209    /**
    210     * <p>Validate/convert a <code>Date</code> using the specified
    211     *    pattern, and <code>Locale</code> and <code>TimeZone</code>.
    212     *
    213     * @param value The value validation is being performed on.
    214     * @param pattern The pattern used to validate the value against, or the
    215     *        default for the <code>Locale</code> if <code>null</code>.
    216     * @param locale The locale to use for the date format, system default if null.
    217     * @param timeZone The Time Zone used to parse the date, system default if null.
    218     * @return The parsed <code>Date</code> if valid or <code>null</code> if invalid.
    219     */
    220    public Date validate(String value, String pattern, Locale locale, TimeZone timeZone) {
    221        return (Date)parse(value, pattern, locale, timeZone);
    222    }
    223
    224    /**
    225     * <p>Compare Dates (day, month and year - not time).</p>
    226     *
    227     * @param value The <code>Calendar</code> value to check.
    228     * @param compare The <code>Calendar</code> to compare the value to.
    229     * @param timeZone The Time Zone used to compare the dates, system default if null.
    230     * @return Zero if the dates are equal, -1 if first
    231     * date is less than the seconds and +1 if the first
    232     * date is greater than.
    233     */
    234    public int compareDates(Date value, Date compare, TimeZone timeZone) {
    235        Calendar calendarValue   = getCalendar(value, timeZone);
    236        Calendar calendarCompare = getCalendar(compare, timeZone);
    237        return compare(calendarValue, calendarCompare, Calendar.DATE);
    238    }
    239
    240    /**
    241     * <p>Compare Weeks (week and year).</p>
    242     *
    243     * @param value The <code>Date</code> value to check.
    244     * @param compare The <code>Date</code> to compare the value to.
    245     * @param timeZone The Time Zone used to compare the dates, system default if null.
    246     * @return Zero if the weeks are equal, -1 if first
    247     * parameter's week is less than the seconds and +1 if the first
    248     * parameter's week is greater than.
    249     */
    250    public int compareWeeks(Date value, Date compare, TimeZone timeZone) {
    251        Calendar calendarValue   = getCalendar(value, timeZone);
    252        Calendar calendarCompare = getCalendar(compare, timeZone);
    253        return compare(calendarValue, calendarCompare, Calendar.WEEK_OF_YEAR);
    254    }
    255
    256    /**
    257     * <p>Compare Months (month and year).</p>
    258     *
    259     * @param value The <code>Date</code> value to check.
    260     * @param compare The <code>Date</code> to compare the value to.
    261     * @param timeZone The Time Zone used to compare the dates, system default if null.
    262     * @return Zero if the months are equal, -1 if first
    263     * parameter's month is less than the seconds and +1 if the first
    264     * parameter's month is greater than.
    265     */
    266    public int compareMonths(Date value, Date compare, TimeZone timeZone) {
    267        Calendar calendarValue   = getCalendar(value, timeZone);
    268        Calendar calendarCompare = getCalendar(compare, timeZone);
    269        return compare(calendarValue, calendarCompare, Calendar.MONTH);
    270    }
    271
    272    /**
    273     * <p>Compare Quarters (quarter and year).</p>
    274     *
    275     * @param value The <code>Date</code> value to check.
    276     * @param compare The <code>Date</code> to compare the value to.
    277     * @param timeZone The Time Zone used to compare the dates, system default if null.
    278     * @return Zero if the months are equal, -1 if first
    279     * parameter's quarter is less than the seconds and +1 if the first
    280     * parameter's quarter is greater than.
    281     */
    282    public int compareQuarters(Date value, Date compare, TimeZone timeZone) {
    283        return compareQuarters(value, compare, timeZone, 1);
    284    }
    285
    286    /**
    287     * <p>Compare Quarters (quarter and year).</p>
    288     *
    289     * @param value The <code>Date</code> value to check.
    290     * @param compare The <code>Date</code> to compare the value to.
    291     * @param timeZone The Time Zone used to compare the dates, system default if null.
    292     * @param monthOfFirstQuarter The  month that the first quarter starts.
    293     * @return Zero if the quarters are equal, -1 if first
    294     * parameter's quarter is less than the seconds and +1 if the first
    295     * parameter's quarter is greater than.
    296     */
    297    public int compareQuarters(Date value, Date compare, TimeZone timeZone, int monthOfFirstQuarter) {
    298        Calendar calendarValue   = getCalendar(value, timeZone);
    299        Calendar calendarCompare = getCalendar(compare, timeZone);
    300        return super.compareQuarters(calendarValue, calendarCompare, monthOfFirstQuarter);
    301    }
    302
    303    /**
    304     * <p>Compare Years.</p>
    305     *
    306     * @param value The <code>Date</code> value to check.
    307     * @param compare The <code>Date</code> to compare the value to.
    308     * @param timeZone The Time Zone used to compare the dates, system default if null.
    309     * @return Zero if the years are equal, -1 if first
    310     * parameter's year is less than the seconds and +1 if the first
    311     * parameter's year is greater than.
    312     */
    313    public int compareYears(Date value, Date compare, TimeZone timeZone) {
    314        Calendar calendarValue   = getCalendar(value, timeZone);
    315        Calendar calendarCompare = getCalendar(compare, timeZone);
    316        return compare(calendarValue, calendarCompare, Calendar.YEAR);
    317    }
    318
    319    /**
    320     * <p>Returns the parsed <code>Date</code> unchanged.</p>
    321     *
    322     * @param value The parsed <code>Date</code> object created.
    323     * @param formatter The Format used to parse the value with.
    324     * @return The parsed value converted to a <code>Calendar</code>.
    325     */
    326    @Override
    327    protected Object processParsedValue(Object value, Format formatter) {
    328        return value;
    329    }
    330
    331    /**
    332     * <p>Convert a <code>Date</code> to a <code>Calendar</code>.</p>
    333     *
    334     * @param value The date value to be converted.
    335     * @return The converted <code>Calendar</code>.
    336     */
    337    private Calendar getCalendar(Date value, TimeZone timeZone) {
    338
    339        Calendar calendar = null;
    340        if (timeZone != null) {
    341            calendar = Calendar.getInstance(timeZone);
    342        } else {
    343            calendar = Calendar.getInstance();
    344        }
    345        calendar.setTime(value);
    346        return calendar;
    347
    348    }
    349
    350}