EMMA Coverage Report (generated Mon Nov 21 00:13:19 GMT 2005)
[all classes][net.sourceforge.calendardate]

COVERAGE SUMMARY FOR SOURCE FILE [CalendarDate.java]

nameclass, %method, %block, %line, %
CalendarDate.java100% (1/1)100% (35/35)100% (683/683)100% (132/132)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class CalendarDate100% (1/1)100% (35/35)100% (683/683)100% (132/132)
<static initializer> 100% (1/1)100% (113/113)100% (27/27)
CalendarDate (TimeZone): void 100% (1/1)100% (7/7)100% (2/2)
CalendarDate (TimeZone, Date): void 100% (1/1)100% (27/27)100% (6/6)
CalendarDate (int, int, int): void 100% (1/1)100% (7/7)100% (2/2)
CalendarDate (int, int, int, boolean): void 100% (1/1)100% (18/18)100% (6/6)
addDays (int): CalendarDate 100% (1/1)100% (13/13)100% (1/1)
addMonths (int): CalendarDate 100% (1/1)100% (36/36)100% (5/5)
checkHourMinSec (int, int, int): void 100% (1/1)100% (49/49)100% (7/7)
checkValidYearMonthDay (int, int, int): void 100% (1/1)100% (25/25)100% (4/4)
compareTo (Object): int 100% (1/1)100% (7/7)100% (1/1)
correctNonLenientFieldsIfNecessary (): void 100% (1/1)100% (38/38)100% (6/6)
daysInMonth (int, int): int 100% (1/1)100% (15/15)100% (4/4)
daysToYear (int): int 100% (1/1)100% (7/7)100% (1/1)
daysUntil (CalendarDate): int 100% (1/1)100% (6/6)100% (1/1)
equals (Object): boolean 100% (1/1)100% (13/13)100% (3/3)
getDayOfMonth (): int 100% (1/1)100% (3/3)100% (1/1)
getDayOfWeek (): int 100% (1/1)100% (7/7)100% (1/1)
getDaysSinceEpoch (): int 100% (1/1)100% (37/37)100% (8/8)
getMonth (): int 100% (1/1)100% (3/3)100% (1/1)
getYear (): int 100% (1/1)100% (3/3)100% (1/1)
hashCode (): int 100% (1/1)100% (13/13)100% (1/1)
init (int, int, int): void 100% (1/1)100% (27/27)100% (7/7)
isAfter (CalendarDate): boolean 100% (1/1)100% (8/8)100% (1/1)
isBefore (CalendarDate): boolean 100% (1/1)100% (8/8)100% (1/1)
isLeapYear (int): boolean 100% (1/1)100% (16/16)100% (1/1)
isOutsideRange (TimeZone, Date): boolean 100% (1/1)100% (13/13)100% (3/3)
isValidYearMonthDay (int, int, int): boolean 100% (1/1)100% (31/31)100% (7/7)
monthsUntil (CalendarDate): int 100% (1/1)100% (14/14)100% (2/2)
numLeapsToYear (int): int 100% (1/1)100% (26/26)100% (5/5)
readObject (ObjectInputStream): void 100% (1/1)100% (13/13)100% (3/3)
toDate (TimeZone): Date 100% (1/1)100% (7/7)100% (1/1)
toDate (TimeZone, int, int, int): Date 100% (1/1)100% (28/28)100% (5/5)
toString (): String 100% (1/1)100% (18/18)100% (2/2)
writeObject (ObjectOutputStream): void 100% (1/1)100% (17/17)100% (5/5)
yearOutOfRange (int): boolean 100% (1/1)100% (10/10)100% (1/1)

