OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [java/] [util/] [GregorianCalendar.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* java.util.GregorianCalendar
2
   Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004
3
   Free Software Foundation, Inc.
4
 
5
This file is part of GNU Classpath.
6
 
7
GNU Classpath is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2, or (at your option)
10
any later version.
11
 
12
GNU Classpath is distributed in the hope that it will be useful, but
13
WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
General Public License for more details.
16
 
17
You should have received a copy of the GNU General Public License
18
along with GNU Classpath; see the file COPYING.  If not, write to the
19
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20
02110-1301 USA.
21
 
22
Linking this library statically or dynamically with other modules is
23
making a combined work based on this library.  Thus, the terms and
24
conditions of the GNU General Public License cover the whole
25
combination.
26
 
27
As a special exception, the copyright holders of this library give you
28
permission to link this library with independent modules to produce an
29
executable, regardless of the license terms of these independent
30
modules, and to copy and distribute the resulting executable under
31
terms of your choice, provided that you also meet, for each linked
32
independent module, the terms and conditions of the license of that
33
module.  An independent module is a module which is not derived from
34
or based on this library.  If you modify this library, you may extend
35
this exception to your version of the library, but you are not
36
obligated to do so.  If you do not wish to do so, delete this
37
exception statement from your version. */
38
 
39
 
40
package java.util;
41
 
42
 
43
/**
44
 * <p>
45
 * This class represents the Gregorian calendar, that is used in most
46
 * countries all over the world.  It does also handle the Julian calendar
47
 * for dates smaller than the date of the change to the Gregorian calendar.
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).
51
 * </p>
52
 * <p>
53
 * This change date is different from country to country, and can be changed with
54
 * <code>setGregorianChange</code>.  The first countries to adopt the Gregorian
55
 * calendar did so on the 15th of October, 1582.  This date followed October
56
 * the 4th, 1582 in the Julian calendar system.  The non-existant days that were
57
 * omitted when the change took place are interpreted as Gregorian dates.
58
 * </p>
59
 * <p>
60
 * Prior to the changeover date, New Year's Day occurred on the 25th of March.
61
 * However, this class always takes New Year's Day as being the 1st of January.
62
 * Client code should manually adapt the year value, if required, for dates
63
 * between January the 1st and March the 24th in years prior to the changeover.
64
 * </p>
65
 * <p>
66
 * Any date infinitely forwards or backwards in time can be represented by
67
 * this class.  A <em>proleptic</em> calendar system is used, which allows
68
 * future dates to be created via the existing rules.  This allows meaningful
69
 * and consistent dates to be produced for all years.  However, dates are only
70
 * historically accurate following March the 1st, 4AD when the Julian calendar
71
 * system was adopted.  Prior to this, leap year rules were applied erraticly.
72
 * </p>
73
 * <p>
74
 * There are two eras available for the Gregorian calendar, namely BC and AD.
75
 * </p>
76
 * <p>
77
 * Weeks are defined as a period of seven days, beginning on the first day
78
 * of the week, as returned by <code>getFirstDayOfWeek()</code>, and ending
79
 * on the day prior to this.
80
 * </p>
81
 * <p>
82
 * The weeks of the year are numbered from 1 to a possible 53.  The first week
83
 * of the year is defined as the first week that contains at least the minimum
84
 * number of days of the first week in the new year (retrieved via
85
 * <code>getMinimalDaysInFirstWeek()</code>).  All weeks after this are numbered
86
 * from 2 onwards.
87
 * </p>
88
 * <p>
89
 * For example, take the year 2004.  It began on a Thursday.  The first week
90
 * of 2004 depends both on where a week begins and how long it must minimally
91
 * last.  Let's say that the week begins on a Monday and must have a minimum
92
 * of 5 days.  In this case, the first week begins on Monday, the 5th of January.
93
 * The first 4 days (Thursday to Sunday) are not eligible, as they are too few
94
 * to make up the minimum number of days of the first week which must be in
95
 * the new year.  If the minimum was lowered to 4 days, then the first week
96
 * would instead begin on Monday, the 29th of December, 2003.  This first week
97
 * has 4 of its days in the new year, and is now eligible.
98
 * </p>
99
 * <p>
100
 * The weeks of the month are numbered from 0 to a possible 6.  The first week
101
 * of the month (numbered 1) is a set of days, prior to the first day of the week,
102
 * which number at least the minimum number of days in a week.  Unlike the first
103
 * week of the year, the first week of the month only uses days from that particular
104
 * month.  As a consequence, it may have a variable number of days (from the minimum
105
 * number required up to a full week of 7) and it need not start on the first day of
106
 * the week.  It must, however, be following by the first day of the week, as this
107
 * marks the beginning of week 2.  Any days of the month which occur prior to the
108
 * first week (because the first day of the week occurs before the minimum number
109
 * of days is met) are seen as week 0.
110
 * </p>
111
 * <p>
112
 * Again, we will take the example of the year 2004 to demonstrate this.  September
113
 * 2004 begins on a Wednesday.  Taking our first day of the week as Monday, and the
114
 * minimum length of the first week as 6, we find that week 1 runs from Monday,
115
 * the 6th of September to Sunday the 12th.  Prior to the 6th, there are only
116
 * 5 days (Wednesday through to Sunday).  This is too small a number to meet the
117
 * minimum, so these are classed as being days in week 0.  Week 2 begins on the
118
 * 13th, and so on.  This changes if we reduce the minimum to 5.  In this case,
119
 * week 1 is a truncated week from Wednesday the 1st to Sunday the 5th, and week
120
 * 0 doesn't exist.  The first seven day week is week 2, starting on the 6th.
121
 * </p>
122
 * <p>
123
 * On using the <code>clear()</code> method, the Gregorian calendar returns
124
 * to its default value of the 1st of January, 1970 AD 00:00:00 (the epoch).
125
 * The day of the week is set to the correct day for that particular time.
126
 * The day is also the first of the month, and the date is in week 0.
127
 * </p>
128
 *
129
 * @see Calendar
130
 * @see TimeZone
131
 * @see Calendar#getFirstDayOfWeek()
132
 * @see Calendar#getMinimalDaysInFirstWeek()
133
 */
134
public class GregorianCalendar extends Calendar
135
{
136
  /**
137
   * Constant representing the era BC (Before Christ).
138
   */
139
  public static final int BC = 0;
140
 
141
  /**
142
   * Constant representing the era AD (Anno Domini).
143
   */
144
  public static final int AD = 1;
145
 
146
  /**
147
   * The point at which the Gregorian calendar rules were used.
148
   * This may be changed by using setGregorianChange;
149
   * The default is midnight (UTC) on October 5, 1582 (Julian),
150
   * or October 15, 1582 (Gregorian).
151
   *
152
   * @serial the changeover point from the Julian calendar
153
   *         system to the Gregorian.
154
   */
155
  private long gregorianCutover = (new Date((24 * 60 * 60 * 1000L) * (((1582 * (365 * 4
156
                                            + 1)) / 4
157
                                            + (java.util.Calendar.OCTOBER * (31
158
                                            + 30 + 31 + 30 + 31) - 9) / 5 + 5)
159
                                            - ((1970 * (365 * 4 + 1)) / 4 + 1
160
                                            - 13)))).getTime();
161
 
162
  /**
163
   * For compatability with Sun's JDK.
164
   */
165
  static final long serialVersionUID = -8125100834729963327L;
166
 
167
  /**
168
   * Days in the epoch. Relative Jan 1, year '0' which is not a leap year.
169
   * (although there is no year zero, this does not matter.)
170
   * This is consistent with the formula:
171
   * = (year-1)*365L + ((year-1) >> 2)
172
   *
173
   * Plus the gregorian correction:
174
   *  Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.);
175
   * For a correct julian date, the correction is -2 instead.
176
   *
177
   * The gregorian cutover in 1582 was 10 days, so by calculating the
178
   * correction from year zero, we have 15 non-leap days (even centuries)
179
   * minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects
180
   * this to the correct number 10.
181
   */
182
  private static final int EPOCH_DAYS = 719162;
183
 
184
  /**
185
   * Constructs a new GregorianCalender representing the current
186
   * time, using the default time zone and the default locale.
187
   */
188
  public GregorianCalendar()
189
  {
190
    this(TimeZone.getDefault(), Locale.getDefault());
191
  }
192
 
193
  /**
194
   * Constructs a new GregorianCalender representing the current
195
   * time, using the specified time zone and the default locale.
196
   *
197
   * @param zone a time zone.
198
   */
199
  public GregorianCalendar(TimeZone zone)
200
  {
201
    this(zone, Locale.getDefault());
202
  }
203
 
204
  /**
205
   * Constructs a new GregorianCalender representing the current
206
   * time, using the default time zone and the specified locale.
207
   *
208
   * @param locale a locale.
209
   */
210
  public GregorianCalendar(Locale locale)
211
  {
212
    this(TimeZone.getDefault(), locale);
213
  }
214
 
215
  /**
216
   * Constructs a new GregorianCalender representing the current
217
   * time with the given time zone and the given locale.
218
   *
219
   * @param zone a time zone.
220
   * @param locale a locale.
221
   */
222
  public GregorianCalendar(TimeZone zone, Locale locale)
223
  {
224
    this(zone, locale, false);
225
    setTimeInMillis(System.currentTimeMillis());
226
    complete();
227
  }
228
 
229
  /**
230
   * Common constructor that all constructors should call.
231
   * @param zone a time zone.
232
   * @param locale a locale.
233
   * @param unused unused parameter to make the signature differ from
234
   * the public constructor (TimeZone, Locale).
235
   */
236
  private GregorianCalendar(TimeZone zone, Locale locale, boolean unused)
237
  {
238
    super(zone, locale);
239
  }
240
 
241
  /**
242
   * Constructs a new GregorianCalendar representing midnight on the
243
   * given date with the default time zone and locale.
244
   *
245
   * @param year corresponds to the YEAR time field.
246
   * @param month corresponds to the MONTH time field.
247
   * @param day corresponds to the DAY time field.
248
   */
249
  public GregorianCalendar(int year, int month, int day)
250
  {
251
    this(TimeZone.getDefault(), Locale.getDefault(), false);
252
    set(year, month, day);
253
  }
254
 
255
  /**
256
   * Constructs a new GregorianCalendar representing midnight on the
257
   * given date with the default time zone and locale.
258
   *
259
   * @param year corresponds to the YEAR time field.
260
   * @param month corresponds to the MONTH time field.
261
   * @param day corresponds to the DAY time field.
262
   * @param hour corresponds to the HOUR_OF_DAY time field.
263
   * @param minute corresponds to the MINUTE time field.
264
   */
265
  public GregorianCalendar(int year, int month, int day, int hour, int minute)
266
  {
267
    this(TimeZone.getDefault(), Locale.getDefault(), false);
268
    set(year, month, day, hour, minute);
269
  }
270
 
271
  /**
272
   * Constructs a new GregorianCalendar representing midnight on the
273
   * given date with the default time zone and locale.
274
   *
275
   * @param year corresponds to the YEAR time field.
276
   * @param month corresponds to the MONTH time field.
277
   * @param day corresponds to the DAY time field.
278
   * @param hour corresponds to the HOUR_OF_DAY time field.
279
   * @param minute corresponds to the MINUTE time field.
280
   * @param second corresponds to the SECOND time field.
281
   */
282
  public GregorianCalendar(int year, int month, int day, int hour, int minute,
283
                           int second)
284
  {
285
    this(TimeZone.getDefault(), Locale.getDefault(), false);
286
    set(year, month, day, hour, minute, second);
287
  }
288
 
289
  /**
290
   * Sets the date of the switch from Julian dates to Gregorian dates.
291
   * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure
292
   * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian
293
   * calendar.
294
   *
295
   * @param date the date of the change.
296
   */
297
  public void setGregorianChange(Date date)
298
  {
299
    gregorianCutover = date.getTime();
300
  }
301
 
302
  /**
303
   * Gets the date of the switch from Julian dates to Gregorian dates.
304
   *
305
   * @return the date of the change.
306
   */
307
  public final Date getGregorianChange()
308
  {
309
    return new Date(gregorianCutover);
310
  }
311
 
312
  /**
313
   * <p>
314
   * Determines if the given year is a leap year.  The result is
315
   * undefined if the Gregorian change took place in 1800, so that
316
   * the end of February is skipped, and that year is specified.
317
   * (well...).
318
   * </p>
319
   * <p>
320
   * To specify a year in the BC era, use a negative value calculated
321
   * as 1 - y, where y is the required year in BC.  So, 1 BC is 0,
322
   * 2 BC is -1, 3 BC is -2, etc.
323
   * </p>
324
   *
325
   * @param year a year (use a negative value for BC).
326
   * @return true, if the given year is a leap year, false otherwise.
327
   */
328
  public boolean isLeapYear(int year)
329
  {
330
    // Only years divisible by 4 can be leap years
331
    if ((year & 3) != 0)
332
      return false;
333
 
334
    // Is the leap-day a Julian date? Then it's a leap year
335
    if (! isGregorian(year, 31 + 29 - 1))
336
      return true;
337
 
338
    // Apply gregorian rules otherwise
339
    return ((year % 100) != 0 || (year % 400) == 0);
340
  }
341
 
342
  /**
343
   * Retrieves the day of the week corresponding to the specified
344
   * day of the specified year.
345
   *
346
   * @param year the year in which the dayOfYear occurs.
347
   * @param dayOfYear the day of the year (an integer between 0 and
348
   *        and 366)
349
   */
350
  private int getWeekDay(int year, int dayOfYear)
351
  {
352
    boolean greg = isGregorian(year, dayOfYear);
353
    int day = (int) getLinearDay(year, dayOfYear, greg);
354
 
355
    // The epoch was a thursday.
356
    int weekday = (day + THURSDAY) % 7;
357
    if (weekday <= 0)
358
      weekday += 7;
359
    return weekday;
360
  }
361
 
362
  /**
363
   * Returns the day of the week for the first day of a given month (0..11)
364
   */
365
  private int getFirstDayOfMonth(int year, int month)
366
  {
367
    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
368
 
369
    if (month > 11)
370
      {
371
        year += (month / 12);
372
        month = month % 12;
373
      }
374
 
375
    if (month < 0)
376
      {
377
        year += (int) month / 12;
378
        month = month % 12;
379
        if (month < 0)
380
          {
381
            month += 12;
382
            year--;
383
          }
384
      }
385
 
386
    int dayOfYear = dayCount[month] + 1;
387
    if (month > 1)
388
      if (isLeapYear(year))
389
        dayOfYear++;
390
 
391
    boolean greg = isGregorian(year, dayOfYear);
392
    int day = (int) getLinearDay(year, dayOfYear, greg);
393
 
394
    // The epoch was a thursday.
395
    int weekday = (day + THURSDAY) % 7;
396
    if (weekday <= 0)
397
      weekday += 7;
398
    return weekday;
399
  }
400
 
401
  /**
402
   * Takes a year, and a (zero based) day of year and determines
403
   * if it is gregorian or not.
404
   */
405
  private boolean isGregorian(int year, int dayOfYear)
406
  {
407
    int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
408
                      - EPOCH_DAYS; // gregorian days from 1 to epoch.
409
    int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
410
                     - (int) Math.floor((double) (year - 1) / 100.);
411
 
412
    return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover);
413
  }
414
 
415
  /**
416
   * Check set fields for validity, without leniency.
417
   *
418
   * @throws IllegalArgumentException if a field is invalid
419
   */
420
  private void nonLeniencyCheck() throws IllegalArgumentException
421
  {
422
    int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
423
    int year = fields[YEAR];
424
    int month = fields[MONTH];
425
    int leap = isLeapYear(year) ? 1 : 0;
426
 
427
    if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC)
428
      throw new IllegalArgumentException("Illegal ERA.");
429
    if (isSet[YEAR] && fields[YEAR] < 1)
430
      throw new IllegalArgumentException("Illegal YEAR.");
431
    if (isSet[MONTH] && (month < 0 || month > 11))
432
      throw new IllegalArgumentException("Illegal MONTH.");
433
    if (isSet[WEEK_OF_YEAR])
434
      {
435
        int daysInYear = 365 + leap;
436
        daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week
437
        int last = getFirstDayOfMonth(year, 11) + 4;
438
        if (last > 7)
439
          last -= 7;
440
        daysInYear += 7 - last;
441
        int weeks = daysInYear / 7;
442
        if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks)
443
          throw new IllegalArgumentException("Illegal WEEK_OF_YEAR.");
444
      }
