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

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [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 is locale dependent; the default for most catholic
149
   * countries 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;
156
 
157
  /**
158
   * For compatability with Sun's JDK.
159
   */
160
  static final long serialVersionUID = -8125100834729963327L;
161
 
162
  /**
163
   * The name of the resource bundle. Used only by getBundle()
164
   */
165
  private static final String bundleName = "gnu.java.locale.Calendar";
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
    ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale,
240
                                                 ClassLoader
241
                                                 .getSystemClassLoader());
242
    gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime();
243
  }
244
 
245
  /**
246
   * Constructs a new GregorianCalendar representing midnight on the
247
   * given date with the default time zone and locale.
248
   * @param year corresponds to the YEAR time field.
249
   * @param month corresponds to the MONTH time field.
250
   * @param day corresponds to the DAY time field.
251
   */
252
  public GregorianCalendar(int year, int month, int day)
253
  {
254
    this(TimeZone.getDefault(), Locale.getDefault(), false);
255
    set(year, month, day);
256
  }
257
 
258
  /**
259
   * Constructs a new GregorianCalendar representing midnight on the
260
   * given date with the default time zone and locale.
261
   *
262
   * @param year corresponds to the YEAR time field.
263
   * @param month corresponds to the MONTH time field.
264
   * @param day corresponds to the DAY time field.
265
   * @param hour corresponds to the HOUR_OF_DAY time field.
266
   * @param minute corresponds to the MINUTE time field.
267
   */
268
  public GregorianCalendar(int year, int month, int day, int hour, int minute)
269
  {
270
    this(TimeZone.getDefault(), Locale.getDefault(), false);
271
    set(year, month, day, hour, minute);
272
  }
273
 
274
  /**
275
   * Constructs a new GregorianCalendar representing midnight on the
276
   * given date with the default time zone and locale.
277
   *
278
   * @param year corresponds to the YEAR time field.
279
   * @param month corresponds to the MONTH time field.
280
   * @param day corresponds to the DAY time field.
281
   * @param hour corresponds to the HOUR_OF_DAY time field.
282
   * @param minute corresponds to the MINUTE time field.
283
   * @param second corresponds to the SECOND time field.
284
   */
285
  public GregorianCalendar(int year, int month, int day, int hour, int minute,
286
                           int second)
287
  {
288
    this(TimeZone.getDefault(), Locale.getDefault(), false);
289
    set(year, month, day, hour, minute, second);
290
  }
291
 
292
  /**
293
   * Sets the date of the switch from Julian dates to Gregorian dates.
294
   * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure
295
   * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian
296
   * calendar.
297
   *
298
   * @param date the date of the change.
299
   */
300
  public void setGregorianChange(Date date)
301
  {
302
    gregorianCutover = date.getTime();
303
  }
304
 
305
  /**
306
   * Gets the date of the switch from Julian dates to Gregorian dates.
307
   *
308
   * @return the date of the change.
309
   */
310
  public final Date getGregorianChange()
311
  {
312
    return new Date(gregorianCutover);
313
  }
314
 
315
  /**
316
   * <p>
317
   * Determines if the given year is a leap year.  The result is
318
   * undefined if the Gregorian change took place in 1800, so that
319
   * the end of February is skipped, and that year is specified.
320
   * (well...).
321
   * </p>
322
   * <p>
323
   * To specify a year in the BC era, use a negative value calculated
324
   * as 1 - y, where y is the required year in BC.  So, 1 BC is 0,
325
   * 2 BC is -1, 3 BC is -2, etc.
326
   * </p>
327
   *
328
   * @param year a year (use a negative value for BC).
329
   * @return true, if the given year is a leap year, false otherwise.
330
   */
331
  public boolean isLeapYear(int year)
332
  {
333
    // Only years divisible by 4 can be leap years
334
    if ((year & 3) != 0)
335
      return false;
336
 
337
    // Is the leap-day a Julian date? Then it's a leap year
338
    if (! isGregorian(year, 31 + 29 - 1))
339
      return true;
340
 
341
    // Apply gregorian rules otherwise
342
    return ((year % 100) != 0 || (year % 400) == 0);
343
  }
344
 
345
  /**
346
   * Retrieves the day of the week corresponding to the specified
347
   * day of the specified year.
348
   *
349
   * @param year the year in which the dayOfYear occurs.
350
   * @param dayOfYear the day of the year (an integer between 0 and
351
   *        and 366)
352
   */
353
  private int getWeekDay(int year, int dayOfYear)
354
  {
355
    boolean greg = isGregorian(year, dayOfYear);
356
    int day = (int) getLinearDay(year, dayOfYear, greg);
357
 
358
    // The epoch was a thursday.
359
    int weekday = (day + THURSDAY) % 7;
360
    if (weekday <= 0)
361
      weekday += 7;
362
    return weekday;
363
  }
364
 
365
  /**
366
   * Returns the day of the week for the first day of a given month (0..11)
367
   */
368
  private int getFirstDayOfMonth(int year, int month)
