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