001    package org.fest.assertions.api;
002    
003    import static org.fest.util.Dates.ISO_DATE_FORMAT;
004    
005    import java.text.DateFormat;
006    import java.text.ParseException;
007    import java.util.ArrayList;
008    import java.util.Calendar;
009    import java.util.Collection;
010    import java.util.Comparator;
011    import java.util.Date;
012    import java.util.concurrent.TimeUnit;
013    
014    import org.fest.assertions.core.Assert;
015    import org.fest.assertions.internal.Dates;
016    import org.fest.assertions.internal.Failures;
017    import org.fest.util.ComparatorBasedComparisonStrategy;
018    import org.fest.util.VisibleForTesting;
019    
020    /**
021     * 
022     * Assertions for {@code Date}s.
023     * <p>
024     * To create a new instance of this class invoke <code>{@link Assertions#assertThat(Date)}</code>.
025     * </p>
026     * Note that assertions with date parameter comes with two flavor, one is obviously a {@link Date} and the other is a
027     * String representing a Date.<br>
028     * For the latter, the default format follows ISO 8901 : "yyyy-MM-dd", user can override it with a custom format by
029     * calling {@link #withDateFormat(DateFormat)}.<br>
030     * The user custom format will then be used for all next Date assertions (i.e not limited to the current assertion) in
031     * the test suite.<br>
032     * To turn back to default format, simply call {@link #withIsoDateFormat()}.
033     * 
034     * @author Tomasz Nurkiewicz (thanks for giving assertions idea)
035     * @author Joel Costigliola
036     * @author Mikhail Mazursky
037     */
038    public class DateAssert extends AbstractAssert<DateAssert, Date> {
039    
040      @VisibleForTesting
041      Dates dates = Dates.instance();
042    
043      /**
044       * Used in String based Date assertions - like {@link #isAfter(String)} - to convert input date represented as string
045       * to Date.<br>
046       * The format used can be overriden by invoking {@link #withDateFormat(DateFormat)}
047       */
048      @VisibleForTesting
049      static DateFormat dateFormat = ISO_DATE_FORMAT;
050    
051      /**
052       * Creates a new </code>{@link DateAssert}</code>.
053       * @param actual the target to verify.
054       */
055      protected DateAssert(Date actual) {
056        super(actual, DateAssert.class);
057      }
058    
059      /**
060       * Same assertion as {@link AbstractAssert#isEqualTo(Object) isEqualTo(Date date)} but given Date is represented as String either with ISO date format
061       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
062       * @param dateAsString the given Date represented as String in default or custom date format.
063       * @return this assertion object.
064       * @throws AssertionError if actual and given Date represented as String are not equal.
065       * @throws AssertionError if the given date as String could not be converted to a Date.
066       */
067      public DateAssert isEqualTo(String dateAsString) {
068        return isEqualTo(parse(dateAsString));
069      }
070    
071      /**
072       * Same assertion as {@link AbstractAssert#isNotEqualTo(Object) isNotEqualTo(Date date)} but given Date is represented as String either with ISO date format
073       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
074       * @param dateAsString the given Date represented as String in default or custom date format.
075       * @return this assertion object.
076       * @throws AssertionError if actual and given Date represented as String are equal.
077       * @throws AssertionError if the given date as String could not be converted to a Date.
078       */
079      public DateAssert isNotEqualTo(String dateAsString) {
080        return isNotEqualTo(parse(dateAsString));
081      }
082    
083      /**
084       * Same assertion as {@link Assert#isIn(Object...)} but given Dates are represented as String either with ISO date format
085       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
086       * @param datesAsString the given Dates represented as String in default or custom date format.
087       * @return this assertion object.
088       * @throws AssertionError if actual is not in given Dates represented as String.
089       * @throws AssertionError if one of the given date as String could not be converted to a Date.
090       */
091      public DateAssert isIn(String... datesAsString) {
092        Date[] dates = new Date[datesAsString.length];
093        for (int i = 0; i < datesAsString.length; i++) {
094          dates[i] = parse(datesAsString[i]);
095        }
096        return isIn(dates);
097      }
098    
099      /**
100       * Same assertion as {@link Assert#isIn(Iterable)} but given Dates are represented as String either with ISO date format
101       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).<br>
102       * Method signature could not be <code>isIn(Collection&lt;String&gt;)</code> because it would be same signature as
103       * <code>isIn(Collection&lt;Date&gt;)</code> since java collection type are erased at runtime.
104       * @param datesAsString the given Dates represented as String in default or custom date format.
105       * @return this assertion object.
106       * @throws AssertionError if actual is not in given Dates represented as String.
107       * @throws AssertionError if one of the given date as String could not be converted to a Date.
108       */
109      public DateAssert isInWithStringDateCollection(Collection<String> datesAsString) {
110        Collection<Date> dates = new ArrayList<Date>(datesAsString.size());
111        for (String dateAsString : datesAsString) {
112          dates.add(parse(dateAsString));
113        }
114        return isIn(dates);
115      }
116    
117      /**
118       * Same assertion as {@link Assert#isNotIn(Object...)} but given Dates are represented as String either with ISO date
119       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
120       * @param datesAsString the given Dates represented as String in default or custom date format.
121       * @return this assertion object.
122       * @throws AssertionError if actual is in given Dates represented as String.
123       * @throws AssertionError if one of the given date as String could not be converted to a Date.
124       */
125      public DateAssert isNotIn(String... datesAsString) {
126        Date[] dates = new Date[datesAsString.length];
127        for (int i = 0; i < datesAsString.length; i++) {
128          dates[i] = parse(datesAsString[i]);
129        }
130        return isNotIn(dates);
131      }
132    
133      /**
134       * Same assertion as {@link Assert#isNotIn(Iterable)} but given Dates are represented as String either with ISO date
135       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).<br>
136       * Method signature could not be <code>isNotIn(Collection&lt;String&gt;)</code> because it would be same signature as
137       * <code>isNotIn(Collection&lt;Date&gt;)</code> since java collection type are erased at runtime.
138       * @param datesAsString the given Dates represented as String in default or custom date format.
139       * @return this assertion object.
140       * @throws AssertionError if actual is in given Dates represented as String.
141       * @throws AssertionError if one of the given date as String could not be converted to a Date.
142       */
143      public DateAssert isNotInWithStringDateCollection(Collection<String> datesAsString) {
144        Collection<Date> dates = new ArrayList<Date>(datesAsString.size());
145        for (String dateAsString : datesAsString) {
146          dates.add(parse(dateAsString));
147        }
148        return isNotIn(dates);
149      }
150    
151      /**
152       * Verifies that the actual {@code Date} is <b>strictly</b> before the given one.
153       * @param other the given Date.
154       * @return this assertion object.
155       * @throws AssertionError if the actual {@code Date} is {@code null}.
156       * @throws NullPointerException if other {@code Date} is {@code null}.
157       * @throws AssertionError if the actual {@code Date} is not strictly before the given one.
158       */
159      public DateAssert isBefore(Date other) {
160        dates.assertIsBefore(info, actual, other);
161        return this;
162      }
163    
164      /**
165       * Same assertion as {@link #isBefore(Date)} but given Date is represented as String either with ISO date format
166       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
167       * @param dateAsString the given Date represented as String in default or custom date format.
168       * @return this assertion object.
169       * @throws AssertionError if the actual {@code Date} is {@code null}.
170       * @throws NullPointerException if given date as String is {@code null}.
171       * @throws AssertionError if the actual {@code Date} is not strictly before the given Date represented as String.
172       * @throws AssertionError if the given date as String could not be converted to a Date.
173       */
174      public DateAssert isBefore(String dateAsString) {
175        return isBefore(parse(dateAsString));
176      }
177    
178      /**
179       * Verifies that the actual {@code Date} is before or equals to the given one.
180       * @param other the given Date.
181       * @return this assertion object.
182       * @throws AssertionError if the actual {@code Date} is {@code null}.
183       * @throws NullPointerException if other {@code Date} is {@code null}.
184       * @throws AssertionError if the actual {@code Date} is not before or equals to the given one.
185       */
186      public DateAssert isBeforeOrEqualsTo(Date other) {
187        dates.assertIsBeforeOrEqualsTo(info, actual, other);
188        return this;
189      }
190    
191      /**
192       * Same assertion as {@link #isBeforeOrEqualsTo(Date)} but given Date is represented as String either with ISO date
193       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
194       * @param dateAsString the given Date represented as String in default or custom date format.
195       * @return this assertion object.
196       * @throws AssertionError if the actual {@code Date} is {@code null}.
197       * @throws NullPointerException if given date as String is {@code null}.
198       * @throws AssertionError if the actual {@code Date} is not before or equals to the given Date represented as String.
199       * @throws AssertionError if the given date as String could not be converted to a Date.
200       */
201      public DateAssert isBeforeOrEqualsTo(String dateAsString) {
202        return isBeforeOrEqualsTo(parse(dateAsString));
203      }
204    
205      /**
206       * Verifies that the actual {@code Date} is <b>strictly</b> after the given one.
207       * @param other the given Date.
208       * @return this assertion object.
209       * @throws AssertionError if the actual {@code Date} is {@code null}.
210       * @throws NullPointerException if other {@code Date} is {@code null}.
211       * @throws AssertionError if the actual {@code Date} is not strictly after the given one.
212       */
213      public DateAssert isAfter(Date other) {
214        dates.assertIsAfter(info, actual, other);
215        return this;
216      }
217    
218      /**
219       * Same assertion as {@link #isAfter(Date)} but given Date is represented as String either with ISO date format
220       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
221       * @param dateAsString the given Date represented as String in default or custom date format.
222       * @return this assertion object.
223       * @throws AssertionError if the actual {@code Date} is {@code null}.
224       * @throws NullPointerException if given date as String is {@code null}.
225       * @throws AssertionError if the actual {@code Date} is not strictly after the given Date represented as String.
226       * @throws AssertionError if the given date as String could not be converted to a Date.
227       */
228      public DateAssert isAfter(String dateAsString) {
229        return isAfter(parse(dateAsString));
230      }
231    
232      /**
233       * Verifies that the actual {@code Date} is after or equals to the given one.
234       * @param other the given Date.
235       * @return this assertion object.
236       * @throws AssertionError if the actual {@code Date} is {@code null}.
237       * @throws NullPointerException if other {@code Date} is {@code null}.
238       * @throws AssertionError if the actual {@code Date} is not after or equals to the given one.
239       */
240      public DateAssert isAfterOrEqualsTo(Date other) {
241        dates.assertIsAfterOrEqualsTo(info, actual, other);
242        return this;
243      }
244    
245      /**
246       * Same assertion as {@link #isAfterOrEqualsTo(Date)} but given Date is represented as String either with ISO date
247       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
248       * @param dateAsString the given Date represented as String in default or custom date format.
249       * @return this assertion object.
250       * @throws AssertionError if the actual {@code Date} is {@code null}.
251       * @throws NullPointerException if given date as String is {@code null}.
252       * @throws AssertionError if the actual {@code Date} is not after or equals to the given Date represented as String.
253       * @throws AssertionError if the given date as String could not be converted to a Date.
254       */
255      public DateAssert isAfterOrEqualsTo(String dateAsString) {
256        return isAfterOrEqualsTo(parse(dateAsString));
257      }
258    
259      /**
260       * Verifies that the actual {@code Date} is in [start, end[ period (start included, end excluded).
261       * @param start the period start (inclusive), expected not to be null.
262       * @param end the period end (exclusive), expected not to be null.
263       * @return this assertion object.
264       * @throws AssertionError if the actual {@code Date} is {@code null}.
265       * @throws NullPointerException if start {@code Date} is {@code null}.
266       * @throws NullPointerException if end {@code Date} is {@code null}.
267       * @throws AssertionError if the actual {@code Date} is not in [start, end[ period.
268       */
269      public DateAssert isBetween(Date start, Date end) {
270        return isBetween(start, end, true, false);
271      }
272    
273      /**
274       * Same assertion as {@link #isBetween(Date, Date)} but given Dates are represented as String either with ISO date
275       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
276       * @param start the period start (inclusive), expected not to be null.
277       * @param end the period end (exclusive), expected not to be null.
278       * @return this assertion object.
279       * @throws AssertionError if the actual {@code Date} is {@code null}.
280       * @throws NullPointerException if start Date as String is {@code null}.
281       * @throws NullPointerException if end Date as String is {@code null}.
282       * @throws AssertionError if the actual {@code Date} is not in [start, end[ period.
283       * @throws AssertionError if one of the given date as String could not be converted to a Date.
284       */
285      public DateAssert isBetween(String start, String end) {
286        return isBetween(parse(start), parse(end));
287      }
288    
289      /**
290       * Verifies that the actual {@code Date} is in the given period defined by start and end dates.<br>
291       * To include start in the period set inclusiveStart parameter to <code>true</code>.<br>
292       * To include end in the period set inclusiveEnd parameter to <code>true</code>.<br>
293       * @param start the period start, expected not to be null.
294       * @param end the period end, expected not to be null.
295       * @param inclusiveStart wether to include start date in period.
296       * @param inclusiveEnd wether to include end date in period.
297       * @return this assertion object.
298       * @throws AssertionError if {@code actual} is {@code null}.
299       * @throws NullPointerException if start {@code Date} is {@code null}.
300       * @throws NullPointerException if end {@code Date} is {@code null}.
301       * @throws AssertionError if the actual {@code Date} is not in (start, end) period.
302       */
303      public DateAssert isBetween(Date start, Date end, boolean inclusiveStart, boolean inclusiveEnd) {
304        dates.assertIsBetween(info, actual, start, end, inclusiveStart, inclusiveEnd);
305        return this;
306      }
307    
308      /**
309       * Same assertion as {@link #isBetween(Date, Date, boolean, boolean)} but given Dates are represented as String either
310       * with ISO date format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
311       * @param start the period start, expected not to be null.
312       * @param end the period end, expected not to be null.
313       * @param inclusiveStart wether to include start date in period.
314       * @param inclusiveEnd wether to include end date in period.
315       * @return this assertion object.
316       * @throws AssertionError if {@code actual} is {@code null}.
317       * @throws NullPointerException if start Date as String is {@code null}.
318       * @throws NullPointerException if end Date as String is {@code null}.
319       * @throws AssertionError if the actual {@code Date} is not in (start, end) period.
320       * @throws AssertionError if one of the given date as String could not be converted to a Date.
321       */
322      public DateAssert isBetween(String start, String end, boolean inclusiveStart, boolean inclusiveEnd) {
323        dates.assertIsBetween(info, actual, parse(start), parse(end), inclusiveStart, inclusiveEnd);
324        return this;
325      }
326    
327      /**
328       * Verifies that the actual {@code Date} is not in the given period defined by start and end dates.<br>
329       * To include start in the period set inclusiveStart parameter to <code>true</code>.<br>
330       * To include end in the period set inclusiveEnd parameter to <code>true</code>.<br>
331       * @param start the period start (inclusive), expected not to be null.
332       * @param end the period end (exclusive), expected not to be null.
333       * @param inclusiveStart wether to include start date in period.
334       * @param inclusiveEnd wether to include end date in period.
335       * @return this assertion object.
336       * @throws AssertionError if {@code actual} is {@code null}.
337       * @throws NullPointerException if start {@code Date} is {@code null}.
338       * @throws NullPointerException if end {@code Date} is {@code null}.
339       * @throws AssertionError if the actual {@code Date} is not in (start, end) period.
340       */
341      public DateAssert isNotBetween(Date start, Date end, boolean inclusiveStart, boolean inclusiveEnd) {
342        dates.assertIsNotBetween(info, actual, start, end, inclusiveStart, inclusiveEnd);
343        return this;
344      }
345    
346      /**
347       * Same assertion as {@link #isNotBetween(Date, Date, boolean, boolean)} but given Dates are represented as String
348       * either with ISO date format (yyyy-MM-dd) or user custom date format (set with method
349       * {@link #withDateFormat(DateFormat)}).
350       * @param start the period start (inclusive), expected not to be null.
351       * @param end the period end (exclusive), expected not to be null.
352       * @param inclusiveStart wether to include start date in period.
353       * @param inclusiveEnd wether to include end date in period.
354       * @return this assertion object.
355       * @throws AssertionError if {@code actual} is {@code null}.
356       * @throws NullPointerException if start Date as String is {@code null}.
357       * @throws NullPointerException if end Date as String is {@code null}.
358       * @throws AssertionError if the actual {@code Date} is not in (start, end) period.
359       * @throws AssertionError if one of the given date as String could not be converted to a Date.
360       */
361      public DateAssert isNotBetween(String start, String end, boolean inclusiveStart, boolean inclusiveEnd) {
362        return isNotBetween(parse(start), parse(end), inclusiveStart, inclusiveEnd);
363      }
364    
365      /**
366       * Verifies that the actual {@code Date} is not in [start, end[ period
367       * @param start the period start (inclusive), expected not to be null.
368       * @param end the period end (exclusive), expected not to be null.
369       * @return this assertion object.
370       * @throws AssertionError if the actual {@code Date} is {@code null}.
371       * @throws NullPointerException if start {@code Date} is {@code null}.
372       * @throws NullPointerException if end {@code Date} is {@code null}.
373       * @throws AssertionError if the actual {@code Date} is in [start, end[ period.
374       * @throws AssertionError if one of the given date as String could not be converted to a Date.
375       */
376      public DateAssert isNotBetween(Date start, Date end) {
377        return isNotBetween(start, end, true, false);
378      }
379    
380      /**
381       * Same assertion as {@link #isNotBetween(Date, Date)} but given Dates are represented as String either with ISO date
382       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
383       * @param start the period start (inclusive), expected not to be null.
384       * @param end the period end (exclusive), expected not to be null.
385       * @return this assertion object.
386       * @throws AssertionError if the actual {@code Date} is {@code null}.
387       * @throws NullPointerException if start Date as String is {@code null}.
388       * @throws NullPointerException if end Date as String is {@code null}.
389       * @throws AssertionError if the actual {@code Date} is in [start, end[ period.
390       * @throws AssertionError if one of the given date as String could not be converted to a Date.
391       */
392      public DateAssert isNotBetween(String start, String end) {
393        return isNotBetween(parse(start), parse(end), true, false);
394      }
395    
396      /**
397       * Verifies that the actual {@code Date} is strictly in the past.
398       * @return this assertion object.
399       * @throws AssertionError if the actual {@code Date} is {@code null}.
400       * @throws AssertionError if the actual {@code Date} is not in the past.
401       */
402      public DateAssert isInThePast() {
403        dates.assertIsInThePast(info, actual);
404        return this;
405      }
406    
407      /**
408       * Verifies that the actual {@code Date} is today, that is matching current year, month and day (no check on hour,
409       * minute, second, milliseconds).
410       * @return this assertion object.
411       * @throws AssertionError if the actual {@code Date} is {@code null}.
412       * @throws AssertionError if the actual {@code Date} is not today.
413       */
414      public DateAssert isToday() {
415        dates.assertIsToday(info, actual);
416        return this;
417      }
418    
419      /**
420       * Verifies that the actual {@code Date} is strictly in the future.
421       * @return this assertion object.
422       * @throws AssertionError if the actual {@code Date} is {@code null}.
423       * @throws AssertionError if the actual {@code Date} is not in the future.
424       */
425      public DateAssert isInTheFuture() {
426        dates.assertIsInTheFuture(info, actual);
427        return this;
428      }
429    
430      /**
431       * Verifies that the actual {@code Date} is <b>strictly</b> before the given year.
432       * @param year the year to compare actual year to
433       * @return this assertion object.
434       * @throws AssertionError if the actual {@code Date} is {@code null}.
435       * @throws AssertionError if the actual {@code Date} year is after or equals to the given year.
436       */
437      public DateAssert isBeforeYear(int year) {
438        dates.assertIsBeforeYear(info, actual, year);
439        return this;
440      }
441    
442      /**
443       * Verifies that the actual {@code Date} is <b>strictly</b> after the given year.
444       * @param year the year to compare actual year to
445       * @return this assertion object.
446       * @throws AssertionError if the actual {@code Date} is {@code null}.
447       * @throws AssertionError if the actual {@code Date} year is before or equals to the given year.
448       */
449      public DateAssert isAfterYear(int year) {
450        dates.assertIsAfterYear(info, actual, year);
451        return this;
452      }
453    
454      /**
455       * Verifies that the actual {@code Date} year is equal to the given year.
456       * <p>
457       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
458       *  
459       * @param year the year to compare actual year to
460       * @return this assertion object.
461       * @throws AssertionError if the actual {@code Date} is {@code null}.
462       * @throws AssertionError if the actual {@code Date} year is not equal to the given year.
463       */
464      public DateAssert isWithinYear(int year) {
465        dates.assertIsWithinYear(info, actual, year);
466        return this;
467      }
468    
469      /**
470       * Verifies that the actual {@code Date} month is equal to the given month, <b>month value starting at 1</b>
471       * (January=1, February=2, ...).
472       * <p>
473       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
474       *  
475       * @param month the month to compare actual month to, <b>month value starting at 1</b> (January=1, February=2, ...).
476       * @return this assertion object.
477       * @throws AssertionError if the actual {@code Date} is {@code null}.
478       * @throws AssertionError if the actual {@code Date} month is not equal to the given month.
479       */
480      public DateAssert isWithinMonth(int month) {
481        dates.assertIsWithinMonth(info, actual, month);
482        return this;
483      }
484    
485      /**
486       * Verifies that the actual {@code Date} day of month is equal to the given day of month.
487       * <p>
488       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
489       *  
490       * @param dayOfMonth the day of month to compare actual day of month to
491       * @return this assertion object.
492       * @throws AssertionError if the actual {@code Date} is {@code null}.
493       * @throws AssertionError if the actual {@code Date} month is not equal to the given day of month.
494       */
495      public DateAssert isWithinDayOfMonth(int dayOfMonth) {
496        dates.assertIsWithinDayOfMonth(info, actual, dayOfMonth);
497        return this;
498      }
499    
500      /**
501       * Verifies that the actual {@code Date} day of week is equal to the given day of week (see
502       * {@link Calendar#DAY_OF_WEEK} for valid values).
503       * <p>
504       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
505       *  
506       * @param dayOfWeek the day of week to compare actual day of week to, see {@link Calendar#DAY_OF_WEEK} for valid
507       *          values
508       * @return this assertion object.
509       * @throws AssertionError if the actual {@code Date} is {@code null}.
510       * @throws AssertionError if the actual {@code Date} week is not equal to the given day of week.
511       */
512      public DateAssert isWithinDayOfWeek(int dayOfWeek) {
513        dates.assertIsWithinDayOfWeek(info, actual, dayOfWeek);
514        return this;
515      }
516    
517      /**
518       * Verifies that the actual {@code Date} hour od day is equal to the given hour of day (24-hour clock).
519       * <p>
520       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
521       *  
522       * @param hourOfDay the hour of day to compare actual hour of day to (24-hour clock)
523       * @return this assertion object.
524       * @throws AssertionError if the actual {@code Date} is {@code null}.
525       * @throws AssertionError if the actual {@code Date} hour is not equal to the given hour.
526       */
527      public DateAssert isWithinHourOfDay(int hourOfDay) {
528        dates.assertIsWithinHourOfDay(info, actual, hourOfDay);
529        return this;
530      }
531    
532      /**
533       * Verifies that the actual {@code Date} minute is equal to the given minute.
534       * <p>
535       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
536       *  
537       * @param minute the minute to compare actual minute to
538       * @return this assertion object.
539       * @throws AssertionError if the actual {@code Date} is {@code null}.
540       * @throws AssertionError if the actual {@code Date} minute is not equal to the given minute.
541       */
542      public DateAssert isWithinMinute(int minute) {
543        dates.assertIsWithinMinute(info, actual, minute);
544        return this;
545      }
546    
547      /**
548       * Verifies that the actual {@code Date} second is equal to the given second.
549       * <p>
550       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
551       *  
552       * @param second the second to compare actual second to
553       * @return this assertion object.
554       * @throws AssertionError if the actual {@code Date} is {@code null}.
555       * @throws AssertionError if the actual {@code Date} second is not equal to the given second.
556       */
557      public DateAssert isWithinSecond(int second) {
558        dates.assertIsWithinSecond(info, actual, second);
559        return this;
560      }
561    
562      /**
563       * Verifies that the actual {@code Date} millisecond is equal to the given millisecond.
564       * <p>
565       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
566       *  
567       * @param millisecond the millisecond to compare actual millisecond to
568       * @return this assertion object.
569       * @throws AssertionError if the actual {@code Date} is {@code null}.
570       * @throws AssertionError if the actual {@code Date} millisecond is not equal to the given millisecond.
571       */
572      public DateAssert isWithinMillisecond(int millisecond) {
573        dates.assertIsWithinMillisecond(info, actual, millisecond);
574        return this;
575      }
576    
577      /**
578       * Verifies that actual and given {@code Date} are in the same year.
579       * <p>
580       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
581       *  
582       * @param other the given {@code Date} to compare actual {@code Date} to.
583       * @return this assertion object.
584       * @throws NullPointerException if {@code Date} parameter is {@code null}.
585       * @throws AssertionError if the actual {@code Date} is {@code null}.
586       * @throws AssertionError if actual and given {@code Date} are not in the same year.
587       */
588      public DateAssert isInSameYearAs(Date other) {
589        dates.assertIsInSameYearAs(info, actual, other);
590        return this;
591      }
592    
593      /**
594       * Same assertion as {@link #isInSameYearAs(Date)} but given Date is represented as String either with ISO date format
595       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
596       * @param dateAsString the given Date represented as String in default or custom date format.
597       * @return this assertion object.
598       * @throws NullPointerException if dateAsString parameter is {@code null}.
599       * @throws AssertionError if the actual {@code Date} is {@code null}.
600       * @throws AssertionError if actual and given Date represented as String are not in the same year.
601       * @throws AssertionError if the given date as String could not be converted to a Date.
602       */
603      public DateAssert isInSameYearAs(String dateAsString) {
604        return isInSameYearAs(parse(dateAsString));
605      }
606    
607      /**
608       * Verifies that actual and given {@code Date} are chronologically in the same month (and thus in the same year).
609       * <p>
610       * If you want to compare month only (without year), use :
611       * <code>assertThat(myDate).isWithinMonth(monthOf(otherDate))</code><br>
612       * See {@link org.fest.util.Dates#monthOf(Date)} to get the month of a given Date.
613       * <p>
614       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
615       *  
616       * @param other the given {@code Date} to compare actual {@code Date} to.
617       * @return this assertion object.
618       * @throws NullPointerException if {@code Date} parameter is {@code null}.
619       * @throws AssertionError if the actual {@code Date} is {@code null}.
620       * @throws AssertionError if actual and given {@code Date} are not in the same month.
621       */
622      public DateAssert isInSameMonthAs(Date other) {
623        dates.assertIsInSameMonthAs(info, actual, other);
624        return this;
625      }
626    
627      /**
628       * Same assertion as {@link #isInSameMonthAs(Date)} but given Date is represented as String either with ISO date
629       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
630       * @param dateAsString the given Date represented as String in default or custom date format.
631       * @return this assertion object.
632       * @throws NullPointerException if dateAsString parameter is {@code null}.
633       * @throws AssertionError if the actual {@code Date} is {@code null}.
634       * @throws AssertionError if actual and given {@code Date} are not in the same month.
635       */
636      public DateAssert isInSameMonthAs(String dateAsString) {
637        return isInSameMonthAs(parse(dateAsString));
638      }
639    
640      /**
641       * Verifies that actual and given {@code Date} are chronologically in the same day of month (and thus in the same
642       * month and year).
643       * <p>
644       * If you want to compare day of month only (without month and year), you could write :
645       * <code>assertThat(myDate).isWithinDayOfMonth(dayOfMonthOf(otherDate))</code><br>
646       * see {@link org.fest.util.Dates#dayOfMonthOf(Date)} to get the day of month of a given Date.
647       * <p>
648       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}. 
649       * 
650       * @param other the given {@code Date} to compare actual {@code Date} to.
651       * @return this assertion object.
652       * @throws NullPointerException if {@code Date} parameter is {@code null}.
653       * @throws AssertionError if the actual {@code Date} is {@code null}.
654       * @throws AssertionError if actual and given {@code Date} are not in the same day of month.
655       */
656      public DateAssert isInSameDayAs(Date other) {
657        dates.assertIsInSameDayAs(info, actual, other);
658        return this;
659      }
660    
661      /**
662       * Same assertion as {@link #isInSameDayAs(Date)} but given Date is represented as String either with ISO date format
663       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
664       * @param dateAsString the given Date represented as String in default or custom date format.
665       * @return this assertion object.
666       * @throws NullPointerException if dateAsString parameter is {@code null}.
667       * @throws AssertionError if the actual {@code Date} is {@code null}.
668       * @throws AssertionError if actual and given {@code Date} are not in the same day of month.
669       */
670      public DateAssert isInSameDayAs(String dateAsString) {
671        return isInSameDayAs(parse(dateAsString));
672      }
673    
674      /**
675       * Verifies that actual and given {@code Date} are chronologically in the same hour (and thus in the same day, month
676       * and year).
677       * <p>
678       * If you want to compare hour only (without day, month and year), you could write :
679       * <code>assertThat(myDate).isWithinHour(hourOfDayOf(otherDate))</code><br>
680       * see {@link org.fest.util.Dates#hourOfDay(Date)} to get the hour of a given Date.
681       * <p>
682       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
683       *  
684       * @param other the given {@code Date} to compare actual {@code Date} to.
685       * @return this assertion object.
686       * @throws NullPointerException if {@code Date} parameter is {@code null}.
687       * @throws AssertionError if the actual {@code Date} is {@code null}.
688       * @throws AssertionError if actual and given {@code Date} are not in the same hour.
689       */
690      public DateAssert isInSameHourAs(Date other) {
691        dates.assertIsInSameHourAs(info, actual, other);
692        return this;
693      }
694    
695      /**
696       * Same assertion as {@link #isInSameHourAs(Date)} but given Date is represented as String either with ISO date format
697       * (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
698       * @param dateAsString the given Date represented as String in default or custom date format.
699       * @return this assertion object.
700       * @throws NullPointerException if dateAsString parameter is {@code null}.
701       * @throws AssertionError if the actual {@code Date} is {@code null}.
702       * @throws AssertionError if actual and given {@code Date} are not in the same hour.
703       */
704      public DateAssert isInSameHourAs(String dateAsString) {
705        return isInSameHourAs(parse(dateAsString));
706      }
707    
708      /**
709       * Verifies that actual and given {@code Date} are chronologically in the same minute (and thus in the same hour, day,
710       * month and year).
711       * <p>
712       * If you want to compare minute only (without hour, day, month and year), you could write :
713       * <code>assertThat(myDate).isWithinMinute(minuteOf(otherDate))</code><br>
714       * see {@link org.fest.util.Dates#minuteOf(Date)} to get the minute of a given Date.
715       * <p>
716       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
717       *  
718       * @param other the given {@code Date} to compare actual {@code Date} to.
719       * @return this assertion object.
720       * @throws NullPointerException if {@code Date} parameter is {@code null}.
721       * @throws AssertionError if the actual {@code Date} is {@code null}.
722       * @throws AssertionError if actual and given {@code Date} are not in the same minute.
723       */
724      public DateAssert isInSameMinuteAs(Date other) {
725        dates.assertIsInSameMinuteAs(info, actual, other);
726        return this;
727      }
728    
729      /**
730       * Same assertion as {@link #isInSameMinuteAs(Date)} but given Date is represented as String either with ISO date
731       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
732       * @param dateAsString the given Date represented as String in default or custom date format.
733       * @return this assertion object.
734       * @throws NullPointerException if dateAsString parameter is {@code null}.
735       * @throws AssertionError if the actual {@code Date} is {@code null}.
736       * @throws AssertionError if actual and given {@code Date} are not in the same minute.
737       */
738      public DateAssert isInSameMinuteAs(String dateAsString) {
739        return isInSameMinuteAs(parse(dateAsString));
740      }
741    
742      /**
743       * Verifies that actual and given {@code Date} are chronologically in the same second (and thus in the same minute,
744       * hour, day, month and year).
745       * <p>
746       * If you want to compare second only (without minute, hour, day, month and year), you could write :
747       * <code>assertThat(myDate).isWithinSecond(secondOf(otherDate))</code><br>
748       * see {@link org.fest.util.Dates#secondOf(Date)} to get the second of a given Date.
749       * <p>
750       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}.
751       *  
752       * @param other the given {@code Date} to compare actual {@code Date} to.
753       * @return this assertion object.
754       * @throws NullPointerException if {@code Date} parameter is {@code null}.
755       * @throws AssertionError if the actual {@code Date} is {@code null}.
756       * @throws AssertionError if actual and given {@code Date} are not in the same second.
757       */
758      public DateAssert isInSameSecondAs(Date other) {
759        dates.assertIsInSameSecondAs(info, actual, other);
760        return this;
761      }
762    
763      /**
764       * Same assertion as {@link #isInSameSecondAs(Date)} but given Date is represented as String either with ISO date
765       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
766       * @param dateAsString the given Date represented as String in default or custom date format.
767       * @return this assertion object.
768       * @throws NullPointerException if dateAsString parameter is {@code null}.
769       * @throws AssertionError if the actual {@code Date} is {@code null}.
770       * @throws AssertionError if actual and given {@code Date} are not in the same second.
771       */
772      public DateAssert isInSameSecondAs(String dateAsString) {
773        return isInSameSecondAs(parse(dateAsString));
774      }
775    
776      /**
777       * Verifies that the actual {@code Date} is close to the other date by less than delta (expressed in milliseconds), if
778       * difference is equals to delta it's ok.
779       * <p>
780       * One can use handy {@link TimeUnit} to convert a duration in milliseconds, for example you can express a delta of 5 seconds with
781       * <code>TimeUnit.SECONDS.toMillis(5)</code>.
782       * <p>
783       * Note that using a custom comparator has no effect on this assertion (see {@link #usingComparator(Comparator)}. 
784       * @param other the date to compare actual to
785       * @param deltaInMilliseconds the delta used for date comparison, expressed in milliseconds
786       * @return this assertion object.
787       * @throws NullPointerException if {@code Date} parameter is {@code null}.
788       * @throws AssertionError if the actual {@code Date} is {@code null}.
789       * @throws AssertionError if the actual {@code Date} week is not close to the given date by less than delta.
790       */
791      public DateAssert isCloseTo(Date other, long deltaInMilliseconds) {
792        dates.assertIsCloseTo(info, actual, other, deltaInMilliseconds);
793        return this;
794      }
795    
796      /**
797       * Same assertion as {@link #isCloseTo(Date, long)} but given Date is represented as String either with ISO date
798       * format (yyyy-MM-dd) or user custom date format (set with method {@link #withDateFormat(DateFormat)}).
799       * @param dateAsString the given Date represented as String in default or custom date format.
800       * @param deltaInMilliseconds the delta used for date comparison, expressed in milliseconds
801       * @return this assertion object.
802       * @throws NullPointerException if dateAsString parameter is {@code null}.
803       * @throws AssertionError if the actual {@code Date} is {@code null}.
804       * @throws AssertionError if the actual {@code Date} week is not close to the given date by less than delta.
805       */
806      public DateAssert isCloseTo(String dateAsString, long deltaInMilliseconds) {
807        return isCloseTo(parse(dateAsString), deltaInMilliseconds);
808      }
809    
810      /**
811       * For String based Date assertions like {@link #isBefore(String)}, given String is expected to follow the default
812       * Date format, that is ISO 8601 format : "yyyy-MM-dd".
813       * <p>
814       * With this method, user can specify its own date format, replacing the current date format for all future Date
815       * assertions in the test suite (i.e. not only the current assertions) since custom DateFormat is stored in a static
816       * field.
817       * <p>
818       * To revert to default format simply call {@link #withIsoDateFormat()}.
819       * 
820       * @param userCustomDateFormat the new Date format used for String based Date assertions.
821       * @return this assertion object.
822       */
823      public DateAssert withDateFormat(DateFormat userCustomDateFormat) {
824        useDateFormat(userCustomDateFormat);
825        return this;
826      }
827    
828      /**
829       * For String based Date assertions like {@link #isBefore(String)}, given String is expected to follow the default
830       * Date format, that is ISO 8601 format : "yyyy-MM-dd".
831       * <p>
832       * With this method, user can specify its own date format, replacing the current date format for all future Date
833       * assertions in the test suite (i.e. not only the current assertions) since custom DateFormat is stored in a static
834       * field.
835       * <p>
836       * To revert to default format simply call {@link #useIsoDateFormat()} (static method) or {@link #withIsoDateFormat()}.
837       * 
838       * @param userCustomDateFormat the new Date format used for String based Date assertions.
839       */
840      public static void useDateFormat(DateFormat userCustomDateFormat) {
841        if (userCustomDateFormat == null) throw new NullPointerException("The given date format should not be null");
842        dateFormat = userCustomDateFormat;
843      }
844    
845      /**
846       * Use ISO 8601 date format ("yyyy-MM-dd") for String based Date assertions.
847       * @return this assertion object.
848       */
849      public DateAssert withIsoDateFormat() {
850        useIsoDateFormat();
851        return this;
852      }
853    
854      /**
855       * Use ISO 8601 date format ("yyyy-MM-dd") for String based Date assertions.
856       */
857      public static void useIsoDateFormat() {
858        dateFormat = ISO_DATE_FORMAT;
859      }
860    
861      /**
862       * Utillity method to parse a Date with {@link #dateFormat}, note that it is thread safe.<br>
863       * Returns <code>null</code> if dateAsString parameter is <code>null</code>.
864       * @param dateAsString the string to parse as a Date with {@link #dateFormat}
865       * @return the corrresponding Date, null if dateAsString parameter is null.
866       * @throws AssertionError if the string can't be parsed as a Date
867       */
868      private static Date parse(String dateAsString) {
869        if (dateAsString == null) { return null; }
870        try {
871          // synchronized is used because SimpleDateFormat which is not thread safe (sigh).
872          synchronized (dateFormat) {
873            return dateFormat.parse(dateAsString);
874          }
875        } catch (ParseException e) {
876          throw Failures.instance().failure("Failed to parse " + dateAsString + " with date format " + dateFormat);
877        }
878      }
879    
880      @Override
881      public DateAssert usingComparator(Comparator<? super Date> customComparator) {
882        super.usingComparator(customComparator);
883        this.dates = new Dates(new ComparatorBasedComparisonStrategy(customComparator));
884        return myself;
885      }
886    
887      @Override
888      public DateAssert usingDefaultComparator() {
889        super.usingDefaultComparator();
890        this.dates = Dates.instance();
891        return myself;
892      }
893    }