445
 
446
    if (isSet[WEEK_OF_MONTH])
447
      {
448
        int weeks = (month == 1 && leap == 0) ? 4 : 5;
449
        if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks)
450
          throw new IllegalArgumentException("Illegal WEEK_OF_MONTH.");
451
      }
452
 
453
    if (isSet[DAY_OF_MONTH])
454
      if (fields[DAY_OF_MONTH] < 1
455
          || fields[DAY_OF_MONTH] > month_days[month]
456
          + ((month == 1) ? leap : 0))
457
        throw new IllegalArgumentException("Illegal DAY_OF_MONTH.");
458
 
459
    if (isSet[DAY_OF_YEAR]
460
        && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap))
461
      throw new IllegalArgumentException("Illegal DAY_OF_YEAR.");
462
 
463
    if (isSet[DAY_OF_WEEK]
464
        && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7))
465
      throw new IllegalArgumentException("Illegal DAY_OF_WEEK.");
466
 
467
    if (isSet[DAY_OF_WEEK_IN_MONTH])
468
      {
469
        int weeks = (month == 1 && leap == 0) ? 4 : 5;
470
        if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks
471
            || fields[DAY_OF_WEEK_IN_MONTH] > weeks)
472
          throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH.");
473
      }
474
 