369
  {
370
    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
371
 
372
    if (month > 11)
373
      {
374
        year += (month / 12);
375
        month = month % 12;
376
      }
377
 
378
    if (month < 0)
379
      {
380
        year += (int) month / 12;
381
        month = month % 12;
382
        if (month < 0)
383
          {
384
            month += 12;
385
            year--;
386
          }
387
      }
388
 
389
    int dayOfYear = dayCount[month] + 1;
390
    if (month > 1)
391
      if (isLeapYear(year))
392
        dayOfYear++;
393
 
394
    boolean greg = isGregorian(year, dayOfYear);
395
    int day = (int) getLinearDay(year, dayOfYear, greg);
396
 
397
    // The epoch was a thursday.
398
    int weekday = (day + THURSDAY) % 7;
399
    if (weekday <= 0)
400
      weekday += 7;
401
    return weekday;
402
  }
403
 
404
  /**
405
   * Takes a year, and a (zero based) day of year and determines
406
   * if it is gregorian or not.
407
   */
408
  private boolean isGregorian(int year, int dayOfYear)
409
  {
410
    int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
411
                      - EPOCH_DAYS; // gregorian days from 1 to epoch.
412
    int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
413
                     - (int) Math.floor((double) (year - 1) / 100.);
414
 
415
    return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover);
416
  }
417
 
418
  /**
419
   * Check set fields for validity, without leniency.
420
   *
421
   * @throws IllegalArgumentException if a field is invalid
422
   */
423
  private void nonLeniencyCheck() throws IllegalArgumentException
424
  {
425
    int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
426
    int year = fields[YEAR];
427
    int month = fields[MONTH];
428
    int leap = isLeapYear(year) ? 1 : 0;
429
 
430
    if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC)
431
      throw new IllegalArgumentException("Illegal ERA.");
432
    if (isSet[YEAR] && fields[YEAR] < 1)
433
      throw new IllegalArgumentException("Illegal YEAR.");
434
    if (isSet[MONTH] && (month < 0 || month > 11))
435
      throw new IllegalArgumentException("Illegal MONTH.");
436
    if (isSet[WEEK_OF_YEAR])
437
      {
438
        int daysInYear = 365 + leap;
439
        daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week
440
        int last = getFirstDayOfMonth(year, 11) + 4;
441
        if (last > 7)
442
          last -= 7;
443
        daysInYear += 7 - last;
444
        int weeks = daysInYear / 7;
445
        if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks)
446
          throw new IllegalArgumentException("Illegal WEEK_OF_YEAR.");
447
      }
448
 
449
    if (isSet[WEEK_OF_MONTH])
450
      {
451
        int weeks = (month == 1 && leap == 0) ? 4 : 5;
452
        if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks)
453
          throw new IllegalArgumentException("Illegal WEEK_OF_MONTH.");
454
      }
455
 
456
    if (isSet[DAY_OF_MONTH])
457
      if (fields[DAY_OF_MONTH] < 1
458
          || fields[DAY_OF_MONTH] > month_days[month]
459
          + ((month == 1) ? leap : 0))
460
        throw new IllegalArgumentException("Illegal DAY_OF_MONTH.");
461
 
462
    if (isSet[DAY_OF_YEAR]
463
        && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap))
464
      throw new IllegalArgumentException("Illegal DAY_OF_YEAR.");
465
 
466
    if (isSet[DAY_OF_WEEK]
467
        && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7))
468
      throw new IllegalArgumentException("Illegal DAY_OF_WEEK.");
469
 
470
    if (isSet[DAY_OF_WEEK_IN_MONTH])
471
      {
472
        int weeks = (month == 1 && leap == 0) ? 4 : 5;
473
        if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks
474
            || fields[DAY_OF_WEEK_IN_MONTH] > weeks)
475
          throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH.");
476
      }
477
 
478
    if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM)
479
      throw new IllegalArgumentException("Illegal AM_PM.");
480
    if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11))
481
      throw new IllegalArgumentException("Illegal HOUR.");
482
    if (isSet[HOUR_OF_DAY]
483
        && (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23))
484
      throw new IllegalArgumentException("Illegal HOUR_OF_DAY.");
485
    if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59))
486
      throw new IllegalArgumentException("Illegal MINUTE.");
487
    if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59))
488
      throw new IllegalArgumentException("Illegal SECOND.");
489
    if (isSet[MILLISECOND]
490
        && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999))
491
      throw new IllegalArgumentException("Illegal MILLISECOND.");
492
    if (isSet[ZONE_OFFSET]
493
        && (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L
494
        || fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L))
495
      throw new IllegalArgumentException("Illegal ZONE_OFFSET.");
496
    if (isSet[DST_OFFSET]
497
        && (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L
498
        || fields[DST_OFFSET] > 12 * 60 * 60 * 1000L))
499
      throw new IllegalArgumentException("Illegal DST_OFFSET.");
500
  }
501
 
