1 /* java.util.GregorianCalendar
2    Copyright (C) 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.util;
40 
41 /**
42  * This class represents the Gregorian calendar, that is used in most
43  * countries all over the world.  It does also handle the Julian calendar
44  * for dates smaller than the date of the change to the Gregorian calendar.
45  * This change date is different from country to country, you can set it with
46  * <code>setGregorianChange</code>
47  *
48  * The Gregorian calendar differs from the Julian calendar by a different
49  * leap year rule (no leap year every 100 years, except if year is divisible
50  * by 400).  The non existing days that were omited when the change took
51  * place are interpreted as gregorian date
52  *
53  * There are to eras available for the Gregorian calendar, namely BC and AD.
54  *
55  * @see Calendar
56  * @see TimeZone
57  */
58 public class GregorianCalendar extends Calendar
59 {
60   /**
61    * Constant representing the era BC (before Christ).
62    */
63   public static final int BC = 0;
64 
65   /**
66    * Constant representing the era AD (Anno Domini).
67    */
68   public static final int AD = 1;
69 
70   /**
71    * The point at which the Gregorian calendar rules were used.
72    * This is locale dependent; the default for most catholic
73    * countries is midnight (UTC) on October 5, 1582 (Julian),
74    * or October 15, 1582 (Gregorian).
75    */
76   private long gregorianCutover;
77 
78   static final long serialVersionUID = -8125100834729963327L;
79 
80   /**
81    * The name of the resource bundle. Used only by getBundle()
82    */
83   private static final String bundleName = "gnu.java.locale.Calendar";
84 
85   /**
86    * get resource bundle:
87    * The resources should be loaded via this method only. Iff an application
88    * uses this method, the resourcebundle is required. --Fridi.
89    */
getBundle(Locale locale)90   private static ResourceBundle getBundle(Locale locale)
91   {
92     return ResourceBundle.getBundle(bundleName, locale);
93   }
94 
95   /**
96    * Constructs a new GregorianCalender representing the current
97    * time, using the default time zone and the default locale.
98    */
GregorianCalendar()99   public GregorianCalendar()
100   {
101     this(TimeZone.getDefault(), Locale.getDefault());
102   }
103 
104   /**
105    * Constructs a new GregorianCalender representing the current
106    * time, using the specified time zone and the default locale.
107    * @param zone a time zone.
108    */
GregorianCalendar(TimeZone zone)109   public GregorianCalendar(TimeZone zone)
110   {
111     this(zone, Locale.getDefault());
112   }
113 
114   /**
115    * Constructs a new GregorianCalender representing the current
116    * time, using the default time zone and the specified locale.
117    * @param locale a locale.
118    */
GregorianCalendar(Locale locale)119   public GregorianCalendar(Locale locale)
120   {
121     this(TimeZone.getDefault(), locale);
122   }
123 
124   /**
125    * Constructs a new GregorianCalender representing the current
126    * time with the given time zone and the given locale.
127    * @param zone a time zone.
128    * @param locale a locale.
129    */
GregorianCalendar(TimeZone zone, Locale locale)130   public GregorianCalendar(TimeZone zone, Locale locale)
131   {
132     super(zone, locale);
133     ResourceBundle rb = getBundle(locale);
134     gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime();
135     setTimeInMillis(System.currentTimeMillis());
136   }
137 
138   /**
139    * Constructs a new GregorianCalendar representing midnight on the
140    * given date with the default time zone and locale.
141    * @param year corresponds to the YEAR time field.
142    * @param month corresponds to the MONTH time field.
143    * @param day corresponds to the DAY time field.
144    */
GregorianCalendar(int year, int month, int day)145   public GregorianCalendar(int year, int month, int day)
146   {
147     super();
148     set(year, month, day);
149   }
150 
151   /**
152    * Constructs a new GregorianCalendar representing midnight on the
153    * given date with the default time zone and locale.
154    * @param year corresponds to the YEAR time field.
155    * @param month corresponds to the MONTH time field.
156    * @param day corresponds to the DAY time field.
157    * @param hour corresponds to the HOUR_OF_DAY time field.
158    * @param minute corresponds to the MINUTE time field.
159    */
GregorianCalendar(int year, int month, int day, int hour, int minute)160   public GregorianCalendar(int year, int month, int day, int hour, int minute)
161   {
162     super();
163     set(year, month, day, hour, minute);
164   }
165 
166   /**
167    * Constructs a new GregorianCalendar representing midnight on the
168    * given date with the default time zone and locale.
169    * @param year corresponds to the YEAR time field.
170    * @param month corresponds to the MONTH time field.
171    * @param day corresponds to the DAY time field.
172    * @param hour corresponds to the HOUR_OF_DAY time field.
173    * @param minute corresponds to the MINUTE time field.
174    * @param second corresponds to the SECOND time field.
175    */
GregorianCalendar(int year, int month, int day, int hour, int minute, int second)176   public GregorianCalendar(int year, int month, int day,
177 			   int hour, int minute, int second)
178   {
179     super();
180     set(year, month, day, hour, minute, second);
181   }
182 
183   /**
184    * Sets the date of the switch from Julian dates to Gregorian dates.
185    * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure
186    * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian
187    * calendar.
188    * @param date the date of the change.
189    */
setGregorianChange(Date date)190   public void setGregorianChange(Date date)
191   {
192     gregorianCutover = date.getTime();
193   }
194 
195   /**
196    * Gets the date of the switch from Julian dates to Gregorian dates.
197    * @return the date of the change.
198    */
getGregorianChange()199   public final Date getGregorianChange()
200   {
201     return new Date(gregorianCutover);
202   }
203 
204   /**
205    * Determines if the given year is a leap year.  The result is
206    * undefined if the gregorian change took place in 1800, so that
207    * the end of february is skiped and you give that year
208    * (well...).<br>
209    *
210    * The year should be positive and you can't give an ERA.  But
211    * remember that before 4 BC there wasn't a consistent leap year
212    * rule, so who cares.
213    *
214    * @param year a year use nonnegative value for BC.
215    * @return true, if the given year is a leap year, false otherwise.  */
isLeapYear(int year)216   public boolean isLeapYear(int year)
217   {
218     if ((year & 3) != 0)
219       // Only years divisible by 4 can be leap years
220       return false;
221 
222     // compute the linear day of the 29. February of that year.
223     // The 13 is the number of days, that were omitted in the Gregorian
224     // Calender until the epoch.
225     int julianDay = (((year-1) * (365*4+1)) >> 2) + (31+29 -
226         (((1970-1) * (365*4+1)) / 4 + 1 - 13));
227 
228     // If that day is smaller than the gregorianChange the julian
229     // rule applies:  This is a leap year since it is divisible by 4.
230     if (julianDay * (24 * 60 * 60 * 1000L) < gregorianCutover)
231       return true;
232 
233     return ((year % 100) != 0 || (year % 400) == 0);
234   }
235 
236   /**
237    * Get the linear time in milliseconds since the epoch.  If you
238    * specify a nonpositive year it is interpreted as BC as
239    * following: 0 is 1 BC, -1 is 2 BC and so on.  The date is
240    * interpreted as gregorian if the change occurred before that date.
241    *
242    * @param year the year of the date.
243    * @param dayOfYear the day of year of the date; 1 based.
244    * @param millis the millisecond in that day.
245    * @return the days since the epoch, may be negative.  */
getLinearTime(int year, int dayOfYear, int millis)246   private long getLinearTime(int year, int dayOfYear, int millis)
247   {
248     // The 13 is the number of days, that were omitted in the Gregorian
249     // Calender until the epoch.
250     // We shift right by 2 instead of dividing by 4, to get correct
251     // results for negative years (and this is even more efficient).
252     int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear -
253       ((1970 * (365 * 4 + 1)) / 4 + 1 - 13);
254     long time = julianDay * (24 * 60 * 60 * 1000L) + millis;
255 
256     if (time >= gregorianCutover)
257       {
258 	// subtract the days that are missing in gregorian calendar
259 	// with respect to julian calendar.
260 	//
261 	// Okay, here we rely on the fact that the gregorian
262 	// calendar was introduced in the AD era.  This doesn't work
263 	// with negative years.
264 	//
265 	// The additional leap year factor accounts for the fact that
266 	// a leap day is not seen on Jan 1 of the leap year.
267 	// And on and after the leap day, the leap day has already been
268 	// included in dayOfYear.
269 	int gregOffset = (year / 400) - (year / 100) + 2;
270 	if (isLeapYear (year, true))
271 	  --gregOffset;
272 	time += gregOffset * (24 * 60 * 60 * 1000L);
273       }
274     return time;
275   }
276 
getWeekDay(int year, int dayOfYear)277   private int getWeekDay(int year, int dayOfYear)
278   {
279     int day =
280       (int) (getLinearTime(year, dayOfYear, 0) / (24 * 60 * 60 * 1000L));
281 
282     // The epoch was a thursday.
283     int weekday = (day + THURSDAY) % 7;
284     if (weekday <= 0)
285       weekday += 7;
286     return weekday;
287   }
288 
289   /**
290    * Calculate the dayOfYear from the fields array.
291    * The relativeDays is used, to account for weeks that begin before
292    * the gregorian change and end after it.<br>
293    *
294    * We return two values, the first is used to determine, if we
295    * should use Gregorian calendar or Julian calendar, in case of
296    * the change year, the second is a relative day after the given
297    * day.  This is necessary for week calculation in the year in
298    * which gregorian change occurs. <br>
299    *
300    * @param year the year, negative for BC.
301    * @return an array of two int values, the first containing a reference
302    * day of current year, the second a relative count since this reference
303    * day.  */
getDayOfYear(int year)304   private int[] getDayOfYear(int year)
305   {
306     if (isSet[MONTH])
307       {
308 	int dayOfYear;
309 	if (fields[MONTH] > FEBRUARY)
310 	  {
311 
312 	    // The months after February are regular:
313 	    // 9 is an offset found by try and error.
314 	    dayOfYear = (fields[MONTH] * (31 + 30 + 31 + 30 + 31) - 9) / 5;
315 	    if (isLeapYear(year))
316 	      dayOfYear++;
317 	  }
318 	else
319 	    dayOfYear = 31 * fields[MONTH];
320 
321 	if (isSet[DAY_OF_MONTH])
322 	  {
323 	    return new int[]
324 	    {
325 	    dayOfYear + fields[DAY_OF_MONTH], 0};
326 	  }
327 	if (isSet[WEEK_OF_MONTH] && isSet[DAY_OF_WEEK])
328 	  {
329 	    // the weekday of the first day in that month is:
330 	    int weekday = getWeekDay(year, ++dayOfYear);
331 
332 	    return new int[]
333 	    {
334 	      dayOfYear,
335 		// the day of week in the first week
336 		// (weeks starting on sunday) is:
337 	      fields[DAY_OF_WEEK] - weekday +
338 		// Now jump to the right week and correct the possible
339 		// error made by assuming sunday is the first week day.
340 	      7 * (fields[WEEK_OF_MONTH]
341 		   + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1)
342 		   + (weekday < getFirstDayOfWeek()? -1 : 0))};
343 	  }
344 	if (isSet[DAY_OF_WEEK] && isSet[DAY_OF_WEEK_IN_MONTH])
345 	  {
346 	    // the weekday of the first day in that month is:
347 	    int weekday = getWeekDay(year, ++dayOfYear);
348 	    return new int[] {
349 		  dayOfYear,
350 		  fields[DAY_OF_WEEK] - weekday +
351 		  7 * (fields[DAY_OF_WEEK_IN_MONTH]
352 		       + (fields[DAY_OF_WEEK] < weekday ? 0 : -1))};
353 	  }
354       }
355 
356     // MONTH + something did not succeed.
357     if (isSet[DAY_OF_YEAR])
358       {
359 	return new int[] {0, fields[DAY_OF_YEAR]};
360       }
361 
362     if (isSet[DAY_OF_WEEK] && isSet[WEEK_OF_YEAR])
363       {
364 	int dayOfYear = getMinimalDaysInFirstWeek();
365 	// the weekday of the day, that begins the first week
366 	// in that year is:
367 	int weekday = getWeekDay(year, dayOfYear);
368 
369 	return new int[] {
370 	    dayOfYear,
371 	      // the day of week in the first week
372 	      // (weeks starting on sunday) is:
373 	    fields[DAY_OF_WEEK] - weekday
374 	      // Now jump to the right week and correct the possible
375 	      // error made by assuming sunday is the first week day.
376 	    + 7 * (fields[WEEK_OF_YEAR]
377 		   + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1)
378 		   + (weekday < getFirstDayOfWeek()? -1 : 0))};
379       }
380 
381     // As last resort return Jan, 1st.
382     return new int[] {1, 0};
383   }
384 
385   /**
386    * Converts the time field values (<code>fields</code>) to
387    * milliseconds since the epoch UTC (<code>time</code>).
388    */
computeTime()389   protected synchronized void computeTime()
390   {
391     int era = isSet[ERA] ? fields[ERA] : AD;
392     int year = isSet[YEAR] ? fields[YEAR] : 1970;
393     if (era == BC)
394       year = 1 - year;
395 
396     int[] daysOfYear = getDayOfYear(year);
397 
398     int hour = 0;
399     if (isSet[HOUR_OF_DAY])
400       hour = fields[HOUR_OF_DAY];
401     else if (isSet[HOUR])
402       {
403 	hour = fields[HOUR];
404         if (isSet[AM_PM] && fields[AM_PM] == PM)
405 	  if (hour != 12) /* not Noon */
406             hour += 12;
407 	/* Fix the problem of the status of 12:00 AM (midnight). */
408 	if (isSet[AM_PM] && fields[AM_PM] == AM && hour == 12)
409 	  hour = 0;
410       }
411 
412     int minute = isSet[MINUTE] ? fields[MINUTE] : 0;
413     int second = isSet[SECOND] ? fields[SECOND] : 0;
414     int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0;
415     int millisInDay;
416 
417     if (isLenient())
418       {
419 	// prevent overflow
420 	long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L
421 	  + millis;
422 	daysOfYear[1] += allMillis / (24 * 60 * 60 * 1000L);
423 	millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
424       }
425     else
426       {
427 	if (hour < 0 || hour >= 24 || minute < 0 || minute > 59
428 	    || second < 0 || second > 59 || millis < 0 || millis >= 1000)
429 	  throw new IllegalArgumentException();
430 	millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis;
431       }
432     time = getLinearTime(year, daysOfYear[0], millisInDay);
433 
434     // Add the relative days after calculating the linear time, to
435     // get right behaviour when jumping over the gregorianCutover.
436     time += daysOfYear[1] * (24 * 60 * 60 * 1000L);
437 
438 
439     TimeZone zone = getTimeZone();
440     int rawOffset = isSet[ZONE_OFFSET]
441       ? fields[ZONE_OFFSET] : zone.getRawOffset();
442 
443     int dayOfYear = daysOfYear[0] + daysOfYear[1];
444     int month = (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31);
445     int day = (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5;
446     int weekday = ((int) (time / (24 * 60 * 60 * 1000L)) + THURSDAY) % 7;
447     if (weekday <= 0)
448       weekday += 7;
449     int dstOffset = isSet[DST_OFFSET]
450       ? fields[DST_OFFSET] : (zone.getOffset((year < 0) ? BC : AD,
451 					     (year < 0) ? 1 - year : year,
452 					     month, day, weekday, millisInDay)
453 			      - zone.getRawOffset());
454     time -= rawOffset + dstOffset;
455     isTimeSet = true;
456   }
457 
458   /**
459    * Determines if the given year is a leap year.
460    *
461    * The year should be positive and you can't give an ERA.  But
462    * remember that before 4 BC there wasn't a consistent leap year
463    * rule, so who cares.
464    *
465    * @param year a year use nonnegative value for BC.
466    * @param gregorian if true, use gregorian leap year rule.
467    * @return true, if the given year is a leap year, false otherwise.  */
isLeapYear(int year, boolean gregorian)468   private boolean isLeapYear(int year, boolean gregorian)
469   {
470     if ((year & 3) != 0)
471       // Only years divisible by 4 can be leap years
472       return false;
473 
474     if (!gregorian)
475       return true;
476 
477     // We rely on AD area here.
478     return ((year % 100) != 0 || (year % 400) == 0);
479   }
480 
481   /**
482    * Get the linear day in days since the epoch, using the
483    * Julian or Gregorian calendar as specified.  If you specify a
484    * nonpositive year it is interpreted as BC as following: 0 is 1
485    * BC, -1 is 2 BC and so on.
486    *
487    * @param year the year of the date.
488    * @param dayOfYear the day of year of the date; 1 based.
489    * @param gregorian True, if we should use Gregorian rules.
490    * @return the days since the epoch, may be negative.  */
getLinearDay(int year, int dayOfYear, boolean gregorian)491   private int getLinearDay(int year, int dayOfYear, boolean gregorian)
492   {
493     // The 13 is the number of days, that were omitted in the Gregorian
494     // Calender until the epoch.
495     // We shift right by 2 instead of dividing by 4, to get correct
496     // results for negative years (and this is even more efficient).
497     int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear -
498       ((1970 * (365 * 4 + 1)) / 4 + 1 - 13);
499 
500     if (gregorian)
501       {
502 	// subtract the days that are missing in gregorian calendar
503 	// with respect to julian calendar.
504 	//
505 	// Okay, here we rely on the fact that the gregorian
506 	// calendar was introduced in the AD era.  This doesn't work
507 	// with negative years.
508 	//
509 	// The additional leap year factor accounts for the fact that
510 	// a leap day is not seen on Jan 1 of the leap year.
511 	int gregOffset = (year / 400) - (year / 100) + 2;
512 	if (isLeapYear (year, true) && dayOfYear < 31 + 29)
513 	  --gregOffset;
514 	julianDay += gregOffset;
515       }
516     return julianDay;
517   }
518 
519   /**
520    * Converts the given linear day into era, year, month,
521    * day_of_year, day_of_month, day_of_week, and writes the result
522    * into the fields array.
523    * @param day the linear day.
524    */
calculateDay(int day, boolean gregorian)525   private void calculateDay(int day, boolean gregorian)
526   {
527     // the epoch is a Thursday.
528     int weekday = (day + THURSDAY) % 7;
529     if (weekday <= 0)
530       weekday += 7;
531     fields[DAY_OF_WEEK] = weekday;
532 
533     // get a first approximation of the year.  This may be one
534     // year to big.
535     int year = 1970 + (gregorian
536 		       ? ((day - 100) * 400) / (365 * 400 + 100 - 4 + 1)
537 		       : ((day - 100) * 4) / (365 * 4 + 1));
538     if (day >= 0)
539       year++;
540 
541     int firstDayOfYear = getLinearDay(year, 1, gregorian);
542 
543     // Now look in which year day really lies.
544     if (day < firstDayOfYear)
545       {
546 	year--;
547 	firstDayOfYear = getLinearDay(year, 1, gregorian);
548       }
549 
550     day -= firstDayOfYear - 1;	// day of year,  one based.
551 
552     fields[DAY_OF_YEAR] = day;
553     if (year <= 0)
554       {
555 	fields[ERA] = BC;
556 	fields[YEAR] = 1 - year;
557       }
558     else
559       {
560 	fields[ERA] = AD;
561 	fields[YEAR] = year;
562       }
563 
564     int leapday = isLeapYear(year, gregorian) ? 1 : 0;
565     if (day <= 31 + 28 + leapday)
566       {
567 	fields[MONTH] = day / 32;	// 31->JANUARY, 32->FEBRUARY
568 	fields[DAY_OF_MONTH] = day - 31 * fields[MONTH];
569       }
570     else
571       {
572 	// A few more magic formulas
573 	int scaledDay = (day - leapday) * 5 + 8;
574 	fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
575 	fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
576       }
577   }
578 
579   /**
580    * Converts the milliseconds since the epoch UTC
581    * (<code>time</code>) to time fields
582    * (<code>fields</code>).
583    */
computeFields()584   protected synchronized void computeFields()
585   {
586     boolean gregorian = (time >= gregorianCutover);
587 
588     TimeZone zone = getTimeZone();
589     fields[ZONE_OFFSET] = zone.getRawOffset();
590     long localTime = time + fields[ZONE_OFFSET];
591 
592     int day = (int) (localTime / (24 * 60 * 60 * 1000L));
593     int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
594     if (millisInDay < 0)
595       {
596 	millisInDay += (24 * 60 * 60 * 1000);
597 	day--;
598       }
599 
600     calculateDay(day, gregorian);
601     fields[DST_OFFSET] =
602       zone.getOffset(fields[ERA], fields[YEAR], fields[MONTH],
603 		     fields[DAY_OF_MONTH], fields[DAY_OF_WEEK],
604 		     millisInDay) - fields[ZONE_OFFSET];
605 
606     millisInDay += fields[DST_OFFSET];
607     if (millisInDay >= 24 * 60 * 60 * 1000)
608       {
609 	millisInDay -= 24 * 60 * 60 * 1000;
610 	calculateDay(++day, gregorian);
611       }
612 
613     fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
614 
615     // which day of the week are we (0..6), relative to getFirstDayOfWeek
616     int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7;
617 
618     fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] - relativeWeekday + 12) / 7;
619 
620     int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7;
621 
622     // Do the Correction: getMinimalDaysInFirstWeek() is always in the
623     // first week.
624     int minDays = getMinimalDaysInFirstWeek();
625     int firstWeekday =
626       (7 + getWeekDay(fields[YEAR], minDays) - getFirstDayOfWeek()) % 7;
627     if (minDays - firstWeekday < 1)
628       weekOfYear++;
629     fields[WEEK_OF_YEAR] = weekOfYear;
630 
631 
632     int hourOfDay = millisInDay / (60 * 60 * 1000);
633     fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
634     int hour = hourOfDay % 12;
635     fields[HOUR] = (hour == 0) ? 12 : hour;
636     fields[HOUR_OF_DAY] = hourOfDay;
637     millisInDay %= (60 * 60 * 1000);
638     fields[MINUTE] = millisInDay / (60 * 1000);
639     millisInDay %= (60 * 1000);
640     fields[SECOND] = millisInDay / (1000);
641     fields[MILLISECOND] = millisInDay % 1000;
642 
643 
644     areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] =
645       isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] =
646       isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] =
647       isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] =
648       isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] =
649       isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
650 
651   }
652 
653   /**
654    * Compares the given calender with this.
655    * @param o the object to that we should compare.
656    * @return true, if the given object is a calendar, that represents
657    * the same time (but doesn't necessary have the same fields).
658    * @XXX Should we check if time zones, locale, cutover etc. are equal?
659    */
equals(Object o)660   public boolean equals(Object o)
661   {
662     if (!(o instanceof GregorianCalendar))
663       return false;
664 
665     GregorianCalendar cal = (GregorianCalendar) o;
666     return (cal.getTimeInMillis() == getTimeInMillis());
667   }
668 
669 //     /**
670 //      * Compares the given calender with this.
671 //      * @param o the object to that we should compare.
672 //      * @return true, if the given object is a calendar, and this calendar
673 //      * represents a smaller time than the calender o.
674 //      */
675 //     public boolean before(Object o) {
676 //         if (!(o instanceof GregorianCalendar))
677 //             return false;
678 
679 //         GregorianCalendar cal = (GregorianCalendar) o;
680 //         return (cal.getTimeInMillis() < getTimeInMillis());
681 //     }
682 
683 //     /**
684 //      * Compares the given calender with this.
685 //      * @param o the object to that we should compare.
686 //      * @return true, if the given object is a calendar, and this calendar
687 //      * represents a bigger time than the calender o.
688 //      */
689 //     public boolean after(Object o) {
690 //         if (!(o instanceof GregorianCalendar))
691 //             return false;
692 
693 //         GregorianCalendar cal = (GregorianCalendar) o;
694 //         return (cal.getTimeInMillis() > getTimeInMillis());
695 //     }
696 
697   /**
698    * Adds the specified amount of time to the given time field.  The
699    * amount may be negative to subtract the time.  If the field overflows
700    * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
701    * @param field the time field. One of the time field constants.
702    * @param amount the amount of time.
703    */
add(int field, int amount)704   public void add(int field, int amount)
705   {
706     switch (field)
707       {
708       case YEAR:
709 	complete();
710 	fields[YEAR] += amount;
711 	isTimeSet = false;
712 	break;
713       case MONTH:
714 	complete();
715 	int months = fields[MONTH] + amount;
716 	fields[YEAR] += months / 12;
717 	fields[MONTH] = months % 12;
718 	if (fields[MONTH] < 0)
719 	  {
720 	    fields[MONTH] += 12;
721 	    fields[YEAR]--;
722 	  }
723 	isTimeSet = false;
724 	int maxDay = getActualMaximum(DAY_OF_MONTH);
725 	if (fields[DAY_OF_MONTH] > maxDay)
726 	  {
727 	    fields[DAY_OF_MONTH] = maxDay;
728 	    isTimeSet = false;
729 	  }
730 	break;
731       case DAY_OF_MONTH:
732       case DAY_OF_YEAR:
733       case DAY_OF_WEEK:
734 	if (!isTimeSet)
735 	  computeTime();
736 	time += amount * (24 * 60 * 60 * 1000L);
737 	areFieldsSet = false;
738 	break;
739       case WEEK_OF_YEAR:
740       case WEEK_OF_MONTH:
741       case DAY_OF_WEEK_IN_MONTH:
742 	if (!isTimeSet)
743 	  computeTime();
744 	time += amount * (7 * 24 * 60 * 60 * 1000L);
745 	areFieldsSet = false;
746 	break;
747       case AM_PM:
748 	if (!isTimeSet)
749 	  computeTime();
750 	time += amount * (12 * 60 * 60 * 1000L);
751 	areFieldsSet = false;
752 	break;
753       case HOUR:
754       case HOUR_OF_DAY:
755 	if (!isTimeSet)
756 	  computeTime();
757 	time += amount * (60 * 60 * 1000L);
758 	areFieldsSet = false;
759 	break;
760       case MINUTE:
761 	if (!isTimeSet)
762 	  computeTime();
763 	time += amount * (60 * 1000L);
764 	areFieldsSet = false;
765 	break;
766       case SECOND:
767 	if (!isTimeSet)
768 	  computeTime();
769 	time += amount * (1000L);
770 	areFieldsSet = false;
771 	break;
772       case MILLISECOND:
773 	if (!isTimeSet)
774 	  computeTime();
775 	time += amount;
776 	areFieldsSet = false;
777 	break;
778       case ZONE_OFFSET:
779 	complete();
780 	fields[ZONE_OFFSET] += amount;
781 	time -= amount;
782 	break;
783       case DST_OFFSET:
784 	complete();
785 	fields[DST_OFFSET] += amount;
786 	isTimeSet = false;
787 	break;
788       default:
789 	throw new IllegalArgumentException
790 	  ("Unknown Calendar field: " + field);
791       }
792   }
793 
794 
795   /**
796    * Rolls the specified time field up or down.  This means add one
797    * to the specified field, but don't change the other fields.  If
798    * the maximum for this field is reached, start over with the
799    * minimum value.
800    *
801    * <strong>Note:</strong> There may be situation, where the other
802    * fields must be changed, e.g rolling the month on May, 31.
803    * The date June, 31 is automatically converted to July, 1.
804    * This requires lenient settings.
805    *
806    * @param field the time field. One of the time field constants.
807    * @param up the direction, true for up, false for down.
808    */
roll(int field, boolean up)809   public void roll(int field, boolean up)
810   {
811     roll(field, up ? 1 : -1);
812   }
813 
cleanUpAfterRoll(int field, int delta)814   private void cleanUpAfterRoll(int field, int delta)
815   {
816     switch (field)
817       {
818       case ERA:
819       case YEAR:
820       case MONTH:
821 	// check that day of month is still in correct range
822 	if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH))
823 	  fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH);
824 	isTimeSet = false;
825 	isSet[WEEK_OF_MONTH] = false;
826 	isSet[DAY_OF_WEEK] = false;
827 	isSet[DAY_OF_WEEK_IN_MONTH] = false;
828 	isSet[DAY_OF_YEAR] = false;
829 	isSet[WEEK_OF_YEAR] = false;
830 	break;
831 
832       case DAY_OF_MONTH:
833 	isSet[WEEK_OF_MONTH] = false;
834 	isSet[DAY_OF_WEEK] = false;
835 	isSet[DAY_OF_WEEK_IN_MONTH] = false;
836 	isSet[DAY_OF_YEAR] = false;
837 	isSet[WEEK_OF_YEAR] = false;
838 	time += delta * (24 * 60 * 60 * 1000L);
839 	break;
840 
841       case WEEK_OF_MONTH:
842 	isSet[DAY_OF_MONTH] = false;
843 	isSet[DAY_OF_WEEK_IN_MONTH] = false;
844 	isSet[DAY_OF_YEAR] = false;
845 	isSet[WEEK_OF_YEAR] = false;
846 	time += delta * (7 * 24 * 60 * 60 * 1000L);
847 	break;
848       case DAY_OF_WEEK_IN_MONTH:
849 	isSet[DAY_OF_MONTH] = false;
850 	isSet[WEEK_OF_MONTH] = false;
851 	isSet[DAY_OF_YEAR] = false;
852 	isSet[WEEK_OF_YEAR] = false;
853 	time += delta * (7 * 24 * 60 * 60 * 1000L);
854 	break;
855       case DAY_OF_YEAR:
856 	isSet[MONTH] = false;
857 	isSet[DAY_OF_MONTH] = false;
858 	isSet[WEEK_OF_MONTH] = false;
859 	isSet[DAY_OF_WEEK_IN_MONTH] = false;
860 	isSet[DAY_OF_WEEK] = false;
861 	isSet[WEEK_OF_YEAR] = false;
862 	time += delta * (24 * 60 * 60 * 1000L);
863 	break;
864       case WEEK_OF_YEAR:
865 	isSet[MONTH] = false;
866 	isSet[DAY_OF_MONTH] = false;
867 	isSet[WEEK_OF_MONTH] = false;
868 	isSet[DAY_OF_WEEK_IN_MONTH] = false;
869 	isSet[DAY_OF_YEAR] = false;
870 	time += delta * (7 * 24 * 60 * 60 * 1000L);
871 	break;
872 
873       case AM_PM:
874 	isSet[HOUR_OF_DAY] = false;
875 	time += delta * (12 * 60 * 60 * 1000L);
876 	break;
877       case HOUR:
878 	isSet[HOUR_OF_DAY] = false;
879 	time += delta * (60 * 60 * 1000L);
880 	break;
881       case HOUR_OF_DAY:
882 	isSet[HOUR] = false;
883 	isSet[AM_PM] = false;
884 	time += delta * (60 * 60 * 1000L);
885 	break;
886 
887       case MINUTE:
888 	time += delta * (60 * 1000L);
889 	break;
890       case SECOND:
891 	time += delta * (1000L);
892 	break;
893       case MILLISECOND:
894 	time += delta;
895 	break;
896       }
897   }
898 
899   /**
900    * Rolls the specified time field by the given amount.  This means
901    * add amount to the specified field, but don't change the other
902    * fields.  If the maximum for this field is reached, start over
903    * with the minimum value and vice versa for negative amounts.
904    *
905    * <strong>Note:</strong> There may be situation, where the other
906    * fields must be changed, e.g rolling the month on May, 31.
907    * The date June, 31 is automatically corrected to June, 30.
908    *
909    * @param field the time field. One of the time field constants.
910    * @param amount the amount by which we should roll.
911    */
roll(int field, int amount)912   public void roll(int field, int amount)
913   {
914     switch (field)
915       {
916       case DAY_OF_WEEK:
917 	// day of week is special: it rolls automatically
918 	add(field, amount);
919 	return;
920       case ZONE_OFFSET:
921       case DST_OFFSET:
922 	throw new IllegalArgumentException("Can't roll time zone");
923       }
924     complete();
925     int min = getActualMinimum(field);
926     int range = getActualMaximum(field) - min + 1;
927     int oldval = fields[field];
928     int newval = (oldval - min + range + amount) % range + min;
929     if (newval < min)
930       newval += range;
931     fields[field] = newval;
932     cleanUpAfterRoll(field, newval - oldval);
933   }
934 
935   private static final int[] minimums =
936       { BC,       1,  0,  0, 1,  1,   1,   SUNDAY, 1,
937         AM,  1,  0,  1,  1,   1, -(12*60*60*1000),               0 };
938 
939   private static final int[] maximums =
940       { AD, 5000000, 11, 53, 5, 31, 366, SATURDAY, 5,
941         PM, 12, 23, 59, 59, 999, +(12*60*60*1000), (12*60*60*1000) };
942 
943   /**
944    * Gets the smallest value that is allowed for the specified field.
945    * @param field the time field. One of the time field constants.
946    * @return the smallest value.
947    */
getMinimum(int field)948   public int getMinimum(int field)
949   {
950     return minimums[field];
951   }
952 
953   /**
954    * Gets the biggest value that is allowed for the specified field.
955    * @param field the time field. One of the time field constants.
956    * @return the biggest value.
957    */
getMaximum(int field)958   public int getMaximum(int field)
959   {
960     return maximums[field];
961   }
962 
963 
964   /**
965    * Gets the greatest minimum value that is allowed for the specified field.
966    * @param field the time field. One of the time field constants.
967    * @return the greatest minimum value.
968    */
getGreatestMinimum(int field)969   public int getGreatestMinimum(int field)
970   {
971     if (field == WEEK_OF_YEAR)
972       return 1;
973     return minimums[field];
974   }
975 
976   /**
977    * Gets the smallest maximum value that is allowed for the
978    * specified field.  For example this is 28 for DAY_OF_MONTH.
979    * @param field the time field. One of the time field constants.
980    * @return the least maximum value.
981    * @since jdk1.2
982    */
getLeastMaximum(int field)983   public int getLeastMaximum(int field)
984   {
985     switch (field)
986       {
987       case WEEK_OF_YEAR:
988 	return 52;
989       case DAY_OF_MONTH:
990 	return 28;
991       case DAY_OF_YEAR:
992 	return 365;
993       case DAY_OF_WEEK_IN_MONTH:
994       case WEEK_OF_MONTH:
995 	return 4;
996       default:
997 	return maximums[field];
998       }
999   }
1000 
1001   /**
1002    * Gets the actual minimum value that is allowed for the specified field.
1003    * This value is dependent on the values of the other fields.  Note that
1004    * this calls <code>complete()</code> if not enough fields are set.  This
1005    * can have ugly side effects.
1006    * @param field the time field. One of the time field constants.
1007    * @return the actual minimum value.
1008    * @since jdk1.2
1009    */
getActualMinimum(int field)1010   public int getActualMinimum(int field)
1011   {
1012     if (field == WEEK_OF_YEAR)
1013       {
1014 	int min = getMinimalDaysInFirstWeek();
1015 	if (min == 0)
1016 	  return 1;
1017 	if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
1018 	  complete();
1019 
1020 	int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1021 	int weekday = getWeekDay(year, min);
1022 	if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1)
1023 	  return 1;
1024 	return 0;
1025       }
1026     return minimums[field];
1027   }
1028 
1029   /**
1030    * Gets the actual maximum value that is allowed for the specified field.
1031    * This value is dependent on the values of the other fields.  Note that
1032    * this calls <code>complete()</code> if not enough fields are set.  This
1033    * can have ugly side effects.
1034    * @param field the time field. One of the time field constants.
1035    * @return the actual maximum value.
1036    */
getActualMaximum(int field)1037   public int getActualMaximum(int field)
1038   {
1039     switch (field)
1040       {
1041       case WEEK_OF_YEAR:
1042 	{
1043 	  if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
1044 	    complete();
1045 	  // This is wrong for the year that contains the gregorian change.
1046 	  // I.e it gives the weeks in the julian year or in the gregorian
1047 	  // year in that case.
1048 	  int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1049 	  int lastDay = isLeapYear(year) ? 366 : 365;
1050 	  int weekday = getWeekDay(year, lastDay);
1051 	  int week = (lastDay + 6
1052 		      - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
1053 
1054 	  int minimalDays = getMinimalDaysInFirstWeek();
1055 	  int firstWeekday = getWeekDay(year, minimalDays);
1056 	  if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
1057 	    return week + 1;
1058 	}
1059 	case DAY_OF_MONTH:
1060 	{
1061 	  if (!areFieldsSet || !isSet[MONTH])
1062 	    complete();
1063 	  int month = fields[MONTH];
1064 	  // If you change this, you should also change
1065 	  // SimpleTimeZone.getDaysInMonth();
1066 	  if (month == FEBRUARY)
1067 	    {
1068 	      if (!isSet[YEAR] || !isSet[ERA])
1069 		complete();
1070 	      int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1071 	      return isLeapYear(year) ? 29 : 28;
1072 	    }
1073 	  else if (month < AUGUST)
1074 	    return 31 - (month & 1);
1075 	  else
1076 	    return 30 + (month & 1);
1077 	}
1078       case DAY_OF_YEAR:
1079 	{
1080 	  if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
1081 	    complete();
1082 	  int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1083 	  return isLeapYear(year) ? 366 : 365;
1084 	}
1085       case DAY_OF_WEEK_IN_MONTH:
1086 	{
1087 	  // This is wrong for the month that contains the gregorian change.
1088 	  int daysInMonth = getActualMaximum(DAY_OF_MONTH);
1089 	  // That's black magic, I know
1090 	  return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
1091 	}
1092       case WEEK_OF_MONTH:
1093 	{
1094 	  int daysInMonth = getActualMaximum(DAY_OF_MONTH);
1095 	  int weekday = (daysInMonth - fields[DAY_OF_MONTH]
1096 			 + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
1097 	  return (daysInMonth + 6
1098 		  - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
1099 	}
1100       default:
1101 	return maximums[field];
1102       }
1103   }
1104 }
1105