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