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