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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [java/] [util/] [GregorianCalendar.java] - Blame information for rev 778

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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