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