475
    if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM)
476
      throw new IllegalArgumentException("Illegal AM_PM.");
477
    if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11))
478
      throw new IllegalArgumentException("Illegal HOUR.");
479
    if (isSet[HOUR_OF_DAY]
480
        && (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23))
481
      throw new IllegalArgumentException("Illegal HOUR_OF_DAY.");
482
    if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59))
483
      throw new IllegalArgumentException("Illegal MINUTE.");
484
    if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59))
485
      throw new IllegalArgumentException("Illegal SECOND.");
486
    if (isSet[MILLISECOND]
487
        && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999))
488
      throw new IllegalArgumentException("Illegal MILLISECOND.");
489
    if (isSet[ZONE_OFFSET]
490
        && (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L
491
        || fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L))
492
      throw new IllegalArgumentException("Illegal ZONE_OFFSET.");
493
    if (isSet[DST_OFFSET]
494
        && (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L
495
        || fields[DST_OFFSET] > 12 * 60 * 60 * 1000L))
496
      throw new IllegalArgumentException("Illegal DST_OFFSET.");
497
  }
498
 
499
  /**
500
   * Converts the time field values (<code>fields</code>) to
501
   * milliseconds since the epoch UTC (<code>time</code>).
502
   *
503
   * @throws IllegalArgumentException if any calendar fields
504
   *         are invalid.
505
   */