1package net.sourceforge.calendardate;
2 
3import java.io.IOException;
4import java.io.Serializable;
5import java.util.Calendar;
6import java.util.Date;
7import java.util.GregorianCalendar;
8import java.util.TimeZone;
9 
10/**
11 * This class represents a date in the Gregorian calendar (for example, December
12 * 20, 1998). It is designed to be a simpler, immutable version of
13 * <code>java.util.GregorianCalendar</code>.
14 * <p>
15 * <b>Don't confuse this class with <code>java.util.Date</code>! </b> They
16 * represent two separate things:
17 * <ul>
18 * <li><code>CalendarDate</code> represents a 'date in the calendar' (e.g.
19 * "John Smith's birthday") while
20 * <li><code>java.util.Date</code> represent an 'instant in time' (e.g. "When
21 * order 23711 was received"). A better name for this class would have been
22 * 'DateTime'.
23 * </ul>
24 * These two ways of recording time are not directly related: For any given
25 * 'instant in time' the corresponding 'date in the calendar' depends on the
26 * timezone. Similiarly the 'instant in time' when a 'date in the calendar'
27 * begins depends on the timezone. (This is why you must supply a
28 * <code>java.util.TimeZone</code> when converting between instances of
29 * <code>CalendarDate</code> and <code>Date</code>.)
30 * <p>
31 * This class is thread-safe and immutable.
32 * <p>
33 * 
34 * @see java.util.Date
35 * @see java.util.GregorianCalendar
36 */
37public final class CalendarDate implements Comparable, Serializable {
38 
39        static final long serialVersionUID = 8577551385869073340L;
40 
41        /** The value returned by getDayOfWeek() representing Sunday */
42        public static final int SUNDAY = 1;
43 
44        /** The value returned by getDayOfWeek() representing Monday */
45        public static final int MONDAY = 2;
46 
47        /** The value returned by getDayOfWeek() representing Tuesday */
48        public static final int TUESDAY = 3;
49 
50        /** The value returned by getDayOfWeek() representing Wednesday */
51        public static final int WEDNESDAY = 4;
52 
53        /** The value returned by getDayOfWeek() representing Thursday */
54        public static final int THURSDAY = 5;
55 
56        /** The value returned by getDayOfWeek() representing Friday */
57        public static final int FRIDAY = 6;
58 
59        /** The value returned by getDayOfWeek() representing Saturday */
60        public static final int SATURDAY = 7;
61 
62        /** The value returned by getMonth() representing January */
63        public static final int JANUARY = 1;
64 
65        /** The value returned by getMonth() representing February */
66        public static final int FEBRUARY = 2;
67 
68        /** The value returned by getMonth() representing March */
69        public static final int MARCH = 3;
70 
71        /** The value returned by getMonth() representing April */
72        public static final int APRIL = 4;
73 
74        /** The value returned by getMonth() representing May */
75        public static final int MAY = 5;
76 
77        /** The value returned by getMonth() representing June */
78        public static final int JUNE = 6;
79 
80        /** The value returned by getMonth() representing July */
81        public static final int JULY = 7;
82 
83        /** The value returned by getMonth() representing August */
84        public static final int AUGUST = 8;
85 
86        /** The value returned by getMonth() representing September */
87        public static final int SEPTEMBER = 9;
88 
89        /** The value returned by getMonth() representing October */
90        public static final int OCTOBER = 10;
91 
92        /** The value returned by getMonth() representing November */
93        public static final int NOVEMBER = 11;
94 
95        /** The value returned by getMonth() representing December */
96        public static final int DECEMBER = 12;
97 
98        /** Days since 1 Jan, 1 A.D., or -1 if not calculated yet */
99        private transient int daysSinceEpoch = -1;
100 
101        private int year;
102 
103        private int month;
104 
105        private int dayOfMonth;
106 
107        /**
108         * The number of days in the year up to (but not including) a month.
109         */
110        private static final int[] cumulDaysToMonth = { 0, // Jan
111                        31, // Feb
112                        59, // Mar
113                        90, // Apr
114                        120, // May
115                        151, // Jun
116                        181, // Jul
117                        212, // Aug
118                        243, // Sep
119                        273, // Oct
120                        304, // Nov
121                        334 // Dec
122        };
123 
124        private static final int[] daysInMonth = { 31, // Jan
125                        28, // Feb
126                        31, // Mar
127                        30, // Apr
128                        31, // May
129                        30, // Jun
130                        31, // Jul
131                        31, // Aug
132                        30, // Sep
133                        31, // Oct
134                        30, // Nov
135                        31, // Dec
136        };
137 
138        /**
139         * The earliest date that can be represented by this class (Januray 1, 1600
140         * A.D.)
141         * <p>
142         * Note: This date may change to before 1600 in later releases of
143         * CalendarDate.
144         */
145        public static final CalendarDate EARLIEST = new CalendarDate(1600, 1, 1);
146 
147        /**
148         * The latest date that can be represented by this class (December 31, 2999
149         * A.D.)
150         * <p>
151         * Note: This date may change to after 2999 in later releases of
152         * CalendarDate.
153         */
154        public static final CalendarDate LATEST = new CalendarDate(2999, 12, 31);
155 
156        /**
157         * Creates a date represented by the given year, month and day.
158         * 
159         * @param year
160         *            The year of the date to create
161         * @param month
162         *            The month of the date to create (1 = January, 12 = December)
163         * @param dayOfMonth
164         *            The day of the month of the date to create.
165         * @throws IllegalArgumentException
166         *             if the year, month and dayOfMonth combination are not valid
167         *             in a Gregorian calendar.
168         */
169        public CalendarDate(int year, int month, int dayOfMonth) {
170                this(year, month, dayOfMonth, false);
171        }
172 
173        /**
174         * Creates a CalendarDate representing the date in the given timezone at the
175         * given instant in time.
176         * <p>
177         * <b>Think carefully about what timezone to use!</b> Often you will want
178         * to use the timezone of the 'user' - which is not always represented by
179         * <code>TimeZone.getDefault()</code>
180         * 
181         * @param tzone
182         *            The timezone to be considered
183         * @param instantInTime
184         *            The instant in time to be considered
185         * @throws IllegalArgumentException
186         *             if the instant in time is out of range in the given timezone
187         * @see #isOutsideRange(TimeZone, Date)
188         */
189        public CalendarDate(TimeZone tzone, Date instantInTime) {
190                GregorianCalendar cal = new GregorianCalendar(tzone);
191                cal.setTime(instantInTime);
192                init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH));
193        }
194 
195        /**
196         * Creates a CalendarDate representing the current date in the given
197         * timezone. Equivalent to <code>CalendarDate(tzone, new Date())</code>
198         * 
199         * @param tzone
200         *            The timezone to be considered
201         */
202        public CalendarDate(TimeZone tzone) {
203                this(tzone, new Date());
204        }
205 
206        /**
207         * Returns true if the given instant in time is before EARLIEST or after
208         * LATEST in the given timezone.
209         */
210        public static boolean isOutsideRange(TimeZone tzone, Date instantInTime) {
211                GregorianCalendar cal = new GregorianCalendar(tzone);
212                cal.setTime(instantInTime);
213                return yearOutOfRange(cal.get(Calendar.YEAR));
214        }
215 
216        /**
217         * 
218         * @param year
219         * @param month
220         * @param dayOfMonth
221         * @param lenient
222         */
223        private CalendarDate(int year, int month, int dayOfMonth, boolean lenient) {
224                if (!lenient) {
225                        checkValidYearMonthDay(year, month, dayOfMonth);
226                }
227                init(year, month, dayOfMonth);
228        }
229 
230        private void init(int year, int month, int dayOfMonth) {
231                this.year = year;
232                this.month = month;
233                this.dayOfMonth = dayOfMonth;
234                correctNonLenientFieldsIfNecessary();
235                if (yearOutOfRange(getYear())) {
236                        throw new IllegalArgumentException("Date year out of range: " + year);
237                }
238        }
239 
240        private void correctNonLenientFieldsIfNecessary() {
241                if (!isValidYearMonthDay(year, month, dayOfMonth)) {
242                        GregorianCalendar cal = new GregorianCalendar(year, month - 1, dayOfMonth);
243                        this.year = cal.get(Calendar.YEAR);
244                        this.month = cal.get(Calendar.MONTH) + 1;
245                        this.dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
246                }
247 
248        }
249 
250        private static boolean isValidYearMonthDay(int year, int month, int dayOfMonth) {
251                if ((dayOfMonth == 29) && (month == 2) && isLeapYear(year)) {
252                        return true;
253                }
254                if ((month <= 0) || (month > 12)) {
255                        return false;
256                }
257                if ((dayOfMonth <= 0) || (dayOfMonth > daysInMonth[month - 1])) {
258                        return false;
259                }
260                return true;
261        }
262 
263        private static boolean yearOutOfRange(int year) {
264                return (year < 1600) || (year >= 3000);
265        }
266 
267        /**
268         * Throws an exception if the input isn't a valid day in the Gregorian
269         * Calendar
270         */
271        private void checkValidYearMonthDay(int year, int month, int dayOfMonth) {
272                if (!isValidYearMonthDay(year, month, dayOfMonth)) {
273                        throw new IllegalArgumentException("Year/month/day combination is invalid: " + year
274                                        + "/" + month + "/" + dayOfMonth);
275                }
276        }
277 
278        /**
279         * The day of the month
280         * 
281         * @return The day of the month (in range 1 to 31)
282         */
283        public synchronized int getDayOfMonth() {
284                return dayOfMonth;
285        }
286 
287        /**
288         * The day of the week for this date
289         * 
290         * @return Day of week in range 1 (Sunday) to 7 (Saturday)
291         */
292        public synchronized int getDayOfWeek() {
293                return getDaysSinceEpoch() % 7 + 1;
294        }
295 
296        /**
297         * The month of this date
298         * 
299         * @return Month in range 1 to 12
300         */
301        public synchronized int getMonth() {
302                return month;
303        }
304 
305        /**
306         * The year of this date
307         * 
308         * @return The year
309         */
310        public synchronized int getYear() {
311                return year;
312        }
313 
314        /**
315         * Returns a new date which is this date offset by numDays.
316         * 
317         * @param numDays
318         *            the number of days to be added to this date (can be negative)
319         * @return A new date offset by numDays
320         * @throws IllegalArgumentException
321         *             if the resulting day would be before EARLIEST or after
322         *             LATEST. That is, if numDays &lt; this.daysUntil(EARLIEST) or
323         *             numDays &gt; this.daysUntil(LATEST)
324         */
325        public CalendarDate addDays(int numDays) {
326                return new CalendarDate(getYear(), getMonth(), getDayOfMonth() + numDays, true);
327        }
328 
329        /**
330         * Returns the number of days until the given date
331         * 
332         * @param otherDay
333         *            The date to compare to
334         * @return The number of days until otherDay (can be negative)
335         */
336        public int daysUntil(CalendarDate otherDay) {
337                return otherDay.getDaysSinceEpoch() - this.getDaysSinceEpoch();
338        }
339 
340        /**
341         * Returns the number of month changes until the given day. Note that this
342         * means there is just one 'month' between 1 November and 31 December.
343         * 
344         * @param otherDay
345         *            The date to compare to
346         * @return The number of month changes until the given day
347         */
348        public int monthsUntil(CalendarDate otherDay) {
349                return (otherDay.getMonth() - this.getMonth())
350                                + (12 * (otherDay.getYear() - this.getYear()));
351        }
352 
353        /**
354         * Days since epoch (1 Jan, 1 A.D.)
355         */
356        private synchronized int getDaysSinceEpoch() {
357                if (daysSinceEpoch == -1) {
358                        int year = getYear();
359                        int month = getMonth();
360                        int daysThisYear = cumulDaysToMonth[month - 1] + getDayOfMonth() - 1;
361                        if ((month > 2) && isLeapYear(year)) {
362                                daysThisYear++;
363                        }
364 
365                        daysSinceEpoch = daysToYear(year) + daysThisYear;
366                }
367                return daysSinceEpoch;
368        }
369 
370        /**
371         * Number of days up to, but not including, the given year since epoch.
372         * 
373         * @param year
374         * @return
375         */
376        static int daysToYear(int year) {
377                return (365 * year) + numLeapsToYear(year);
378        }
379 
380        /**
381         * Returns the number of leap years from the epoch until (but not including)
382         * the given year. The epoch begins on 1 Jan, 1AD
383         * 
384         * @param year
385         * @return The number of leap years
386         */
387        static int numLeapsToYear(int year) {
388                int num4y = (year - 1) / 4;
389                int num100y = (year - 1) / 100;
390                int num400y = (year - 1) / 400;
391                int numLeaps = num4y - num100y + num400y;
392                return numLeaps;
393        }
394 
395        /**
396         * Returns true if the year is a leap year in the Gregorian calendar
397         * 
398         * @param year
399         *            The year to consider
400         * @return True if <code>year</code> is a leap year
401         */
402        public static boolean isLeapYear(int year) {
403                return (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0));
404        }
405 
406        /**
407         * If the given object is a CalendarDate:
408         * <ol>
409         * <li>returns less than 0 if this date is before the given date
410         * <li>returns 0 if this date is equal to the given date
411         * <li>returns more than 0 if this date is after the given date
412         * </ol>
413         * 
414         * @param other
415         *            the date to compare this one to
416         * @throws ClassCastException
417         *             if <code>other</code> is not an instance of CalendarDate
418         * @see java.lang.Comparable#compareTo(java.lang.Object)
419         */
420        public int compareTo(Object other) {
421                return this.getDaysSinceEpoch() - ((CalendarDate) other).getDaysSinceEpoch();
422        }
423 
424        /**
425         * Returns true if this date is before the given date
426         * 
427         * @param other
428         *            The date to consider
429         * @return true if this date is before the given date
430         */
431        public boolean isBefore(CalendarDate other) {
432                return compareTo(other) < 0;
433        }
434 
435        /**
436         * Returns true if this date is after the given date
437         * 
438         * @param other
439         *            The date to consider
440         * @return true if this date is after the given date
441         */
442        public boolean isAfter(CalendarDate other) {
443                return compareTo(other) > 0;
444        }
445 
446        /**
447         * Returns true if the given object is a CalendarDate representing the same
448         * date as this object
449         * 
450         * @param other
451         *            The date to test against
452         * @return true if this is the same date as <code>other</code>
453         */
454        public boolean equals(Object other) {
455                if (other instanceof CalendarDate) {
456                        return (this.compareTo(other) == 0);
457                }
458                return false;
459        }
460 
461        public int hashCode() {
462                return (375 * getYear()) + (35 * getMonth()) + getDayOfMonth();
463        }
464 
465        /**
466         * Returns a string form of this date in the form "2004-9-23"
467         * 
468         * @return A string form of this date
469         */
470        public String toString() {
471                return new StringBuffer().append(getYear()).append("-").append(getMonth()).append("-")
472                                .append(getDayOfMonth()).toString();
473        }
474 
475        private void writeObject(java.io.ObjectOutputStream out) throws IOException {
476                out.writeInt(getYear());
477                out.writeInt(getMonth());
478                out.writeInt(getDayOfMonth());
479                out.writeInt(daysSinceEpoch);
480        }
481 
482        private void readObject(java.io.ObjectInputStream in) throws IOException,
483                        ClassNotFoundException {
484                init(in.readInt(), in.readInt(), in.readInt());
485                daysSinceEpoch = in.readInt();
486        }
487 
488        /**
489         * Returns the instant in time when this day begins in the given timezone.
490         * 
491         * @param timezone
492         *            The timezone to consider
493         * @return the instant in time when this day begins
494         */
495        public Date toDate(TimeZone timezone) {
496                return toDate(timezone, 0, 0, 0);
497        }
498 
499        /**
500         * Returns the instant in time when the given time of day is reached in the
501         * given timezone. If the time occurs twice on this date (as may happen when
502         * coming out of daylight savings time) the second occurrence will be
503         * returned.
504         * 
505         * @param timezone
506         *            The timezone to for which the date applies
507         * @param hour
508         *            The hour of the day in range 0 - 23
509         * @param min
510         *            The minute of the day in range 0 - 59
511         * @param sec
512         *            The second of the day in range 0 - 60 (60 is for leap-seconds)
513         * @return The instant in time when the given time of day is reached in the
514         *         given timezone
515         * @throws IllegalArgumentException
516         *             if the hour, min or sec parameters are outside the correct
517         *             range
518         */
519        public Date toDate(TimeZone timezone, int hour, int min, int sec) {
520                checkHourMinSec(hour, min, sec);
521                GregorianCalendar cal = new GregorianCalendar(timezone);
522                cal.clear();
523                cal.set(getYear(), getMonth() - 1, getDayOfMonth(), hour, min, sec);
524                return cal.getTime();
525        }
526 
527        private void checkHourMinSec(int hour, int min, int sec) {
528                if ((hour < 0) || (hour >= 24)) {
529                        throw new IllegalArgumentException("Hour out of range: " + hour);
530                }
531                if ((min < 0) || (min >= 60)) {
532                        throw new IllegalArgumentException("Minute out of range: " + min);
533                }
534                // Leap seconds mean some minutes are 61 seconds long!
535                if ((sec < 0) || (sec >= 61)) {
536                        throw new IllegalArgumentException("Second out of range: " + hour);
537                }
538        }
539 
540        /**
541         * Returns the number of days in the given month.
542         * 
543         * @param year
544         *            The year
545         * @param month
546         *            The month in range 1 - 12
547         * @return The number of days in the given month
548         */
549        public static int daysInMonth(int year, int month) {
550                int result = daysInMonth[month - 1];
551                if ((month == 2) && (isLeapYear(year))) {
552                        result++;
553                }
554                return result;
555        }
556 
557        /**
558         * Adds a given number of months to the date while attempting to keep the
559         * day of the month. Note that this method is NOT transitive. For example:
560         * <ul>
561         * <li><code>new CalendarDate(2005, 12, 31).addMonths(2).addMonths(1)</code>
562         * results in "March 28, 2006"; but,
563         * <li><code>new CalendarDate(2005, 12, 31).addMonths(3)</code> results
564         * in "March 31, 2006"
565         * </ul>
566         * 
567         * @param numMonths
568         *            the number of months to be added (can be negative)
569         */
570        public CalendarDate addMonths(int numMonths) {
571                int newMonthsSinceEpoch = getYear() * 12 + getMonth() + numMonths - 1;
572                int newYear = newMonthsSinceEpoch / 12;
573                int newMonth = (newMonthsSinceEpoch % 12) + 1;
574                int newDay = Math.min(getDayOfMonth(), daysInMonth(newYear, newMonth));
575                return new CalendarDate(newYear, newMonth, newDay);
576        }
577 
578}

[all classes][net.sourceforge.calendardate]
EMMA 2.0.4217 (C) Vladimir Roubtsov