502
  /**
503
   * Converts the time field values (<code>fields</code>) to
504
   * milliseconds since the epoch UTC (<code>time</code>).
505
   *
506
   * @throws IllegalArgumentException if any calendar fields
507
   *         are invalid.
508
   */
509
  protected synchronized void computeTime()
510
  {
511
    int millisInDay = 0;
512
    int era = fields[ERA];
513
    int year = fields[YEAR];
514
    int month = fields[MONTH];
515
    int day = fields[DAY_OF_MONTH];
516
 
517
    int minute = fields[MINUTE];
518
    int second = fields[SECOND];
519
    int millis = fields[MILLISECOND];
520
    int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
521
    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
522
    int hour = 0;
523
 
524
    if (! isLenient())
525
      nonLeniencyCheck();
526
 
527
    if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR]))
528
      {
529
        // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
530
        if (isSet[WEEK_OF_YEAR])
531
          {
532
            int first = getFirstDayOfMonth(year, 0);
533
            int offs = 1;
534
            int daysInFirstWeek = getFirstDayOfWeek() - first;
535
            if (daysInFirstWeek <= 0)
536
              daysInFirstWeek += 7;
537
 
538
            if (daysInFirstWeek < getMinimalDaysInFirstWeek())
539
              offs += daysInFirstWeek;
540
            else
541
              offs -= 7 - daysInFirstWeek;
542
            month = 0;
543
            day = offs + 7 * (fields[WEEK_OF_YEAR] - 1);
544
            offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
545
 
546
            if (offs < 0)
547
              offs += 7;
548
            day += offs;
549
          }
550
        else
551
          {
552
            // 4:  YEAR + DAY_OF_YEAR
553
            month = 0;
554
            day = fields[DAY_OF_YEAR];
555
          }
556
      }
557
    else
558
      {
559
        if (isSet[DAY_OF_WEEK])
560
          {
561
            int first = getFirstDayOfMonth(year, month);
562
 
563
            // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
564
            if (isSet[DAY_OF_WEEK_IN_MONTH])
565
              {
566
                if (fields[DAY_OF_WEEK_IN_MONTH] < 0)
567
                  {
568
                    month++;
569
                    first = getFirstDayOfMonth(year, month);
570
                    day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH]);
571
                  }
572
                else
573
                  day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1);
574
 
575
                int offs = fields[DAY_OF_WEEK] - first;
576
                if (offs < 0)
577
                  offs += 7;
578
                day += offs;
579
              }
580
            else
581
              { // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
582
                int offs = 1;
583
                int daysInFirstWeek = getFirstDayOfWeek() - first;
584
                if (daysInFirstWeek <= 0)
585
                  daysInFirstWeek += 7;
586
 
587
                if (daysInFirstWeek < getMinimalDaysInFirstWeek())
588
                  offs += daysInFirstWeek;
589
                else
590
                  offs -= 7 - daysInFirstWeek;
591
 
592
                day = offs + 7 * (fields[WEEK_OF_MONTH] - 1);
593
                offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
594
                if (offs <= 0)
595
                  offs += 7;
596
                day += offs;
597
              }
598
          }
599
 
600
        // 1:  YEAR + MONTH + DAY_OF_MONTH
601
      }
602
    if (era == BC && year > 0)
603
      year = 1 - year;
604
 
605
    // rest of code assumes day/month/year set
606
    // should negative BC years be AD?
607
    // get the hour (but no check for validity)
608
    if (isSet[HOUR])
609
      {
610
        hour = fields[HOUR];
611
        if (fields[AM_PM] == PM)
612
          hour += 12;
613
      }
614
    else
615
      hour = fields[HOUR_OF_DAY];
616
 
617
    // Read the era,year,month,day fields and convert as appropriate.
618
    // Calculate number of milliseconds into the day
619
    // This takes care of both h, m, s, ms over/underflows.
620
    long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis;
621
    day += allMillis / (24 * 60 * 60 * 1000L);
622
    millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
623
 
624
    if (month < 0)
625
      {
626
        year += (int) month / 12;
627
        month = month % 12;
628
        if (month < 0)
629
          {
630
            month += 12;
631
            year--;
632
          }
633
      }
634
    if (month > 11)
635
      {
636
        year += (month / 12);
637
        month = month % 12;
638
      }
639
 
640
    month_days[1] = isLeapYear(year) ? 29 : 28;
641
 
642
    while (day <= 0)
643
      {
644
        if (month == 0)
645
          {
646
            year--;
647
            month_days[1] = isLeapYear(year) ? 29 : 28;
648
          }
649
        month = (month + 11) % 12;
650
        day += month_days[month];
651
      }
652
    while (day > month_days[month])
653
      {
654
        day -= (month_days[month]);
655
        month = (month + 1) % 12;
656
        if (month == 0)
657
          {
658
            year++;
659
            month_days[1] = isLeapYear(year) ? 29 : 28;
660
          }
661
      }
662
 
663
    // ok, by here we have valid day,month,year,era and millisinday
664
    int dayOfYear = dayCount[month] + day - 1; // (day starts on 1)
665
    if (isLeapYear(year) && month > 1)