506
  protected synchronized void computeTime()
507
  {
508
    int millisInDay = 0;
509
    int era = fields[ERA];
510
    int year = fields[YEAR];
511
    int month = fields[MONTH];
512
    int day = fields[DAY_OF_MONTH];
513
 
514
    int minute = fields[MINUTE];
515
    int second = fields[SECOND];
516
    int millis = fields[MILLISECOND];
517
    int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
518
    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
519
    int hour = 0;
520
 
521
    if (! isLenient())
522
      nonLeniencyCheck();
523
 
524
    if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR]))
525
      {
526
        // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
527
        if (isSet[WEEK_OF_YEAR])
528
          {
529
            int first = getFirstDayOfMonth(year, 0);
530
            int offs = 1;
531
            int daysInFirstWeek = getFirstDayOfWeek() - first;
532
            if (daysInFirstWeek <= 0)
533
              daysInFirstWeek += 7;
534
 
535
            if (daysInFirstWeek < getMinimalDaysInFirstWeek())
536
              offs += daysInFirstWeek;
537
            else
538
              offs -= 7 - daysInFirstWeek;
539
            month = 0;
540
            day = offs + 7 * (fields[WEEK_OF_YEAR] - 1);
541
            offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
542
 
543
            if (offs < 0)
544
              offs += 7;
545
            day += offs;
546
          }
547
        else
548
          {
549
            // 4:  YEAR + DAY_OF_YEAR
550
            month = 0;
551
            day = fields[DAY_OF_YEAR];
552
          }
553
      }
554
    else
555
      {
556
        if (isSet[DAY_OF_WEEK])
557
          {
558
            int first = getFirstDayOfMonth(year, month);
559
 
560
            // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
561
            if (isSet[DAY_OF_WEEK_IN_MONTH])
562
              {
563
                if (fields[DAY_OF_WEEK_IN_MONTH] < 0)
564
                  {
565
                    month++;
566
                    first = getFirstDayOfMonth(year, month);
567
                    day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH]);
568
                  }
569
                else
570
                  day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1);
571
 
572
                int offs = fields[DAY_OF_WEEK] - first;
573
                if (offs < 0)
574
                  offs += 7;
575
                day += offs;
576
              }
577
            else
578
              { // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
579
                int offs = 1;
580
                int daysInFirstWeek = getFirstDayOfWeek() - first;
581
                if (daysInFirstWeek <= 0)
582
                  daysInFirstWeek += 7;
583
 
584
                if (daysInFirstWeek < getMinimalDaysInFirstWeek())
585
                  offs += daysInFirstWeek;
586
                else
587
                  offs -= 7 - daysInFirstWeek;
588
 
589
                day = offs + 7 * (fields[WEEK_OF_MONTH] - 1);
590
                offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
591
                if (offs <= 0)
592
                  offs += 7;
593
                day += offs;
594
              }
595
          }
596
 
597
        // 1:  YEAR + MONTH + DAY_OF_MONTH
598
      }
599
    if (era == BC && year > 0)
600
      year = 1 - year;
601
 
602
    // rest of code assumes day/month/year set
603
    // should negative BC years be AD?
604
    // get the hour (but no check for validity)
605
    if (isSet[HOUR])
606
      {
607
        hour = fields[HOUR];
608
        if (fields[AM_PM] == PM)
609
          hour += 12;
610
      }
611
    else
612
      hour = fields[HOUR_OF_DAY];
613
 
614
    // Read the era,year,month,day fields and convert as appropriate.
615
    // Calculate number of milliseconds into the day
616
    // This takes care of both h, m, s, ms over/underflows.
617
    long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis;
618
    day += allMillis / (24 * 60 * 60 * 1000L);
619
    millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
620
 
621
    if (month < 0)
622
      {
623
        year += (int) month / 12;
624
        month = month % 12;
625
        if (month < 0)
626
          {
627
            month += 12;
628
            year--;
629
          }
630
      }
631
    if (month > 11)
632
      {
633
        year += (month / 12);
634
        month = month % 12;
635
      }
636
 
637
    month_days[1] = isLeapYear(year) ? 29 : 28;
638
 
639
    while (day <= 0)
640
      {
641
        if (month == 0)
642
          {
643
            year--;
644
            month_days[1] = isLeapYear(year) ? 29 : 28;
645
          }
646
        month = (month + 11) % 12;
647
        day += month_days[month];
648
      }
649
    while (day > month_days[month])
650
      {
651
        day -= (month_days[month]);
652
        month = (month + 1) % 12;
653
        if (month == 0)
654
          {
655
            year++;
656
            month_days[1] = isLeapYear(year) ? 29 : 28;
657
          }
658
      }
659
 
660
    // ok, by here we have valid day,month,year,era and millisinday
661
    int dayOfYear = dayCount[month] + day - 1; // (day starts on 1)
662
    if (isLeapYear(year) && month > 1)
663
      dayOfYear++;
664
 
665
    int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
666
                      - EPOCH_DAYS; // gregorian days from 1 to epoch.
667
    int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
668
                     - (int) Math.floor((double) (year - 1) / 100.);
669
 
670
    if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover)
