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 successfully 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, returning 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, returning 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, returning 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, returning 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, returning 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 * @since 1.3.0
086 */
087public class CalendarValidator extends AbstractCalendarValidator {
088
089    private static final long serialVersionUID = 9109652318762134167L;
090
091    private static final CalendarValidator VALIDATOR = new CalendarValidator();
092
093    /**
094     * <p>Adjusts a Calendar's value to a different TimeZone.</p>
095     *
096     * @param value The value to adjust.
097     * @param timeZone The new time zone to use to adjust the Calendar to.
098     */
099    public static void adjustToTimeZone(final Calendar value, final TimeZone timeZone) {
100        if (value.getTimeZone().hasSameRules(timeZone)) {
101            value.setTimeZone(timeZone);
102        } else {
103            final int year = value.get(Calendar.YEAR);
104            final int month = value.get(Calendar.MONTH);
105            final int date = value.get(Calendar.DATE);
106            final int hour = value.get(Calendar.HOUR_OF_DAY);
107            final int minute = value.get(Calendar.MINUTE);
108            value.setTimeZone(timeZone);
109            value.set(year, month, date, hour, minute);
110        }
111    }
112
113    /**
114     * Return a singleton instance of this validator.
115     * @return A singleton instance of the CalendarValidator.
116     */
117    public static CalendarValidator getInstance() {
118        return VALIDATOR;
119    }
120
121    /**
122     * Constructs a <i>strict</i> instance with <i>short</i>
123     * date style.
124     */
125    public CalendarValidator() {
126        this(true, DateFormat.SHORT);
127    }
128
129    /**
130     * Constructs an instance with the specified <i>strict</i>
131     * and <i>date style</i> parameters.
132     *
133     * @param strict {@code true} if strict
134     *        <code>Format</code> parsing should be used.
135     * @param dateStyle the date style to use for Locale validation.
136     */
137    public CalendarValidator(final boolean strict, final int dateStyle) {
138        super(strict, dateStyle, -1);
139    }
140
141    /**
142     * <p>Compare Dates (day, month and year - not time).</p>
143     *
144     * @param value The <code>Calendar</code> value to check.
145     * @param compare The <code>Calendar</code> to compare the value to.
146     * @return Zero if the dates are equal, -1 if first
147     * date is less than the seconds and +1 if the first
148     * date is greater than.
149     */
150    public int compareDates(final Calendar value, final Calendar compare) {
151        return compare(value, compare, Calendar.DATE);
152    }
153
154    /**
155     * <p>Compare Months (month and year).</p>
156     *
157     * @param value The <code>Calendar</code> value to check.
158     * @param compare The <code>Calendar</code> to compare the value to.
159     * @return Zero if the months are equal, -1 if first
160     * parameter's month is less than the seconds and +1 if the first
161     * parameter's month is greater than.
162     */
163    public int compareMonths(final Calendar value, final Calendar compare) {
164        return compare(value, compare, Calendar.MONTH);
165    }
166
167    /**
168     * <p>Compare Quarters (quarter and year).</p>
169     *
170     * @param value The <code>Calendar</code> value to check.
171     * @param compare The <code>Calendar</code> to check the value against.
172     * @return Zero if the quarters are equal, -1 if first
173     * parameter's quarter is less than the seconds and +1 if the first
174     * parameter's quarter is greater than.
175     */
176    public int compareQuarters(final Calendar value, final Calendar compare) {
177        return compareQuarters(value, compare, 1);
178    }
179
180    /**
181     * <p>Compare Quarters (quarter and year).</p>
182     *
183     * @param value The <code>Calendar</code> value to check.
184     * @param compare The <code>Calendar</code> to compare the value to.
185     * @param monthOfFirstQuarter The  month that the first quarter starts.
186     * @return Zero if the quarters are equal, -1 if first
187     * parameter's quarter is less than the seconds and +1 if the first
188     * parameter's quarter is greater than.
189     */
190    @Override
191    public int compareQuarters(final Calendar value, final Calendar compare, final int monthOfFirstQuarter) {
192        return super.compareQuarters(value, compare, monthOfFirstQuarter);
193    }
194
195    /**
196     * <p>Compare Weeks (week and year).</p>
197     *
198     * @param value The <code>Calendar</code> value to check.
199     * @param compare The <code>Calendar</code> to compare the value to.
200     * @return Zero if the weeks are equal, -1 if first
201     * parameter's week is less than the seconds and +1 if the first
202     * parameter's week is greater than.
203     */
204    public int compareWeeks(final Calendar value, final Calendar compare) {
205        return compare(value, compare, Calendar.WEEK_OF_YEAR);
206    }
207
208    /**
209     * <p>Compare Years.</p>
210     *
211     * @param value The <code>Calendar</code> value to check.
212     * @param compare The <code>Calendar</code> to compare the value to.
213     * @return Zero if the years are equal, -1 if first
214     * parameter's year is less than the seconds and +1 if the first
215     * parameter's year is greater than.
216     */
217    public int compareYears(final Calendar value, final Calendar compare) {
218        return compare(value, compare, Calendar.YEAR);
219    }
220
221    /**
222     * <p>Convert the parsed <code>Date</code> to a <code>Calendar</code>.</p>
223     *
224     * @param value The parsed <code>Date</code> object created.
225     * @param formatter The Format used to parse the value with.
226     * @return The parsed value converted to a <code>Calendar</code>.
227     */
228    @Override
229    protected Object processParsedValue(final Object value, final Format formatter) {
230        return ((DateFormat)formatter).getCalendar();
231    }
232
233    /**
234     * <p>Validate/convert a <code>Calendar</code> using the default
235     *    <code>Locale</code> and <code>TimeZone</code>.
236     *
237     * @param value The value validation is being performed on.
238     * @return The parsed <code>Calendar</code> if valid or <code>null</code>
239     *  if invalid.
240     */
241    public Calendar validate(final String value) {
242        return (Calendar)parse(value, (String)null, (Locale)null, (TimeZone)null);
243    }
244
245    /**
246     * <p>Validate/convert a <code>Calendar</code> using the specified
247     *    <code>Locale</code> and default <code>TimeZone</code>.
248     *
249     * @param value The value validation is being performed on.
250     * @param locale The locale to use for the date format, system default if null.
251     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
252     */
253    public Calendar validate(final String value, final Locale locale) {
254        return (Calendar)parse(value, (String)null, locale, (TimeZone)null);
255    }
256
257    /**
258     * <p>Validate/convert a <code>Calendar</code> using the specified
259     *    <code>Locale</code> and <code>TimeZone</code>.
260     *
261     * @param value The value validation is being performed on.
262     * @param locale The locale to use for the date format, system default if null.
263     * @param timeZone The Time Zone used to parse the date, system default if null.
264     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
265     */
266    public Calendar validate(final String value, final Locale locale, final TimeZone timeZone) {
267        return (Calendar)parse(value, (String)null, locale, timeZone);
268    }
269
270    /**
271     * <p>Validate/convert a <code>Calendar</code> using the specified
272     *    <i>pattern</i> and default <code>TimeZone</code>.
273     *
274     * @param value The value validation is being performed on.
275     * @param pattern The pattern used to validate the value against.
276     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
277     */
278    public Calendar validate(final String value, final String pattern) {
279        return (Calendar)parse(value, pattern, (Locale)null, (TimeZone)null);
280    }
281
282    /**
283     * <p>Validate/convert a <code>Calendar</code> using the specified pattern
284     *    and <code>Locale</code> and the default <code>TimeZone</code>.
285     *
286     * @param value The value validation is being performed on.
287     * @param pattern The pattern used to validate the value against, or the
288     *        default for the <code>Locale</code> if <code>null</code>.
289     * @param locale The locale to use for the date format, system default if null.
290     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
291     */
292    public Calendar validate(final String value, final String pattern, final Locale locale) {
293        return (Calendar)parse(value, pattern, locale, (TimeZone)null);
294    }
295
296    /**
297     * <p>Validate/convert a <code>Calendar</code> using the specified
298     *    pattern, and <code>Locale</code> and <code>TimeZone</code>.
299     *
300     * @param value The value validation is being performed on.
301     * @param pattern The pattern used to validate the value against, or the
302     *        default for the <code>Locale</code> if <code>null</code>.
303     * @param locale The locale to use for the date format, system default if null.
304     * @param timeZone The Time Zone used to parse the date, system default if null.
305     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
306     */
307    public Calendar validate(final String value, final String pattern, final Locale locale, final TimeZone timeZone) {
308        return (Calendar)parse(value, pattern, locale, timeZone);
309    }
310
311    /**
312     * <p>Validate/convert a <code>Calendar</code> using the specified
313     *    <i>pattern</i> and <code>TimeZone</code>.
314     *
315     * @param value The value validation is being performed on.
316     * @param pattern The pattern used to validate the value against.
317     * @param timeZone The Time Zone used to parse the date, system default if null.
318     * @return The parsed <code>Calendar</code> if valid or <code>null</code> if invalid.
319     */
320    public Calendar validate(final String value, final String pattern, final TimeZone timeZone) {
321        return (Calendar)parse(value, pattern, (Locale)null, timeZone);
322    }
323
324    /**
325     * <p>Validate/convert a <code>Calendar</code> using the specified
326     *    <code>TimeZone</code> and default <code>Locale</code>.
327     *
328     * @param value The value validation is being performed on.
329     * @param timeZone The Time Zone used to parse the date, system default if null.
330     * @return The parsed <code>Calendar</code> if valid or <code>null</code>
331     *  if invalid.
332     */
333    public Calendar validate(final String value, final TimeZone timeZone) {
334        return (Calendar)parse(value, (String)null, (Locale)null, timeZone);
335    }
336
337}