666
      dayOfYear++;
667
 
668
    int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
669
                      - EPOCH_DAYS; // gregorian days from 1 to epoch.
670
    int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
671
                     - (int) Math.floor((double) (year - 1) / 100.);
672
 
673
    if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover)
674
      relativeDay += gregFactor;
675
    else
676
      relativeDay -= 2;
677
 
678
    time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay;
679
 
680
    // the epoch was a Thursday.
681
    int weekday = (int) (relativeDay + THURSDAY) % 7;
682
    if (weekday <= 0)
683
      weekday += 7;
684
    fields[DAY_OF_WEEK] = weekday;
685
 
686
    // Time zone corrections.
687
    TimeZone zone = getTimeZone();
688
    int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET]
689
                                       : zone.getRawOffset();
690
 
691
    int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET]
692
                                      : (zone.getOffset((year < 0) ? BC : AD,
693
                                                        (year < 0) ? 1 - year
694
                                                                   : year,
695
                                                        month, day, weekday,
696
                                                        millisInDay)
697
                                      - zone.getRawOffset());
698
 
699
    time -= rawOffset + dstOffset;
700
 
701
    isTimeSet = true;
702
  }
703
 
704
  /**
705
   * Get the linear day in days since the epoch, using the
706
   * Julian or Gregorian calendar as specified.  If you specify a
707
   * nonpositive year it is interpreted as BC as following: 0 is 1
708
   * BC, -1 is 2 BC and so on.
709
   *
710
   * @param year the year of the date.
711
   * @param dayOfYear the day of year of the date; 1 based.
712
   * @param gregorian <code>true</code>, if we should use the Gregorian rules.
713
   * @return the days since the epoch, may be negative.
714
   */
715
  private long getLinearDay(int year, int dayOfYear, boolean gregorian)
716
  {
717
    // The 13 is the number of days, that were omitted in the Gregorian
718
    // Calender until the epoch.
719
    // We shift right by 2 instead of dividing by 4, to get correct
720
    // results for negative years (and this is even more efficient).
721
    long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1)
722
                     - EPOCH_DAYS; // gregorian days from 1 to epoch.
723
 
724
    if (gregorian)
725
      {
726
        // subtract the days that are missing in gregorian calendar
727
        // with respect to julian calendar.
728
        //
729
        // Okay, here we rely on the fact that the gregorian
730
        // calendar was introduced in the AD era.  This doesn't work
731
        // with negative years.
732
        //
733
        // The additional leap year factor accounts for the fact that
734
        // a leap day is not seen on Jan 1 of the leap year.
735
        int gregOffset = (int) Math.floor((double) (year - 1) / 400.)
736
                         - (int) Math.floor((double) (year - 1) / 100.);
737
 
738
        return julianDay + gregOffset;
739
      }
740
    else
741
      julianDay -= 2;
742
    return julianDay;
743
  }
744
 
745
  /**
746
   * Converts the given linear day into era, year, month,
747
   * day_of_year, day_of_month, day_of_week, and writes the result
748
   * into the fields array.
749
   *
750
   * @param day the linear day.
751
   * @param gregorian true, if we should use Gregorian rules.
752
   */
753
  private void calculateDay(int[] fields, long day, boolean gregorian)
754
  {
755
    // the epoch was a Thursday.
756
    int weekday = (int) (day + THURSDAY) % 7;
757
    if (weekday <= 0)
758
      weekday += 7;
759
    fields[DAY_OF_WEEK] = weekday;
760
 
761
    // get a first approximation of the year.  This may be one 
762
    // year too big.
763
    int year = 1970
764
               + (int) (gregorian
765
                        ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L
766
                        + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L));
767
    if (day >= 0)
768
      year++;
769
 
770
    long firstDayOfYear = getLinearDay(year, 1, gregorian);
771
 
772
    // Now look in which year day really lies.
773
    if (day < firstDayOfYear)
774
      {
775
        year--;
776
        firstDayOfYear = getLinearDay(year, 1, gregorian);
777
      }
778
 
779
    day -= firstDayOfYear - 1; // day of year,  one based.
780
 
781
    fields[DAY_OF_YEAR] = (int) day;
782
    if (year <= 0)
783
      {
784
        fields[ERA] = BC;
785
        fields[YEAR] = 1 - year;
786
      }
787
    else
788
      {
789
        fields[ERA] = AD;
790
        fields[YEAR] = year;
791
      }
792
 
793
    int leapday = isLeapYear(year) ? 1 : 0;
794
    if (day <= 31 + 28 + leapday)
795
      {
796
        fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY
797
        fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH];
798
      }
799
    else
800
      {
801
        // A few more magic formulas
802
        int scaledDay = ((int) day - leapday) * 5 + 8;
803
        fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
804
        fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
805
      }
806
  }
807
 
808
  /**
809
   * Converts the milliseconds since the epoch UTC
810
   * (<code>time</code>) to time fields
811
   * (<code>fields</code>).
812
   */
813
  protected synchronized void computeFields()
