KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > date > SpreadsheetDate


1 /* ========================================================================
2  * JCommon : a free general purpose class library for the Java(tm) platform
3  * ========================================================================
4  *
5  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jcommon/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * --------------------
28  * SpreadsheetDate.java
29  * --------------------
30  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): -;
34  *
35  * $Id: SpreadsheetDate.java,v 1.10 2006/08/29 13:59:30 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 11-Oct-2001 : Version 1 (DG);
40  * 05-Nov-2001 : Added getDescription() and setDescription() methods (DG);
41  * 12-Nov-2001 : Changed name from ExcelDate.java to SpreadsheetDate.java (DG);
42  * Fixed a bug in calculating day, month and year from serial
43  * number (DG);
44  * 24-Jan-2002 : Fixed a bug in calculating the serial number from the day,
45  * month and year. Thanks to Trevor Hills for the report (DG);
46  * 29-May-2002 : Added equals(Object) method (SourceForge ID 558850) (DG);
47  * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
48  * 13-Mar-2003 : Implemented Serializable (DG);
49  * 04-Sep-2003 : Completed isInRange() methods (DG);
50  * 05-Sep-2003 : Implemented Comparable (DG);
51  * 21-Oct-2003 : Added hashCode() method (DG);
52  * 29-Aug-2006 : Removed redundant description attribute (DG);
53  *
54  */

55
56 package org.jfree.date;
57
58 import java.util.Calendar JavaDoc;
59 import java.util.Date JavaDoc;
60
61 /**
62  * Represents a date using an integer, in a similar fashion to the
63  * implementation in Microsoft Excel. The range of dates supported is
64  * 1-Jan-1900 to 31-Dec-9999.
65  * <P>
66  * Be aware that there is a deliberate bug in Excel that recognises the year
67  * 1900 as a leap year when in fact it is not a leap year. You can find more
68  * information on the Microsoft website in article Q181370:
69  * <P>
70  * http://support.microsoft.com/support/kb/articles/Q181/3/70.asp
71  * <P>
72  * Excel uses the convention that 1-Jan-1900 = 1. This class uses the
73  * convention 1-Jan-1900 = 2.
74  * The result is that the day number in this class will be different to the
75  * Excel figure for January and February 1900...but then Excel adds in an extra
76  * day (29-Feb-1900 which does not actually exist!) and from that point forward
77  * the day numbers will match.
78  *
79  * @author David Gilbert
80  */

