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.Locale;
023import java.util.TimeZone;
024
025/**
026 * <p><b>Calendar Validation</b> and Conversion routines (<code>java.util.Calendar</code>).</p>
027 *
028 * <p>This validator provides a number of methods for validating/converting
029 *    a <code>String</code> date value to a <code>java.util.Calendar</code> using
030 *    <code>java.text.DateFormat</code> to parse either:</p>
031 *    <ul>
032 *       <li>using the default format for the default <code>Locale</code></li>
033 *       <li>using a specified pattern with the default <code>Locale</code></li>
034 *       <li>using the default format for a specified <code>Locale</code></li>
035 *       <li>using a specified pattern with a specified <code>Locale</code></li>
036 *    </ul>
037 *
038 * <p>For each of the above mechanisms, conversion method (i.e the
039 *    <code>validate</code> methods) implementations are provided which
040 *    either use the default <code>TimeZone</code> or allow the
041 *    <code>TimeZone</code> to be specified.</p>
042 *
043 * <p>Use one of the <code>isValid()</code> methods to just validate or
044 *    one of the <code>validate()</code> methods to validate and receive a
045 *    <i>converted</i> <code>Calendar</code> value.</p>
046 *
047 * <p>Implementations of the <code>validate()</code> method are provided
048 *    to create <code>Calendar</code> objects for different <i>time zones</i>
049 *    if the system default is not appropriate.</p>
050 *
051 * <p>Alternatively the CalendarValidator's <code>adjustToTimeZone()</code> method
052 *    can be used to adjust the <code>TimeZone</code> of the <code>Calendar</code>
053 *    object afterwards.</p>
054 *
055 * <p>Once a value has been sucessfully converted the following
056 *    methods can be used to perform various date comparison checks:</p>
057 *    <ul>
058 *       <li><code>compareDates()</code> compares the day, month and
059 *           year of two calendars, returing 0, -1 or +1 indicating
060 *           whether the first date is equal, before or after the second.</li>
061 *       <li><code>compareWeeks()</code> compares the week and
062 *           year of two calendars, returing 0, -1 or +1 indicating
063 *           whether the first week is equal, before or after the second.</li>
064 *       <li><code>compareMonths()</code> compares the month and
065 *           year of two calendars, returing 0, -1 or +1 indicating
066 *           whether the first month is equal, before or after the second.</li>
067 *       <li><code>compareQuarters()</code> compares the quarter and
068 *           year of two calendars, returing 0, -1 or +1 indicating
069 *           whether the first quarter is equal, before or after the second.</li>
070 *       <li><code>compareYears()</code> compares the
071 *           year of two calendars, returing 0, -1 or +1 indicating
072 *           whether the first year is equal, before or after the second.</li>
073 *    </ul>
074 *
075 * <p>So that the same mechanism used for parsing an <i>input</i> value
076 *    for validation can be used to format <i>output</i>, corresponding
077 *    <code>format()</code> methods are also provided. That is you can
078 *    format either:</p>
079 *    <ul>
080 *       <li>using a specified pattern</li>
081 *       <li>using the format for a specified <code>Locale</code></li>
082 *       <li>using the format for the <i>default</i> <code>Locale</code></li>
083 *    </ul>
084 *
085 * @version $Revision: 1227719 $ $Date: 2012-01-05 18:45:51 +0100 (Do, 05 Jan 2012) $
086 * @since Validator 1.3.0
087 */
088public class CalendarValidator extends AbstractCalendarValidator {
089
090    private static final long serialVersionUID = 9109652318762134167L;
091
092    private static final CalendarValidator VALIDATOR = new CalendarValidator();
093
094    /**
095     * Return a singleton instance of this validator.
096     * @return A singleton instance of the CalendarValidator.
097     */
098    public static CalendarValidator getInstance() {
099        return VALIDATOR;
100    }
101
102    /**
103     * Construct a <i>strict</i> instance with <i>short</i>
104     * date style.
105     */
106    public CalendarValidator() {
107        this(true, DateFormat.SHORT);
108    }
109
110    /**
111     * Construct an instance with the specified <i>strict</i>
112     * and <i>date style</i> parameters.
113     *
114     * @param strict <code>true</code> if strict
115     *        <code>Format</code> parsing should be used.
116     * @param dateStyle the date style to use for Locale validation.
117     */
118    public CalendarValidator(boolean strict, int dateStyle) {
119        super(strict, dateStyle, -1);
120    }
121
122    /**
123     * <p>Validate/convert a <code>Calendar</code> using the default
124     *    <code>Locale</code> and <code>TimeZone</code>.
125     *
126     * @param value The value validation is being performed on.
127     * @return The parsed <code>Calendar</code> if valid or <code>null</code>
128     *  if invalid.
129     */
130    public Calendar validate(String value) {
131        return (Calendar)parse(value, (String)null, (Locale)null, (TimeZone)null);
132    }
133
134    /**
135     * <p>Validate/convert a <code>Calendar</code> using the specified
136     *    <code>TimeZone</code> and default <code>Locale</code>.
137     *
138     * @param value The value validation is being performed on.
139     * @param timeZone The Time Zone used to parse the date, system default if null.
140     * @return The parsed <code>Calendar</code> if valid or <code>null</code>
141     *  if invalid.
142     */
143    public Calendar validate(String value, TimeZone timeZone) {
144        return (Calendar)parse(value, (String)null, (Locale)null, timeZone);
145    }
146
147    /**
148     * <p>Validate/convert a <code>Calendar</code> using the specified
149     *    <i>pattern</i> and default <code>TimeZone</code>.
150     *
151     * @param value The value validation is being performed on.
152     * @param pattern The pattern used to validate the value against.
153     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
154     */
155    public Calendar validate(String value, String pattern) {
156        return (Calendar)parse(value, pattern, (Locale)null, (TimeZone)null);
157    }
158
159    /**
160     * <p>Validate/convert a <code>Calendar</code> using the specified
161     *    <i>pattern</i> and <code>TimeZone</code>.
162     *
163     * @param value The value validation is being performed on.
164     * @param pattern The pattern used to validate the value against.
165     * @param timeZone The Time Zone used to parse the date, system default if null.
166     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
167     */
168    public Calendar validate(String value, String pattern, TimeZone timeZone) {
169        return (Calendar)parse(value, pattern, (Locale)null, timeZone);
170    }
171
172    /**
173     * <p>Validate/convert a <code>Calendar</code> using the specified
174     *    <code>Locale</code> and default <code>TimeZone</code>.
175     *
176     * @param value The value validation is being performed on.
177     * @param locale The locale to use for the date format, system default if null.
178     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
179     */
180    public Calendar validate(String value, Locale locale) {
181        return (Calendar)parse(value, (String)null, locale, (TimeZone)null);
182    }
183
184    /**
185     * <p>Validate/convert a <code>Calendar</code> using the specified
186     *    <code>Locale</code> and <code>TimeZone</code>.
187     *
188     * @param value The value validation is being performed on.
189     * @param locale The locale to use for the date format, system default if null.
190     * @param timeZone The Time Zone used to parse the date, system default if null.
191     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
192     */
193    public Calendar validate(String value, Locale locale, TimeZone timeZone) {
194        return (Calendar)parse(value, (String)null, locale, timeZone);
195    }
196
197    /**
198     * <p>Validate/convert a <code>Calendar</code> using the specified pattern
199     *    and <code>Locale</code> and the default <code>TimeZone</code>.
200     *
201     * @param value The value validation is being performed on.
202     * @param pattern The pattern used to validate the value against, or the
203     *        default for the <code>Locale</code> if <code>null</code>.
204     * @param locale The locale to use for the date format, system default if null.
205     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
206     */
207    public Calendar validate(String value, String pattern, Locale locale) {
208        return (Calendar)parse(value, pattern, locale, (TimeZone)null);
209    }
210
211    /**
212     * <p>Validate/convert a <code>Calendar</code> using the specified
213     *    pattern, and <code>Locale</code> and <code>TimeZone</code>.
214     *
215     * @param value The value validation is being performed on.
216     * @param pattern The pattern used to validate the value against, or the
217     *        default for the <code>Locale</code> if <code>null</code>.
218     * @param locale The locale to use for the date format, system default if null.
219     * @param timeZone The Time Zone used to parse the date, system default if null.
220     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
221     */
222    public Calendar validate(String value, String pattern, Locale locale, TimeZone timeZone) {
223        return (Calendar)parse(value, pattern, locale, timeZone);
224    }
225
226    /**
227     * <p>Adjusts a Calendar's value to a different TimeZone.</p>
228     *
229     * @param value The value to adjust.
230     * @param timeZone The new time zone to use to adjust the Calendar to.
231     */
232    public static void adjustToTimeZone(Calendar value, TimeZone timeZone) {
233        if (value.getTimeZone().hasSameRules(timeZone)) {
234            value.setTimeZone(timeZone);
235        } else {
236            int year   = value.get(Calendar.YEAR);
237            int month  = value.get(Calendar.MONTH);
238            int date   = value.get(Calendar.DATE);
239            int hour   = value.get(Calendar.HOUR_OF_DAY);
240            int minute = value.get(Calendar.MINUTE);
241            value.setTimeZone(timeZone);
242            value.set(year, month, date, hour, minute);
243        }
244    }
245
246    /**
247     * <p>Compare Dates (day, month and year - not time).</p>
248     *
249     * @param value The <code>Calendar</code> value to check.
250     * @param compare The <code>Calendar</code> to compare the value to.
251     * @return Zero if the dates are equal, -1 if first
252     * date is less than the seconds and +1 if the first
253     * date is greater than.
254     */
255    public int compareDates(Calendar value, Calendar compare) {
256        return compare(value, compare, Calendar.DATE);
257    }
258
259    /**
260     * <p>Compare Weeks (week and year).</p>
261     *
262     * @param value The <code>Calendar</code> value to check.
263     * @param compare The <code>Calendar</code> to compare the value to.
264     * @return Zero if the weeks are equal, -1 if first
265     * parameter's week is less than the seconds and +1 if the first
266     * parameter's week is greater than.
267     */
268    public int compareWeeks(Calendar value, Calendar compare) {
269        return compare(value, compare, Calendar.WEEK_OF_YEAR);
270    }
271
272    /**
273     * <p>Compare Months (month and year).</p>
274     *
275     * @param value The <code>Calendar</code> value to check.
276     * @param compare The <code>Calendar</code> to compare the value to.
277     * @return Zero if the months are equal, -1 if first
278     * parameter's month is less than the seconds and +1 if the first
279     * parameter's month is greater than.
280     */
281    public int compareMonths(Calendar value, Calendar compare) {
282        return compare(value, compare, Calendar.MONTH);
283    }
284
285    /**
286     * <p>Compare Quarters (quarter and year).</p>
287     *
288     * @param value The <code>Calendar</code> value to check.
289     * @param compare The <code>Calendar</code> to check the value against.
290     * @return Zero if the quarters are equal, -1 if first
291     * parameter's quarter is less than the seconds and +1 if the first
292     * parameter's quarter is greater than.
293     */
294    public int compareQuarters(Calendar value, Calendar compare) {
295        return compareQuarters(value, compare, 1);
296    }
297
298    /**
299     * <p>Compare Quarters (quarter and year).</p>
300     *
301     * @param value The <code>Calendar</code> value to check.
302     * @param compare The <code>Calendar</code> to compare the value to.
303     * @param monthOfFirstQuarter The  month that the first quarter starts.
304     * @return Zero if the quarters are equal, -1 if first
305     * parameter's quarter is less than the seconds and +1 if the first
306     * parameter's quarter is greater than.
307     */
308    public int compareQuarters(Calendar value, Calendar compare, int monthOfFirstQuarter) {
309        return super.compareQuarters(value, compare, monthOfFirstQuarter);
310    }
311
312    /**
313     * <p>Compare Years.</p>
314     *
315     * @param value The <code>Calendar</code> value to check.
316     * @param compare The <code>Calendar</code> to compare the value to.
317     * @return Zero if the years are equal, -1 if first
318     * parameter's year is less than the seconds and +1 if the first
319     * parameter's year is greater than.
320     */
321    public int compareYears(Calendar value, Calendar compare) {
322        return compare(value, compare, Calendar.YEAR);
323    }
324
325    /**
326     * <p>Convert the parsed <code>Date</code> to a <code>Calendar</code>.</p>
327     *
328     * @param value The parsed <code>Date</code> object created.
329     * @param formatter The Format used to parse the value with.
330     * @return The parsed value converted to a <code>Calendar</code>.
331     */
332    protected Object processParsedValue(Object value, Format formatter) {
333        return ((DateFormat)formatter).getCalendar();
334    }
335
336}