814
  {
815
    boolean gregorian = (time >= gregorianCutover);
816
 
817
    TimeZone zone = getTimeZone();
818
    fields[ZONE_OFFSET] = zone.getRawOffset();
819
    long localTime = time + fields[ZONE_OFFSET];
820
 
821
    long day = localTime / (24 * 60 * 60 * 1000L);
822
    int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
823
 
824
    if (millisInDay < 0)
825
      {
826
        millisInDay += (24 * 60 * 60 * 1000);
827
        day--;
828
      }
829
 
830
    calculateDay(fields, day, gregorian);
831
    fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR],
832
                                        fields[MONTH], fields[DAY_OF_MONTH],
833
                                        fields[DAY_OF_WEEK], millisInDay)
834
                         - fields[ZONE_OFFSET];
835
 
836
    millisInDay += fields[DST_OFFSET];
837
    if (millisInDay >= 24 * 60 * 60 * 1000)
838
      {
839
        millisInDay -= 24 * 60 * 60 * 1000;
840
        calculateDay(fields, ++day, gregorian);
841
      }
842
 
843
    fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
844
 
845
    // which day of the week are we (0..6), relative to getFirstDayOfWeek
846
    int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7;
847
 
848
    fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] - relativeWeekday + 12) / 7;
849
 
850
    int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7;
851
 
852
    // Do the Correction: getMinimalDaysInFirstWeek() is always in the 
853
    // first week.
854
    int minDays = getMinimalDaysInFirstWeek();
855
    int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays)
856
                       - getFirstDayOfWeek()) % 7;
857
    if (minDays - firstWeekday < 1)
858
      weekOfYear++;
859
    fields[WEEK_OF_YEAR] = weekOfYear;
860
 
861
    int hourOfDay = millisInDay / (60 * 60 * 1000);
862
    fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
863
    int hour = hourOfDay % 12;
864
    fields[HOUR] = hour;
865
    fields[HOUR_OF_DAY] = hourOfDay;
866
    millisInDay %= (60 * 60 * 1000);
867
    fields[MINUTE] = millisInDay / (60 * 1000);
868
    millisInDay %= (60 * 1000);
869
    fields[SECOND] = millisInDay / (1000);
870
    fields[MILLISECOND] = millisInDay % 1000;
871
 
872
    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;
873
  }
874
 
875
  /**
876
   * Return a hash code for this object, following the general contract
877
   * specified by {@link Object#hashCode()}.
878
   * @return the hash code
879
   */
880
  public int hashCode()
881
  {
882
    int val = (int) ((gregorianCutover >>> 32) ^ (gregorianCutover & 0xffffffff));
883
    return super.hashCode() ^ val;
884
  }
885
 
886
  /**
887
   * Compares the given calendar with this.  An object, o, is
888
   * equivalent to this if it is also a <code>GregorianCalendar</code>
889
   * with the same time since the epoch under the same conditions
890
   * (same change date and same time zone).
891
   *
892
   * @param o the object to that we should compare.
893
   * @return true, if the given object is a calendar, that represents
894
   * the same time (but doesn't necessarily have the same fields).
895
   * @throws IllegalArgumentException if one of the fields
896
   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
897
   *         specified, if an unknown field is specified or if one
898
   *         of the calendar fields receives an illegal value when
899
   *         leniancy is not enabled.
900
   */
901
  public boolean equals(Object o)
902
  {
903
    if (! (o instanceof GregorianCalendar))
904
      return false;
905
 
906
    GregorianCalendar cal = (GregorianCalendar) o;
907
    return (cal.gregorianCutover == gregorianCutover
908
            && super.equals(o));
909
  }
910
 
911
  /**
912
   * Adds the specified amount of time to the given time field.  The
913
   * amount may be negative to subtract the time.  If the field overflows
914
   * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
915
   * @param field one of the time field constants.
916
   * @param amount the amount of time to add.
917
   * @exception IllegalArgumentException if <code>field</code> is
918
   *   <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or
919
   *   if <code>amount</code> contains an out-of-range value and the calendar
920
   *   is not in lenient mode.
921
   */
922
  public void add(int field, int amount)