671
      relativeDay += gregFactor;
672
    else
673
      relativeDay -= 2;
674
 
675
    time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay;
676
 
677
    // the epoch was a Thursday.
678
    int weekday = (int) (relativeDay + THURSDAY) % 7;
679
    if (weekday <= 0)
680
      weekday += 7;
681
    fields[DAY_OF_WEEK] = weekday;
682
 
683
    // Time zone corrections.
684
    TimeZone zone = getTimeZone();
685
    int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET]
686
                                       : zone.getRawOffset();
687
 
688
    int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET]
689
                                      : (zone.getOffset((year < 0) ? BC : AD,
690
                                                        (year < 0) ? 1 - year
691
                                                                   : year,
692
                                                        month, day, weekday,
693
                                                        millisInDay)
694
                                      - zone.getRawOffset());
695
 
696
    time -= rawOffset + dstOffset;
697
 
698
    isTimeSet = true;
699
  }
700
 
701
  /**
702
   * Get the linear day in days since the epoch, using the
703
   * Julian or Gregorian calendar as specified.  If you specify a
704
   * nonpositive year it is interpreted as BC as following: 0 is 1
705
   * BC, -1 is 2 BC and so on.
706
   *
707
   * @param year the year of the date.
708
   * @param dayOfYear the day of year of the date; 1 based.
709
   * @param gregorian <code>true</code>, if we should use the Gregorian rules.
710
   * @return the days since the epoch, may be negative.
711
   */
712
  private long getLinearDay(int year, int dayOfYear, boolean gregorian)
713
  {
714
    // The 13 is the number of days, that were omitted in the Gregorian
715
    // Calender until the epoch.
716
    // We shift right by 2 instead of dividing by 4, to get correct
717
    // results for negative years (and this is even more efficient).
718
    long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1)
719
                     - EPOCH_DAYS; // gregorian days from 1 to epoch.
720
 
721
    if (gregorian)
722
      {
723
        // subtract the days that are missing in gregorian calendar
724
        // with respect to julian calendar.
725
        //
726
        // Okay, here we rely on the fact that the gregorian
727
        // calendar was introduced in the AD era.  This doesn't work
728
        // with negative years.
729
        //
730
        // The additional leap year factor accounts for the fact that
731
        // a leap day is not seen on Jan 1 of the leap year.
732
        int gregOffset = (int) Math.floor((double) (year - 1) / 400.)
733
                         - (int) Math.floor((double) (year - 1) / 100.);
734
 
735
        return julianDay + gregOffset;
736
      }
737
    else
738
      julianDay -= 2;
739
    return julianDay;
740
  }
741
 
742
  /**
743
   * Converts the given linear day into era, year, month,
744
   * day_of_year, day_of_month, day_of_week, and writes the result
745
   * into the fields array.
746
   *
747
   * @param day the linear day.
748
   * @param gregorian true, if we should use Gregorian rules.
749
   */
750
  private void calculateDay(int[] fields, long day, boolean gregorian)
751
  {
752
    // the epoch was a Thursday.
753
    int weekday = (int) (day + THURSDAY) % 7;
754
    if (weekday <= 0)
755
      weekday += 7;
756
    fields[DAY_OF_WEEK] = weekday;
757
 
758
    // get a first approximation of the year.  This may be one 
759
    // year too big.
760
    int year = 1970
761
               + (int) (gregorian
762
                        ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L
763
                        + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L));
764
    if (day >= 0)
765
      year++;
766
 
767
    long firstDayOfYear = getLinearDay(year, 1, gregorian);
768
 
769
    // Now look in which year day really lies.
770
    if (day < firstDayOfYear)
771
      {
772
        year--;
773
        firstDayOfYear = getLinearDay(year, 1, gregorian);
774
      }
775
 
776
    day -= firstDayOfYear - 1; // day of year,  one based.
777
 
778
    fields[DAY_OF_YEAR] = (int) day;
779
    if (year <= 0)
780
      {
781
        fields[ERA] = BC;
782
        fields[YEAR] = 1 - year;
783
      }
784
    else
785
      {
786
        fields[ERA] = AD;
787
        fields[YEAR] = year;
788
      }
789
 
790
    int leapday = isLeapYear(year) ? 1 : 0;
791
    if (day <= 31 + 28 + leapday)
792
      {
793
        fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY
794
        fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH];
795
      }
796
    else
797
      {
798
        // A few more magic formulas
799
        int scaledDay = ((int) day - leapday) * 5 + 8;
800
        fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
801
        fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
802
      }
803
  }
804
 
805
  /**
806
   * Converts the milliseconds since the epoch UTC
807
   * (<code>time</code>) to time fields
808
   * (<code>fields</code>).
809
   */
810
  protected synchronized void computeFields()
811
  {
812
    boolean gregorian = (time >= gregorianCutover);
813
 
814
    TimeZone zone = getTimeZone();
815
    fields[ZONE_OFFSET] = zone.getRawOffset();
816
    long localTime = time + fields[ZONE_OFFSET];
817
 
818
    long day = localTime / (24 * 60 * 60 * 1000L);
819
    int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
820
 
821
    if (millisInDay < 0)
822
      {
823
        millisInDay += (24 * 60 * 60 * 1000);
824
        day--;
825
      }
826
 
827
    calculateDay(fields, day, gregorian);
828
    fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR],
829
                                        fields[MONTH], fields[DAY_OF_MONTH],
830
                                        fields[DAY_OF_WEEK], millisInDay)
831
                         - fields[ZONE_OFFSET];
832
 
833
    millisInDay += fields[DST_OFFSET];
834
    if (millisInDay >= 24 * 60 * 60 * 1000)
835
      {
836
        millisInDay -= 24 * 60 * 60 * 1000;
837
        calculateDay(fields, ++day, gregorian);
838
      }
839
 
840
    fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
841
 
842
    // which day of the week are we (0..6), relative to getFirstDayOfWeek
843
    int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7;
844
 
845
    fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] - relativeWeekday + 12) / 7;
846
 
847
    int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7;
848
 
849
    // Do the Correction: getMinimalDaysInFirstWeek() is always in the 
850
    // first week.