81 public class SpreadsheetDate extends SerialDate {
82
83     /** For serialization. */
84     private static final long serialVersionUID = -2039586705374454461L;
85     
86     /**
87      * The day number (1-Jan-1900 = 2, 2-Jan-1900 = 3, ..., 31-Dec-9999 =
88      * 2958465).
89      */

90     private final int serial;
91
92     /** The day of the month (1 to 28, 29, 30 or 31 depending on the month). */
93     private final int day;
94
95     /** The month of the year (1 to 12). */
96     private final int month;
97
98     /** The year (1900 to 9999). */
99     private final int year;
100
101     /**
102      * Creates a new date instance.
103      *
104      * @param day the day (in the range 1 to 28/29/30/31).
105      * @param month the month (in the range 1 to 12).
106      * @param year the year (in the range 1900 to 9999).
107      */

108     public SpreadsheetDate(final int day, final int month, final int year) {
109
110         if ((year >= 1900) && (year <= 9999)) {
111             this.year = year;
112         }
113         else {
114             throw new IllegalArgumentException JavaDoc(
115                 "The 'year' argument must be in range 1900 to 9999."
116             );
117         }
118
119         if ((month >= MonthConstants.JANUARY)
120                 && (month <= MonthConstants.DECEMBER)) {
121             this.month = month;
122         }
123         else {
124             throw new IllegalArgumentException JavaDoc(
125                 "The 'month' argument must be in the range 1 to 12."
126             );
127         }
128
129         if ((day >= 1) && (day <= SerialDate.lastDayOfMonth(month, year))) {
130             this.day = day;
131         }
132         else {
133             throw new IllegalArgumentException JavaDoc("Invalid 'day' argument.");
134         }
135
136         // the serial number needs to be synchronised with the day-month-year...
137
this.serial = calcSerial(day, month, year);
138
139     }
140
141     /**
142      * Standard constructor - creates a new date object representing the
143      * specified day number (which should be in the range 2 to 2958465.
144      *
145      * @param serial the serial number for the day (range: 2 to 2958465).
146      */

147     public SpreadsheetDate(final int serial) {
148
149         if ((serial >= SERIAL_LOWER_BOUND) && (serial <= SERIAL_UPPER_BOUND)) {
150             this.serial = serial;
151         }
152         else {
153             throw new IllegalArgumentException JavaDoc(
154                 "SpreadsheetDate: Serial must be in range 2 to 2958465.");
155         }
156
157         // the day-month-year needs to be synchronised with the serial number...
158
// get the year from the serial date
159
final int days = this.serial - SERIAL_LOWER_BOUND;
160       // overestimated because we ignored leap days
161
final int overestimatedYYYY = 1900 + (days / 365);
162       final int leaps = SerialDate.leapYearCount(overestimatedYYYY);
163       final int nonleapdays = days - leaps;
164       // underestimated because we overestimated years
165
int underestimatedYYYY = 1900 + (nonleapdays / 365);
166
167       if (underestimatedYYYY == overestimatedYYYY) {
168           this.year = underestimatedYYYY;
169       }
170       else {
171           int ss1 = calcSerial(1, 1, underestimatedYYYY);
172           while (ss1 <= this.serial) {
173               underestimatedYYYY = underestimatedYYYY + 1;
174               ss1 = calcSerial(1, 1, underestimatedYYYY);
175           }
176           this.year = underestimatedYYYY - 1;
177       }
178
179       final int ss2 = calcSerial(1, 1, this.year);
180
181       int[] daysToEndOfPrecedingMonth
182           = AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH;
183
184       if (isLeapYear(this.year)) {
185           daysToEndOfPrecedingMonth
186               = LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH;
187       }
188
189       // get the month from the serial date
190
int mm = 1;
191       int sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1;
192       while (sss < this.serial) {
193           mm = mm + 1;
194           sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1;
195       }
196       this.month = mm - 1;
197
198       // what's left is d(+1);
199
this.day = this.serial - ss2
200                  - daysToEndOfPrecedingMonth[this.month] + 1;
201
202     }
203
204     /**
205      * Returns the serial number for the date, where 1 January 1900 = 2
206      * (this corresponds, almost, to the numbering system used in Microsoft
207      * Excel for Windows and Lotus 1-2-3).
208      *
209      * @return The serial number of this date.
210      */

211     public int toSerial() {
212         return this.serial;
213     }
214
215     /**
216      * Returns a <code>java.util.Date</code> equivalent to this date.
217      *
218      * @return The date.
219      */

220     public Date JavaDoc toDate() {
221         final Calendar JavaDoc calendar = Calendar.getInstance();
222         calendar.set(getYYYY(), getMonth() - 1, getDayOfMonth(), 0, 0, 0);
223         return calendar.getTime();
224     }
225
226     /**
227      * Returns the year (assume a valid range of 1900 to 9999).
228      *
229      * @return The year.
230      */

231     public int getYYYY() {
232         return this.year;
233     }
234
235     /**
236      * Returns the month (January = 1, February = 2, March = 3).
237      *
238      * @return The month of the year.
239      */

240     public int getMonth() {
241         return this.month;
242     }
243
244     /**
245      * Returns the day of the month.
246      *
247      * @return The day of the month.
248      */

249     public int getDayOfMonth() {
250         return this.day;
251     }
252
253     /**
254      * Returns a code representing the day of the week.
255      * <P>
256      * The codes are defined in the {@link SerialDate} class as:
257      * <code>SUNDAY</code>, <code>MONDAY</code>, <code>TUESDAY</code>,
258      * <code>WEDNESDAY</code>, <code>THURSDAY</code>, <code>FRIDAY</code>, and
259      * <code>SATURDAY</code>.
260      *
261      * @return A code representing the day of the week.
262      */

263     public int getDayOfWeek() {
264         return (this.serial + 6) % 7 + 1;
265     }
266
267     /**
268      * Tests the equality of this date with an arbitrary object.
269      * <P>
270      * This method will return true ONLY if the object is an instance of the
271      * {@link SerialDate} base class, and it represents the same day as this
272      * {@link SpreadsheetDate}.
273      *
274      * @param object the object to compare (<code>null</code> permitted).
275      *
276      * @return A boolean.
277      */

278     public boolean equals(final Object JavaDoc object) {
279
280         if (object instanceof SerialDate) {
281             final SerialDate s = (SerialDate) object;
282             return (s.toSerial() == this.toSerial());
283         }
284         else {
285             return false;
286         }
287
288     }
289
290     /**
291      * Returns a hash code for this object instance.
292      *
293      * @return A hash code.
294      */

295     public int hashCode() {
296         return toSerial();
297     }
298
299     /**
300      * Returns the difference (in days) between this date and the specified
301      * 'other' date.
302      *
303      * @param other the date being compared to.
304      *
305      * @return The difference (in days) between this date and the specified
306      * 'other' date.
307      */

308     public int compare(final SerialDate other) {
309         return this.serial - other.toSerial();
310     }
311
312     /**
313      * Implements the method required by the Comparable interface.
314      *
315      * @param other the other object (usually another SerialDate).
316      *
317      * @return A negative integer, zero, or a positive integer as this object
318      * is less than, equal to, or greater than the specified object.
319      */

320     public int compareTo(final Object JavaDoc other) {
321         return compare((SerialDate) other);
322     }
323     
324     /**
325      * Returns true if this SerialDate represents the same date as the
326      * specified SerialDate.
327      *
328      * @param other the date being compared to.
329      *
330      * @return <code>true</code> if this SerialDate represents the same date as
331      * the specified SerialDate.
332      */

333     public boolean isOn(final SerialDate other) {
334         return (this.serial == other.toSerial());
335     }
336
337     /**
338      * Returns true if this SerialDate represents an earlier date compared to
339      * the specified SerialDate.
340      *
341      * @param other the date being compared to.
342      *
343      * @return <code>true</code> if this SerialDate represents an earlier date
344      * compared to the specified SerialDate.
345      */

346     public boolean isBefore(final SerialDate other) {
347         return (this.serial < other.toSerial());
348     }
349
350     /**
351      * Returns true if this SerialDate represents the same date as the
352      * specified SerialDate.
353      *
354      * @param other the date being compared to.
355      *
356      * @return <code>true</code> if this SerialDate represents the same date
357      * as the specified SerialDate.
358      */

359     public boolean isOnOrBefore(final SerialDate other) {
360         return (this.serial <= other.toSerial());
361     }
362
363     /**
364      * Returns true if this SerialDate represents the same date as the
365      * specified SerialDate.
366      *
367      * @param other the date being compared to.
368      *
369      * @return <code>true</code> if this SerialDate represents the same date
370      * as the specified SerialDate.
371      */

372     public boolean isAfter(final SerialDate other) {
373         return (this.serial > other.toSerial());
374     }
375
376     /**
377      * Returns true if this SerialDate represents the same date as the
378      * specified SerialDate.
379      *
380      * @param other the date being compared to.
381      *
382      * @return <code>true</code> if this SerialDate represents the same date as
383      * the specified SerialDate.
384      */

385     public boolean isOnOrAfter(final SerialDate other) {
386         return (this.serial >= other.toSerial());
387     }
388
389     /**
390      * Returns <code>true</code> if this {@link SerialDate} is within the
391      * specified range (INCLUSIVE). The date order of d1 and d2 is not
392      * important.
393      *
394      * @param d1 a boundary date for the range.
395      * @param d2 the other boundary date for the range.
396      *
397      * @return A boolean.
398      */

399     public boolean isInRange(final SerialDate d1, final SerialDate d2) {
400         return isInRange(d1, d2, SerialDate.INCLUDE_BOTH);
401     }
402
403     /**
404      * Returns true if this SerialDate is within the specified range (caller
405      * specifies whether or not the end-points are included). The order of d1
406      * and d2 is not important.
407      *
408      * @param d1 one boundary date for the range.
409      * @param d2 a second boundary date for the range.
410      * @param include a code that controls whether or not the start and end
411      * dates are included in the range.
412      *
413      * @return <code>true</code> if this SerialDate is within the specified
414      * range.
415      */

416     public boolean isInRange(final SerialDate d1, final SerialDate d2,
417                              final int include) {
418         final int s1 = d1.toSerial();
419         final int s2 = d2.toSerial();
420         final int start = Math.min(s1, s2);
421         final int end = Math.max(s1, s2);
422         
423         final int s = toSerial();
424         if (include == SerialDate.INCLUDE_BOTH) {
425             return (s >= start && s <= end);
426         }
427         else if (include == SerialDate.INCLUDE_FIRST) {
428             return (s >= start && s < end);
429         }
430         else if (include == SerialDate.INCLUDE_SECOND) {
431             return (s > start && s <= end);
432         }
433         else {
434             return (s > start && s < end);
435         }
436     }
437
438     /**
439      * Calculate the serial number from the day, month and year.
440      * <P>
441      * 1-Jan-1900 = 2.
442      *
443      * @param d the day.
444      * @param m the month.
445      * @param y the year.
446      *
447      * @return the serial number from the day, month and year.
448      */

449     private int calcSerial(final int d, final int m, final int y) {
450         final int yy = ((y - 1900) * 365) + SerialDate.leapYearCount(y - 1);
451         int mm = SerialDate.AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[m];
452         if (m > MonthConstants.FEBRUARY) {
453             if (SerialDate.isLeapYear(y)) {
454                 mm = mm + 1;
455             }
456         }
457         final int dd = d;
458         return yy + mm + dd + 1;
459     }
460
461 }
462
Popular Tags