923
  {
924
    switch (field)
925
      {
926
      case YEAR:
927
        complete();
928
        fields[YEAR] += amount;
929
        isTimeSet = false;
930
        break;
931
      case MONTH:
932
        complete();
933
        int months = fields[MONTH] + amount;
934
        fields[YEAR] += months / 12;
935
        fields[MONTH] = months % 12;
936
        if (fields[MONTH] < 0)
937
          {
938
            fields[MONTH] += 12;
939
            fields[YEAR]--;
940
          }
941
        int maxDay = getActualMaximum(DAY_OF_MONTH);
942
        if (fields[DAY_OF_MONTH] > maxDay)
943
          fields[DAY_OF_MONTH] = maxDay;
944
        set(YEAR, fields[YEAR]);
945
        set(MONTH, fields[MONTH]);
946
        break;
947
      case DAY_OF_MONTH:
948
      case DAY_OF_YEAR:
949
      case DAY_OF_WEEK:
950
        if (! isTimeSet)
951
          computeTime();
952
        time += amount * (24 * 60 * 60 * 1000L);
953
        areFieldsSet = false;
954
        break;
955
      case WEEK_OF_YEAR:
956
      case WEEK_OF_MONTH:
957
      case DAY_OF_WEEK_IN_MONTH:
958
        if (! isTimeSet)
959
          computeTime();
960
        time += amount * (7 * 24 * 60 * 60 * 1000L);
961
        areFieldsSet = false;
962
        break;
963
      case AM_PM:
964
        if (! isTimeSet)
965
          computeTime();
966
        time += amount * (12 * 60 * 60 * 1000L);
967
        areFieldsSet = false;
968
        break;
969
      case HOUR:
970
      case HOUR_OF_DAY:
971
        if (! isTimeSet)
972
          computeTime();
973
        time += amount * (60 * 60 * 1000L);
974
        areFieldsSet = false;
975
        break;
976
      case MINUTE:
977
        if (! isTimeSet)
978
          computeTime();
979
        time += amount * (60 * 1000L);
980
        areFieldsSet = false;
981
        break;
982
      case SECOND:
983
        if (! isTimeSet)
984
          computeTime();
985
        time += amount * (1000L);
986
        areFieldsSet = false;
987
        break;
988
      case MILLISECOND:
989
        if (! isTimeSet)
990
          computeTime();
991
        time += amount;
992
        areFieldsSet = false;
993
        break;
994
      case ZONE_OFFSET:
995
      case DST_OFFSET:default:
996
        throw new IllegalArgumentException("Invalid or unknown field");
997
      }
998
  }
999
 
1000
  /**
1001
   * Rolls the specified time field up or down.  This means add one
1002
   * to the specified field, but don't change the other fields.  If
1003
   * the maximum for this field is reached, start over with the
1004
   * minimum value.
1005
   *
1006
   * <strong>Note:</strong> There may be situation, where the other
1007
   * fields must be changed, e.g rolling the month on May, 31.
1008
   * The date June, 31 is automatically converted to July, 1.
1009
   * This requires lenient settings.
1010
   *
1011
   * @param field the time field. One of the time field constants.
1012
   * @param up the direction, true for up, false for down.
1013
   * @throws IllegalArgumentException if one of the fields
1014
   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
1015
   *         specified, if an unknown field is specified or if one
1016
   *         of the calendar fields receives an illegal value when
1017
   *         leniancy is not enabled.
1018
   */
1019
  public void roll(int field, boolean up)
1020
  {
1021
    roll(field, up ? 1 : -1);
1022
  }
1023
 
1024
  /**
1025
   * Checks that the fields are still within their legal bounds,
1026
   * following use of the <code>roll()</code> method.
1027
   *
1028
   * @param field the field to check.
1029
   * @param delta multipler for alterations to the <code>time</code>.
1030
   * @see #roll(int, boolean)
1031
   * @see #roll(int, int)
1032
   */
1033
  private void cleanUpAfterRoll(int field, int delta)
1034
  {
1035
    switch (field)
1036
      {
1037
      case ERA:
1038
      case YEAR:
1039
      case MONTH:
1040
        // check that day of month is still in correct range
1041
        if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH))
1042
          fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH);
1043
        isTimeSet = false;
1044
        isSet[WEEK_OF_MONTH] = false;
1045
        isSet[DAY_OF_WEEK] = false;
1046
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1047
        isSet[DAY_OF_YEAR] = false;
1048
        isSet[WEEK_OF_YEAR] = false;
1049
        break;
1050
      case DAY_OF_MONTH:
1051
        isSet[WEEK_OF_MONTH] = false;
1052
        isSet[DAY_OF_WEEK] = false;
1053
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1054
        isSet[DAY_OF_YEAR] = false;
1055
        isSet[WEEK_OF_YEAR] = false;
1056
        time += delta * (24 * 60 * 60 * 1000L);
1057
        break;
1058
      case WEEK_OF_MONTH:
1059
        isSet[DAY_OF_MONTH] = false;
1060
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1061
        isSet[DAY_OF_YEAR] = false;
1062
        isSet[WEEK_OF_YEAR] = false;
1063
        time += delta * (7 * 24 * 60 * 60 * 1000L);
1064
        break;
1065
      case DAY_OF_WEEK_IN_MONTH:
1066
        isSet[DAY_OF_MONTH] = false;
1067
        isSet[WEEK_OF_MONTH] = false;
1068
        isSet[DAY_OF_YEAR] = false;
1069
        isSet[WEEK_OF_YEAR] = false;
1070
        time += delta * (7 * 24 * 60 * 60 * 1000L);
1071
        break;
1072
      case DAY_OF_YEAR:
1073
        isSet[MONTH] = false;
1074
        isSet[DAY_OF_MONTH] = false;
1075
        isSet[WEEK_OF_MONTH] = false;
1076
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1077
        isSet[DAY_OF_WEEK] = false;