851
    int minDays = getMinimalDaysInFirstWeek();
852
    int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays)
853
                       - getFirstDayOfWeek()) % 7;
854
    if (minDays - firstWeekday < 1)
855
      weekOfYear++;
856
    fields[WEEK_OF_YEAR] = weekOfYear;
857
 
858
    int hourOfDay = millisInDay / (60 * 60 * 1000);
859
    fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
860
    int hour = hourOfDay % 12;
861
    fields[HOUR] = hour;
862
    fields[HOUR_OF_DAY] = hourOfDay;
863
    millisInDay %= (60 * 60 * 1000);
864
    fields[MINUTE] = millisInDay / (60 * 1000);
865
    millisInDay %= (60 * 1000);
866
    fields[SECOND] = millisInDay / (1000);
867
    fields[MILLISECOND] = millisInDay % 1000;
868
 
869
    areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
870
  }
871
 
872
  /**
873
   * Return a hash code for this object, following the general contract
874
   * specified by {@link Object#hashCode()}.
875
   * @return the hash code
876
   */
877
  public int hashCode()
878
  {
879
    int val = (int) ((gregorianCutover >>> 32) ^ (gregorianCutover & 0xffffffff));
880
    return super.hashCode() ^ val;
881
  }
882
 
883
  /**
884
   * Compares the given calendar with this.  An object, o, is
885
   * equivalent to this if it is also a <code>GregorianCalendar</code>
886
   * with the same time since the epoch under the same conditions
887
   * (same change date and same time zone).
888
   *
889
   * @param o the object to that we should compare.
890
   * @return true, if the given object is a calendar, that represents
891
   * the same time (but doesn't necessarily have the same fields).
892
   * @throws IllegalArgumentException if one of the fields
893
   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
894
   *         specified, if an unknown field is specified or if one
895
   *         of the calendar fields receives an illegal value when
896
   *         leniancy is not enabled.
897
   */
898
  public boolean equals(Object o)
899
  {
900
    if (! (o instanceof GregorianCalendar))
901
      return false;
902
 
903
    GregorianCalendar cal = (GregorianCalendar) o;
904
    return (cal.gregorianCutover == gregorianCutover
905
            && super.equals(o));
906
  }
907
 
908
  /**
909
   * Adds the specified amount of time to the given time field.  The
910
   * amount may be negative to subtract the time.  If the field overflows
911
   * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
912
   * @param field one of the time field constants.
913
   * @param amount the amount of time to add.
914
   * @exception IllegalArgumentException if <code>field</code> is
915
   *   <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or
916
   *   if <code>amount</code> contains an out-of-range value and the calendar
917
   *   is not in lenient mode.
918
   */
919
  public void add(int field, int amount)
920
  {
921
    switch (field)
922
      {
923
      case YEAR:
924
        complete();
925
        fields[YEAR] += amount;
926
        isTimeSet = false;
927
        break;
928
      case MONTH:
929
        complete();
930
        int months = fields[MONTH] + amount;
931
        fields[YEAR] += months / 12;
932
        fields[MONTH] = months % 12;
933
        if (fields[MONTH] < 0)
934
          {
935
            fields[MONTH] += 12;
936
            fields[YEAR]--;
937
          }
938
        int maxDay = getActualMaximum(DAY_OF_MONTH);
939
        if (fields[DAY_OF_MONTH] > maxDay)
940
          fields[DAY_OF_MONTH] = maxDay;
941
        set(YEAR, fields[YEAR]);
942
        set(MONTH, fields[MONTH]);
943
        break;
944
      case DAY_OF_MONTH:
945
      case DAY_OF_YEAR:
946
      case DAY_OF_WEEK:
947
        if (! isTimeSet)
948
          computeTime();
949
        time += amount * (24 * 60 * 60 * 1000L);
950
        areFieldsSet = false;
951
        break;
952
      case WEEK_OF_YEAR:
953
      case WEEK_OF_MONTH:
954
      case DAY_OF_WEEK_IN_MONTH:
955
        if (! isTimeSet)
956
          computeTime();
957
        time += amount * (7 * 24 * 60 * 60 * 1000L);
958
        areFieldsSet = false;
959
        break;
960
      case AM_PM:
961
        if (! isTimeSet)
962
          computeTime();
963
        time += amount * (12 * 60 * 60 * 1000L);
964
        areFieldsSet = false;
965
        break;
966
      case HOUR:
967
      case HOUR_OF_DAY:
968
        if (! isTimeSet)
969
          computeTime();
970
        time += amount * (60 * 60 * 1000L);
971
        areFieldsSet = false;
972
        break;
973
      case MINUTE:
974
        if (! isTimeSet)
975
          computeTime();
976
        time += amount * (60 * 1000L);
977
        areFieldsSet = false;
978
        break;
979
      case SECOND:
980
        if (! isTimeSet)
981
          computeTime();
982
        time += amount * (1000L);
983
        areFieldsSet = false;
984
        break;
985
      case MILLISECOND:
986
        if (! isTimeSet)
987
          computeTime();
988
        time += amount;
989
        areFieldsSet = false;
990
        break;
991
      case ZONE_OFFSET:
992
      case DST_OFFSET:default:
993
        throw new IllegalArgumentException("Invalid or unknown field");
994
      }
995
  }
996
 
997
  /**
998
   * Rolls the specified time field up or down.  This means add one
999
   * to the specified field, but don't change the other fields.  If
1000
   * the maximum for this field is reached, start over with the
1001
   * minimum value.
1002
   *
1003
   * <strong>Note:</strong> There may be situation, where the other
1004
   * fields must be changed, e.g rolling the month on May, 31.
1005
   * The date June, 31 is automatically converted to July, 1.
1006
   * This requires lenient settings.
1007
   *
1008
   * @param field the time field. One of the time field constants.
1009
   * @param up the direction, true for up, false for down.
1010
   * @throws IllegalArgumentException if one of the fields
1011
   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
1012
   *         specified, if an unknown field is specified or if one
1013
   *         of the calendar fields receives an illegal value when
1014
   *         leniancy is not enabled.
1015
   */
1016
  public void roll(int field, boolean up)
