1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.validator.routines; 18 19 import java.text.DateFormat; 20 import java.text.Format; 21 import java.util.Calendar; 22 import java.util.Date; 23 import java.util.Locale; 24 import java.util.TimeZone; 25 26 /** 27 * <p><strong>Date Validation</strong> and Conversion routines ({@code java.util.Date}).</p> 28 * 29 * <p>This validator provides a number of methods for validating/converting 30 * a {@link String} date value to a {@code java.util.Date} using 31 * {@link DateFormat} to parse either:</p> 32 * <ul> 33 * <li>using the default format for the default {@link Locale}</li> 34 * <li>using a specified pattern with the default {@link Locale}</li> 35 * <li>using the default format for a specified {@link Locale}</li> 36 * <li>using a specified pattern with a specified {@link Locale}</li> 37 * </ul> 38 * 39 * <p>For each of the above mechanisms, conversion method (that is, the 40 * {@code validate} methods) implementations are provided which 41 * either use the default {@code TimeZone} or allow the 42 * {@code TimeZone} to be specified.</p> 43 * 44 * <p>Use one of the {@code isValid()} methods to just validate or 45 * one of the {@code validate()} methods to validate and receive a 46 * <em>converted</em> {@code Date} value.</p> 47 * 48 * <p>Implementations of the {@code validate()} method are provided 49 * to create {@code Date} objects for different <em>time zones</em> 50 * if the system default is not appropriate.</p> 51 * 52 * <p>Once a value has been successfully converted the following 53 * methods can be used to perform various date comparison checks:</p> 54 * <ul> 55 * <li>{@code compareDates()} compares the day, month and 56 * year of two dates, returning 0, -1 or +1 indicating 57 * whether the first date is equal, before or after the second.</li> 58 * <li>{@code compareWeeks()} compares the week and 59 * year of two dates, returning 0, -1 or +1 indicating 60 * whether the first week is equal, before or after the second.</li> 61 * <li>{@code compareMonths()} compares the month and 62 * year of two dates, returning 0, -1 or +1 indicating 63 * whether the first month is equal, before or after the second.</li> 64 * <li>{@code compareQuarters()} compares the quarter and 65 * year of two dates, returning 0, -1 or +1 indicating 66 * whether the first quarter is equal, before or after the second.</li> 67 * <li>{@code compareYears()} compares the 68 * year of two dates, returning 0, -1 or +1 indicating 69 * whether the first year is equal, before or after the second.</li> 70 * </ul> 71 * 72 * <p>So that the same mechanism used for parsing an <em>input</em> value 73 * for validation can be used to format <em>output</em>, corresponding 74 * {@code format()} methods are also provided. That is you can 75 * format either:</p> 76 * <ul> 77 * <li>using a specified pattern</li> 78 * <li>using the format for a specified {@link Locale}</li> 79 * <li>using the format for the <em>default</em> {@link Locale}</li> 80 * </ul> 81 * 82 * @since 1.3.0 83 */ 84 public class DateValidator extends AbstractCalendarValidator { 85 86 private static final long serialVersionUID = -3966328400469953190L; 87 88 private static final DateValidator VALIDATOR = new DateValidator(); 89 90 /** 91 * Gets the singleton instance of this validator. 92 * @return A singleton instance of the DateValidator. 93 */ 94 public static DateValidator getInstance() { 95 return VALIDATOR; 96 } 97 98 /** 99 * Constructs a <em>strict</em> instance with <em>short</em> 100 * date style. 101 */ 102 public DateValidator() { 103 this(true, DateFormat.SHORT); 104 } 105 106 /** 107 * Constructs an instance with the specified <em>strict</em> 108 * and <em>date style</em> parameters. 109 * 110 * @param strict {@code true} if strict 111 * {@code Format} 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 {@link Calendar} value to check. 122 * @param compare The {@link Calendar} 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} value to check. 138 * @param compare The {@code Date} 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} value to check. 154 * @param compare The {@code Date} 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} value to check. 168 * @param compare The {@code Date} 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} value to check. 185 * @param compare The {@code Date} 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} value to check. 201 * @param compare The {@code Date} 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} to a {@link Calendar}.</p> 215 * 216 * @param value The date value to be converted. 217 * @return The converted {@link Calendar}. 218 */ 219 private Calendar getCalendar(final Date value, final TimeZone timeZone) { 220 final 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} unchanged.</p> 233 * 234 * @param value The parsed {@code Date} object created. 235 * @param formatter The Format used to parse the value with. 236 * @return The parsed value converted to a {@link Calendar}. 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} using the default 245 * {@link Locale} and {@code TimeZone}. 246 * 247 * @param value The value validation is being performed on. 248 * @return The parsed {@code Date} 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} using the specified 257 * {@link Locale} and default {@code TimeZone}. 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} 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} using the specified 269 * {@link Locale} and {@code TimeZone}. 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} 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} using the specified 282 * <em>pattern</em> and default {@code TimeZone}. 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 {@link Locale} if {@code null}. 287 * @return The parsed {@code Date} 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} using the specified pattern 295 * and {@link Locale} and the default {@code TimeZone}. 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 {@link Locale} if {@code null}. 300 * @param locale The locale to use for the date format, system default if null. 301 * @return The parsed {@code Date} 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} using the specified 309 * pattern, and {@link Locale} and {@code TimeZone}. 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 {@link Locale} 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} 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} using the specified 324 * <em>pattern</em> and {@code TimeZone}. 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 {@link Locale} if {@code null}. 329 * @param timeZone The Time Zone used to parse the date, system default if null. 330 * @return The parsed {@code Date} 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} using the specified 338 * {@code TimeZone} and default {@link Locale}. 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} 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 }