1078
        isSet[WEEK_OF_YEAR] = false;
1079
        time += delta * (24 * 60 * 60 * 1000L);
1080
        break;
1081
      case WEEK_OF_YEAR:
1082
        isSet[MONTH] = false;
1083
        isSet[DAY_OF_MONTH] = false;
1084
        isSet[WEEK_OF_MONTH] = false;
1085
        isSet[DAY_OF_WEEK_IN_MONTH] = false;
1086
        isSet[DAY_OF_YEAR] = false;
1087
        time += delta * (7 * 24 * 60 * 60 * 1000L);
1088
        break;
1089
      case AM_PM:
1090
        isSet[HOUR_OF_DAY] = false;
1091
        time += delta * (12 * 60 * 60 * 1000L);
1092
        break;
1093
      case HOUR:
1094
        isSet[HOUR_OF_DAY] = false;
1095
        time += delta * (60 * 60 * 1000L);
1096
        break;
1097
      case HOUR_OF_DAY:
1098
        isSet[HOUR] = false;
1099
        isSet[AM_PM] = false;
1100
        time += delta * (60 * 60 * 1000L);
1101
        break;
1102
      case MINUTE:
1103
        time += delta * (60 * 1000L);
1104
        break;
1105
      case SECOND:
1106
        time += delta * (1000L);
1107
        break;
1108
      case MILLISECOND:
1109
        time += delta;
1110
        break;
1111
      }
1112
  }
1113
 
1114
  /**
1115
   * Rolls the specified time field by the given amount.  This means
1116
   * add amount to the specified field, but don't change the other
1117
   * fields.  If the maximum for this field is reached, start over
1118
   * with the minimum value and vice versa for negative amounts.
1119
   *
1120
   * <strong>Note:</strong> There may be situation, where the other
1121
   * fields must be changed, e.g rolling the month on May, 31.
1122
   * The date June, 31 is automatically corrected to June, 30.
1123
   *
1124
   * @param field the time field. One of the time field constants.
1125
   * @param amount the amount by which we should roll.
1126
   * @throws IllegalArgumentException if one of the fields
1127
   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
1128
   *         specified, if an unknown field is specified or if one
1129
   *         of the calendar fields receives an illegal value when
1130
   *         leniancy is not enabled.
1131
   */
1132
  public void roll(int field, int amount)
1133
  {
1134
    switch (field)
1135
      {
1136
      case DAY_OF_WEEK:
1137
        // day of week is special: it rolls automatically
1138
        add(field, amount);
1139
        return;
1140
      case ZONE_OFFSET:
1141
      case DST_OFFSET:
1142
        throw new IllegalArgumentException("Can't roll time zone");
1143
      }
1144
    complete();
1145
    int min = getActualMinimum(field);
1146
    int range = getActualMaximum(field) - min + 1;
1147
    int oldval = fields[field];
1148
    int newval = (oldval - min + range + amount) % range + min;
1149
    if (newval < min)
1150
      newval += range;
1151
    fields[field] = newval;
1152
    cleanUpAfterRoll(field, newval - oldval);
1153
  }
1154
 
1155
  /**
1156
   * The minimum values for the calendar fields.
1157
   */
1158
  private static final int[] minimums =
1159
                                        {
1160
                                          BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM,
1161
                                          1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000),
1162
 
1163
                                        };
1164
 
1165
  /**
1166
   * The maximum values for the calendar fields.
1167
   */
1168
  private static final int[] maximums =
1169
                                        {
1170
                                          AD, 5000000, 11, 53, 5, 31, 366,
1171
                                          SATURDAY, 5, PM, 12, 23, 59, 59, 999,
1172
                                          +(12 * 60 * 60 * 1000),
1173
                                          (12 * 60 * 60 * 1000)
1174
                                        };
1175
 
1176
  /**
1177
   * Gets the smallest value that is allowed for the specified field.
1178
   *
1179
   * @param field one of the time field constants.
1180
   * @return the smallest value for the specified field.
1181
   */
1182
  public int getMinimum(int field)
1183
  {
1184
    return minimums[field];
1185
  }
1186
 
1187
  /**
1188
   * Gets the biggest value that is allowed for the specified field.
1189
   *
1190
   * @param field one of the time field constants.
1191
   * @return the biggest value.
1192
   */
1193
  public int getMaximum(int field)
1194
  {
1195
    return maximums[field];
1196
  }
1197
 
1198
  /**
1199
   * Gets the greatest minimum value that is allowed for the specified field.
1200
   * This is the largest value returned by the <code>getActualMinimum(int)</code>
1201
   * method.
1202
   *
1203
   * @param field the time field. One of the time field constants.
1204
   * @return the greatest minimum value.
1205
   * @see #getActualMinimum(int)
1206
   */
1207
  public int getGreatestMinimum(int field)
1208
  {
1209
    if (field == WEEK_OF_YEAR)
1210
      return 1;
1211
    return minimums[field];
1212
  }
1213
 