1017
  {
1018
    roll(field, up ? 1 : -1);
1019
  }
1020
 
1021
  /**
1022
   * Checks that the fields are still within their legal bounds,
1023
   * following use of the <code>roll()</code> method.
1024
   *
1025
   * @param field the field to check.
1026
   * @param delta multipler for alterations to the <code>time</code>.
1027
   * @see #roll(int, boolean)
1028
   * @see #roll(int, int)
1029
   */
1030
  private void cleanUpAfterRoll(int field, int delta)
1031
  {
1032
    switch (field)
1033
      {
1034
      case ERA:
1035
      case YEAR:
1036
      case MONTH:
1037
        // check that day of month is still in correct range
1038
        if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH))
1039
          fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH);
1040
        isTimeSet = false;
1041
        isSet[WEEK_OF_MONTH] = false;
1042
        isSet[DAY_OF_WEEK] = false;
1043
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1044
        isSet[DAY_OF_YEAR] = false;
1045
        isSet[WEEK_OF_YEAR] = false;
1046
        break;
1047
      case DAY_OF_MONTH:
1048
        isSet[WEEK_OF_MONTH] = false;
1049
        isSet[DAY_OF_WEEK] = false;
1050
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1051
        isSet[DAY_OF_YEAR] = false;
1052
        isSet[WEEK_OF_YEAR] = false;
1053
        time += delta * (24 * 60 * 60 * 1000L);
1054
        break;
1055
      case WEEK_OF_MONTH:
1056
        isSet[DAY_OF_MONTH] = false;
1057
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1058
        isSet[DAY_OF_YEAR] = false;
1059
        isSet[WEEK_OF_YEAR] = false;
1060
        time += delta * (7 * 24 * 60 * 60 * 1000L);
1061
        break;
1062
      case DAY_OF_WEEK_IN_MONTH:
1063
        isSet[DAY_OF_MONTH] = false;
1064
        isSet[WEEK_OF_MONTH] = false;
1065
        isSet[DAY_OF_YEAR] = false;
1066
        isSet[WEEK_OF_YEAR] = false;
1067
        time += delta * (7 * 24 * 60 * 60 * 1000L);
1068
        break;
1069
      case DAY_OF_YEAR:
1070
        isSet[MONTH] = false;
1071
        isSet[DAY_OF_MONTH] = false;
1072
        isSet[WEEK_OF_MONTH] = false;
1073
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1074
        isSet[DAY_OF_WEEK] = false;
1075
        isSet[WEEK_OF_YEAR] = false;
1076
        time += delta * (24 * 60 * 60 * 1000L);
1077
        break;
1078
      case WEEK_OF_YEAR:
1079
        isSet[MONTH] = false;
1080
        isSet[DAY_OF_MONTH] = false;
1081
        isSet[WEEK_OF_MONTH] = false;
1082
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1083
        isSet[DAY_OF_YEAR] = false;
1084
        time += delta * (7 * 24 * 60 * 60 * 1000L);
1085
        break;
1086
      case AM_PM:
1087
        isSet[HOUR_OF_DAY] = false;
1088
        time += delta * (12 * 60 * 60 * 1000L);
1089
        break;
1090
      case HOUR:
1091
        isSet[HOUR_OF_DAY] = false;
1092
        time += delta * (60 * 60 * 1000L);
1093
        break;
1094
      case HOUR_OF_DAY:
1095
        isSet[HOUR] = false;
1096
        isSet[AM_PM] = false;
1097
        time += delta * (60 * 60 * 1000L);
1098
        break;
1099
      case MINUTE:
1100
        time += delta * (60 * 1000L);
1101
        break;
1102
      case SECOND:
1103
        time += delta * (1000L);
1104
        break;
1105
      case MILLISECOND:
1106
        time += delta;
1107
        break;
1108
      }
1109
  }
1110
 
1111
  /**
1112
   * Rolls the specified time field by the given amount.  This means
1113
   * add amount to the specified field, but don't change the other
1114
   * fields.  If the maximum for this field is reached, start over
1115
   * with the minimum value and vice versa for negative amounts.
1116
   *
1117
   * <strong>Note:</strong> There may be situation, where the other
1118
   * fields must be changed, e.g rolling the month on May, 31.
1119
   * The date June, 31 is automatically corrected to June, 30.
1120
   *
1121
   * @param field the time field. One of the time field constants.
1122
   * @param amount the amount by which we should roll.
1123
   * @throws IllegalArgumentException if one of the fields
1124
   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
1125
   *         specified, if an unknown field is specified or if one
1126
   *         of the calendar fields receives an illegal value when
1127
   *         leniancy is not enabled.
1128
   */
1129
  public void roll(int field, int amount)
1130
  {
1131
    switch (field)
1132
      {
1133
      case DAY_OF_WEEK:
1134
        // day of week is special: it rolls automatically
1135
        add(field, amount);
1136
        return;
1137
      case ZONE_OFFSET:
1138
      case DST_OFFSET:
1139
        throw new IllegalArgumentException("Can't roll time zone");
1140
      }
1141
    complete();
1142
    int min = getActualMinimum(field);
1143
    int range = getActualMaximum(field) - min + 1;
1144
    int oldval = fields[field];
1145
    int newval = (oldval - min + range + amount) % range + min;
1146
    if (newval < min)
1147
      newval += range;
1148
    fields[field] = newval;
1149
    cleanUpAfterRoll(field, newval - oldval);
1150
  }
1151
 
1152
  /**
1153
   * The minimum values for the calendar fields.
1154
   */
1155
  private static final int[] minimums =
1156
                                        {
1157
                                          BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM,
1158
                                          1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000),
1159
 
1160
                                        };
1161
 
1162
  /**
1163
   * The maximum values for the calendar fields.
1164
   */
1165
  private static final int[] maximums =
1166
                                        {
1167
                                          AD, 5000000, 11, 53, 5, 31, 366,
1168
                                          SATURDAY, 5, PM, 12, 23, 59, 59, 999,
1169
                                          +(12 * 60 * 60 * 1000),
1170
                                          (12 * 60 * 60 * 1000)
1171
                                        };
1172
 