1214
  /**
1215
   * Gets the smallest maximum value that is allowed for the
1216
   * specified field.  This is the smallest value returned
1217
   * by the <code>getActualMaximum(int)</code>.  For example,
1218
   * this is 28 for DAY_OF_MONTH (as all months have at least
1219
   * 28 days).
1220
   *
1221
   * @param field the time field. One of the time field constants.
1222
   * @return the least maximum value.
1223
   * @see #getActualMaximum(int)
1224
   * @since 1.2
1225
   */
1226
  public int getLeastMaximum(int field)
1227
  {
1228
    switch (field)
1229
      {
1230
      case WEEK_OF_YEAR:
1231
        return 52;
1232
      case DAY_OF_MONTH:
1233
        return 28;
1234
      case DAY_OF_YEAR:
1235
        return 365;
1236
      case DAY_OF_WEEK_IN_MONTH:
1237
      case WEEK_OF_MONTH:
1238
        return 4;
1239
      default:
1240
        return maximums[field];
1241
      }
1242
  }
1243
 
1244
  /**
1245
   * Gets the actual minimum value that is allowed for the specified field.
1246
   * This value is dependent on the values of the other fields.  Note that
1247
   * this calls <code>complete()</code> if not enough fields are set.  This
1248
   * can have ugly side effects.  The value given depends on the current
1249
   * time used by this instance.
1250
   *
1251
   * @param field the time field. One of the time field constants.
1252
   * @return the actual minimum value.
1253
   * @since 1.2
1254
   */
1255
  public int getActualMinimum(int field)
1256
  {
1257
    if (field == WEEK_OF_YEAR)
1258
      {
1259
        int min = getMinimalDaysInFirstWeek();
1260
        if (min == 0)
1261
          return 1;
1262
        if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
1263
          complete();
1264
 
1265
        int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1266
        int weekday = getWeekDay(year, min);
1267
        if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1)
1268
          return 1;
1269
        return 0;
1270
      }
1271
    return minimums[field];
1272
  }
1273
 
1274
  /**
1275
   * Gets the actual maximum value that is allowed for the specified field.
1276
   * This value is dependent on the values of the other fields.  Note that
1277
   * this calls <code>complete()</code> if not enough fields are set.  This
1278
   * can have ugly side effects.  The value given depends on the current time
1279
   * used by this instance; thus, leap years have a maximum day of month value of
1280
   * 29, rather than 28.
1281
   *
1282
   * @param field the time field. One of the time field constants.
1283
   * @return the actual maximum value.
1284
   */
1285
  public int getActualMaximum(int field)
1286
  {
1287
    switch (field)
1288
      {
1289
      case WEEK_OF_YEAR:
1290
        {
1291
          if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
1292
            complete();
1293
 
1294
          // This is wrong for the year that contains the gregorian change.
1295
          // I.e it gives the weeks in the julian year or in the gregorian
1296
          // year in that case.
1297
          int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1298
          int lastDay = isLeapYear(year) ? 366 : 365;
1299
          int weekday = getWeekDay(year, lastDay);
1300
          int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
1301
 
1302
          int minimalDays = getMinimalDaysInFirstWeek();
1303
          int firstWeekday = getWeekDay(year, minimalDays);
1304
          /*
1305
           * Is there a set of days at the beginning of the year, before the
1306
           * first day of the week, equal to or greater than the minimum number
1307
           * of days required in the first week?
1308
           */
1309
          if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
1310
            return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */
1311
        }
1312
      case DAY_OF_MONTH:
1313
        {
1314
          if (! areFieldsSet || ! isSet[MONTH])
1315
            complete();
1316
          int month = fields[MONTH];
1317
 
1318
          // If you change this, you should also change 
1319
          // SimpleTimeZone.getDaysInMonth();
1320
          if (month == FEBRUARY)
1321
            {
1322
              if (! isSet[YEAR] || ! isSet[ERA])
1323
                complete();
1324
              int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1325
              return isLeapYear(year) ? 29 : 28;
1326
            }
1327
          else if (month < AUGUST)
1328
            return 31 - (month & 1);
1329
          else
1330
            return 30 + (month & 1);
1331
        }
1332
      case DAY_OF_YEAR:
1333
        {
1334
          if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
1335
            complete();
1336
          int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
1337
          return isLeapYear(year) ? 366 : 365;
1338
        }
1339
      case DAY_OF_WEEK_IN_MONTH:
1340
        {
1341
          // This is wrong for the month that contains the gregorian change.
1342
          int daysInMonth = getActualMaximum(DAY_OF_MONTH);
1343
 
1344
          // That's black magic, I know
1345
          return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
1346
        }
1347
      case WEEK_OF_MONTH:
1348
        {
1349
          int daysInMonth = getActualMaximum(DAY_OF_MONTH);
1350
          int weekday = (daysInMonth - fields[DAY_OF_MONTH]
1351
                        + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
1352
          return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
1353
        }
1354
      default:
1355
        return maximums[field];
1356
      }
1357
  }
1358
}

powered by: WebSVN 2.1.0

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