1173
  /**
1174
   * Gets the smallest value that is allowed for the specified field.
1175
   *
1176
   * @param field one of the time field constants.
1177
   * @return the smallest value for the specified field.
1178
   */
1179
  public int getMinimum(int field)
1180
  {
1181
    return minimums[field];
1182
  }
1183
 
1184
  /**
1185
   * Gets the biggest value that is allowed for the specified field.
1186
   *
1187
   * @param field one of the time field constants.
1188
   * @return the biggest value.
1189
   */
1190
  public int getMaximum(int field)
1191
  {
1192
    return maximums[field];
1193
  }
1194
 
1195
  /**
1196
   * Gets the greatest minimum value that is allowed for the specified field.
1197
   * This is the largest value returned by the <code>getActualMinimum(int)</code>
1198
   * method.
1199
   *
1200
   * @param field the time field. One of the time field constants.
1201
   * @return the greatest minimum value.
1202
   * @see #getActualMinimum(int)
1203
   */
1204
  public int getGreatestMinimum(int field)
1205
  {
1206
    if (field == WEEK_OF_YEAR)
1207
      return 1;
1208
    return minimums[field];
1209
  }
1210
 
1211
  /**
1212
   * Gets the smallest maximum value that is allowed for the
1213
   * specified field.  This is the smallest value returned
1214
   * by the <code>getActualMaximum(int)</code>.  For example,
1215
   * this is 28 for DAY_OF_MONTH (as all months have at least
1216
   * 28 days).
1217
   *
1218
   * @param field the time field. One of the time field constants.
1219
   * @return the least maximum value.
1220
   * @see #getActualMaximum(int)
1221
   * @since 1.2
1222
   */
1223
  public int getLeastMaximum(int field)
1224
  {
1225
    switch (field)
1226
      {
1227
      case WEEK_OF_YEAR:
1228
        return 52;
1229
      case DAY_OF_MONTH:
1230
        return 28;
1231
      case DAY_OF_YEAR:
1232
        return 365;
1233
      case DAY_OF_WEEK_IN_MONTH:
1234
      case WEEK_OF_MONTH:
1235
        return 4;
1236
      default:
1237
        return maximums[field];
1238
      }
1239
  }
1240
 
1241
  /**
1242
   * Gets the actual minimum value that is allowed for the specified field.
1243
   * This value is dependent on the values of the other fields.  Note that
1244
   * this calls <code>complete()</code> if not enough fields are set.  This
1245
   * can have ugly side effects.  The value given depends on the current
1246
   * time used by this instance.
1247
   *
1248
   * @param field the time field. One of the time field constants.
1249
   * @return the actual minimum value.
1250
   * @since 1.2
1251
   */
1252
  public int getActualMinimum(int field)
1253
  {
1254
    if (field == WEEK_OF_YEAR)
1255
      {
1256
        int min = getMinimalDaysInFirstWeek();
1257
        if (min == 0)
1258
          return 1;
1259
        if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
1260
          complete();
1261
 
1262
        int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1263
        int weekday = getWeekDay(year, min);
1264
        if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1)
1265
          return 1;
1266
        return 0;
1267
      }
1268
    return minimums[field];
1269
  }
1270
 
1271
  /**
1272
   * Gets the actual maximum value that is allowed for the specified field.
1273
   * This value is dependent on the values of the other fields.  Note that
1274
   * this calls <code>complete()</code> if not enough fields are set.  This
1275
   * can have ugly side effects.  The value given depends on the current time
1276
   * used by this instance; thus, leap years have a maximum day of month value of
1277
   * 29, rather than 28.
1278
   *
1279
   * @param field the time field. One of the time field constants.
1280
   * @return the actual maximum value.
1281
   */
1282
  public int getActualMaximum(int field)
1283
  {
1284
    switch (field)
1285
      {
1286
      case WEEK_OF_YEAR:
1287
        {
1288
          if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
1289
            complete();
1290
 
1291
          // This is wrong for the year that contains the gregorian change.
1292
          // I.e it gives the weeks in the julian year or in the gregorian
1293
          // year in that case.
1294
          int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1295
          int lastDay = isLeapYear(year) ? 366 : 365;
1296
          int weekday = getWeekDay(year, lastDay);
1297
          int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
1298
 
1299
          int minimalDays = getMinimalDaysInFirstWeek();
1300
          int firstWeekday = getWeekDay(year, minimalDays);
1301
          /*
1302
           * Is there a set of days at the beginning of the year, before the
1303
           * first day of the week, equal to or greater than the minimum number
1304
           * of days required in the first week?
1305
           */
1306
          if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
1307
            return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */
1308
        }
1309
      case DAY_OF_MONTH:
1310
        {
1311
          if (! areFieldsSet || ! isSet[MONTH])
1312
            complete();
1313
          int month = fields[MONTH];
1314
 
1315
          // If you change this, you should also change 
1316
          // SimpleTimeZone.getDaysInMonth();
1317
          if (month == FEBRUARY)
1318
            {
1319
              if (! isSet[YEAR] || ! isSet[ERA])
1320
                complete();
1321
              int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1322
              return isLeapYear(year) ? 29 : 28;
1323
            }
1324
          else if (month < AUGUST)
1325
            return 31 - (month & 1);
1326
          else
1327
            return 30 + (month & 1);
1328
        }
1329
      case DAY_OF_YEAR:
1330
        {
1331
          if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
1332
            complete();
1333
          int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1334
          return isLeapYear(year) ? 366 : 365;
1335
        }
1336
      case DAY_OF_WEEK_IN_MONTH:
1337
        {
1338
          // This is wrong for the month that contains the gregorian change.
1339
          int daysInMonth = getActualMaximum(DAY_OF_MONTH);
1340
 
1341
          // That's black magic, I know
1342
          return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
1343
        }
1344
      case WEEK_OF_MONTH:
1345
        {
1346
          int daysInMonth = getActualMaximum(DAY_OF_MONTH);
1347
          int weekday = (daysInMonth - fields[DAY_OF_MONTH]
1348
                        + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
1349
          return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
1350
        }
1351
      default:
1352
        return maximums[field];
1353
      }
1354
  }
1355
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.