1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366 |
- /* java.util.GregorianCalendar
- Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2007
- Free Software Foundation, Inc.
- This file is part of GNU Classpath.
- GNU Classpath is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
- GNU Classpath is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with GNU Classpath; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301 USA.
- Linking this library statically or dynamically with other modules is
- making a combined work based on this library. Thus, the terms and
- conditions of the GNU General Public License cover the whole
- combination.
- As a special exception, the copyright holders of this library give you
- permission to link this library with independent modules to produce an
- executable, regardless of the license terms of these independent
- modules, and to copy and distribute the resulting executable under
- terms of your choice, provided that you also meet, for each linked
- independent module, the terms and conditions of the license of that
- module. An independent module is a module which is not derived from
- or based on this library. If you modify this library, you may extend
- this exception to your version of the library, but you are not
- obligated to do so. If you do not wish to do so, delete this
- exception statement from your version. */
- package java.util;
- /**
- * <p>
- * This class represents the Gregorian calendar, that is used in most
- * countries all over the world. It does also handle the Julian calendar
- * for dates smaller than the date of the change to the Gregorian calendar.
- * The Gregorian calendar differs from the Julian calendar by a different
- * leap year rule (no leap year every 100 years, except if year is divisible
- * by 400).
- * </p>
- * <p>
- * This change date is different from country to country, and can be changed with
- * <code>setGregorianChange</code>. The first countries to adopt the Gregorian
- * calendar did so on the 15th of October, 1582. This date followed October
- * the 4th, 1582 in the Julian calendar system. The non-existant days that were
- * omitted when the change took place are interpreted as Gregorian dates.
- * </p>
- * <p>
- * Prior to the changeover date, New Year's Day occurred on the 25th of March.
- * However, this class always takes New Year's Day as being the 1st of January.
- * Client code should manually adapt the year value, if required, for dates
- * between January the 1st and March the 24th in years prior to the changeover.
- * </p>
- * <p>
- * Any date infinitely forwards or backwards in time can be represented by
- * this class. A <em>proleptic</em> calendar system is used, which allows
- * future dates to be created via the existing rules. This allows meaningful
- * and consistent dates to be produced for all years. However, dates are only
- * historically accurate following March the 1st, 4AD when the Julian calendar
- * system was adopted. Prior to this, leap year rules were applied erraticly.
- * </p>
- * <p>
- * There are two eras available for the Gregorian calendar, namely BC and AD.
- * </p>
- * <p>
- * Weeks are defined as a period of seven days, beginning on the first day
- * of the week, as returned by <code>getFirstDayOfWeek()</code>, and ending
- * on the day prior to this.
- * </p>
- * <p>
- * The weeks of the year are numbered from 1 to a possible 53. The first week
- * of the year is defined as the first week that contains at least the minimum
- * number of days of the first week in the new year (retrieved via
- * <code>getMinimalDaysInFirstWeek()</code>). All weeks after this are numbered
- * from 2 onwards.
- * </p>
- * <p>
- * For example, take the year 2004. It began on a Thursday. The first week
- * of 2004 depends both on where a week begins and how long it must minimally
- * last. Let's say that the week begins on a Monday and must have a minimum
- * of 5 days. In this case, the first week begins on Monday, the 5th of January.
- * The first 4 days (Thursday to Sunday) are not eligible, as they are too few
- * to make up the minimum number of days of the first week which must be in
- * the new year. If the minimum was lowered to 4 days, then the first week
- * would instead begin on Monday, the 29th of December, 2003. This first week
- * has 4 of its days in the new year, and is now eligible.
- * </p>
- * <p>
- * The weeks of the month are numbered from 0 to a possible 6. The first week
- * of the month (numbered 1) is a set of days, prior to the first day of the week,
- * which number at least the minimum number of days in a week. Unlike the first
- * week of the year, the first week of the month only uses days from that particular
- * month. As a consequence, it may have a variable number of days (from the minimum
- * number required up to a full week of 7) and it need not start on the first day of
- * the week. It must, however, be following by the first day of the week, as this
- * marks the beginning of week 2. Any days of the month which occur prior to the
- * first week (because the first day of the week occurs before the minimum number
- * of days is met) are seen as week 0.
- * </p>
- * <p>
- * Again, we will take the example of the year 2004 to demonstrate this. September
- * 2004 begins on a Wednesday. Taking our first day of the week as Monday, and the
- * minimum length of the first week as 6, we find that week 1 runs from Monday,
- * the 6th of September to Sunday the 12th. Prior to the 6th, there are only
- * 5 days (Wednesday through to Sunday). This is too small a number to meet the
- * minimum, so these are classed as being days in week 0. Week 2 begins on the
- * 13th, and so on. This changes if we reduce the minimum to 5. In this case,
- * week 1 is a truncated week from Wednesday the 1st to Sunday the 5th, and week
- * 0 doesn't exist. The first seven day week is week 2, starting on the 6th.
- * </p>
- * <p>
- * On using the <code>clear()</code> method, the Gregorian calendar returns
- * to its default value of the 1st of January, 1970 AD 00:00:00 (the epoch).
- * The day of the week is set to the correct day for that particular time.
- * The day is also the first of the month, and the date is in week 0.
- * </p>
- *
- * @see Calendar
- * @see TimeZone
- * @see Calendar#getFirstDayOfWeek()
- * @see Calendar#getMinimalDaysInFirstWeek()
- */
- public class GregorianCalendar extends Calendar
- {
- /**
- * Constant representing the era BC (Before Christ).
- */
- public static final int BC = 0;
- /**
- * Constant representing the era AD (Anno Domini).
- */
- public static final int AD = 1;
- /**
- * The point at which the Gregorian calendar rules were used.
- * This may be changed by using setGregorianChange;
- * The default is midnight (UTC) on October 5, 1582 (Julian),
- * or October 15, 1582 (Gregorian).
- *
- * @serial the changeover point from the Julian calendar
- * system to the Gregorian.
- */
- private long gregorianCutover = (new Date((24 * 60 * 60 * 1000L) * (((1582 * (365 * 4
- + 1)) / 4
- + (java.util.Calendar.OCTOBER * (31
- + 30 + 31 + 30 + 31) - 9) / 5 + 5)
- - ((1970 * (365 * 4 + 1)) / 4 + 1
- - 13)))).getTime();
- /**
- * For compatability with Sun's JDK.
- */
- static final long serialVersionUID = -8125100834729963327L;
- /**
- * Days in the epoch. Relative Jan 1, year '0' which is not a leap year.
- * (although there is no year zero, this does not matter.)
- * This is consistent with the formula:
- * = (year-1)*365L + ((year-1) >> 2)
- *
- * Plus the gregorian correction:
- * Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.);
- * For a correct julian date, the correction is -2 instead.
- *
- * The gregorian cutover in 1582 was 10 days, so by calculating the
- * correction from year zero, we have 15 non-leap days (even centuries)
- * minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects
- * this to the correct number 10.
- */
- private static final int EPOCH_DAYS = 719162;
- /**
- * Constructs a new GregorianCalender representing the current
- * time, using the default time zone and the default locale.
- */
- public GregorianCalendar()
- {
- this(TimeZone.getDefault(), Locale.getDefault());
- }
- /**
- * Constructs a new GregorianCalender representing the current
- * time, using the specified time zone and the default locale.
- *
- * @param zone a time zone.
- */
- public GregorianCalendar(TimeZone zone)
- {
- this(zone, Locale.getDefault());
- }
- /**
- * Constructs a new GregorianCalender representing the current
- * time, using the default time zone and the specified locale.
- *
- * @param locale a locale.
- */
- public GregorianCalendar(Locale locale)
- {
- this(TimeZone.getDefault(), locale);
- }
- /**
- * Constructs a new GregorianCalender representing the current
- * time with the given time zone and the given locale.
- *
- * @param zone a time zone.
- * @param locale a locale.
- */
- public GregorianCalendar(TimeZone zone, Locale locale)
- {
- this(zone, locale, false);
- setTimeInMillis(System.currentTimeMillis());
- }
- /**
- * Common constructor that all constructors should call.
- * @param zone a time zone.
- * @param locale a locale.
- * @param unused unused parameter to make the signature differ from
- * the public constructor (TimeZone, Locale).
- */
- private GregorianCalendar(TimeZone zone, Locale locale, boolean unused)
- {
- super(zone, locale);
- }
- /**
- * Constructs a new GregorianCalendar representing midnight on the
- * given date with the default time zone and locale.
- *
- * @param year corresponds to the YEAR time field.
- * @param month corresponds to the MONTH time field.
- * @param day corresponds to the DAY time field.
- */
- public GregorianCalendar(int year, int month, int day)
- {
- this(TimeZone.getDefault(), Locale.getDefault(), false);
- set(year, month, day);
- }
- /**
- * Constructs a new GregorianCalendar representing midnight on the
- * given date with the default time zone and locale.
- *
- * @param year corresponds to the YEAR time field.
- * @param month corresponds to the MONTH time field.
- * @param day corresponds to the DAY time field.
- * @param hour corresponds to the HOUR_OF_DAY time field.
- * @param minute corresponds to the MINUTE time field.
- */
- public GregorianCalendar(int year, int month, int day, int hour, int minute)
- {
- this(TimeZone.getDefault(), Locale.getDefault(), false);
- set(year, month, day, hour, minute);
- }
- /**
- * Constructs a new GregorianCalendar representing midnight on the
- * given date with the default time zone and locale.
- *
- * @param year corresponds to the YEAR time field.
- * @param month corresponds to the MONTH time field.
- * @param day corresponds to the DAY time field.
- * @param hour corresponds to the HOUR_OF_DAY time field.
- * @param minute corresponds to the MINUTE time field.
- * @param second corresponds to the SECOND time field.
- */
- public GregorianCalendar(int year, int month, int day, int hour, int minute,
- int second)
- {
- this(TimeZone.getDefault(), Locale.getDefault(), false);
- set(year, month, day, hour, minute, second);
- }
- /**
- * Sets the date of the switch from Julian dates to Gregorian dates.
- * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure
- * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian
- * calendar.
- *
- * @param date the date of the change.
- */
- public void setGregorianChange(Date date)
- {
- gregorianCutover = date.getTime();
- }
- /**
- * Gets the date of the switch from Julian dates to Gregorian dates.
- *
- * @return the date of the change.
- */
- public final Date getGregorianChange()
- {
- return new Date(gregorianCutover);
- }
- /**
- * <p>
- * Determines if the given year is a leap year. The result is
- * undefined if the Gregorian change took place in 1800, so that
- * the end of February is skipped, and that year is specified.
- * (well...).
- * </p>
- * <p>
- * To specify a year in the BC era, use a negative value calculated
- * as 1 - y, where y is the required year in BC. So, 1 BC is 0,
- * 2 BC is -1, 3 BC is -2, etc.
- * </p>
- *
- * @param year a year (use a negative value for BC).
- * @return true, if the given year is a leap year, false otherwise.
- */
- public boolean isLeapYear(int year)
- {
- // Only years divisible by 4 can be leap years
- if ((year & 3) != 0)
- return false;
- // Is the leap-day a Julian date? Then it's a leap year
- if (! isGregorian(year, 31 + 29 - 1))
- return true;
- // Apply gregorian rules otherwise
- return ((year % 100) != 0 || (year % 400) == 0);
- }
- /**
- * Retrieves the day of the week corresponding to the specified
- * day of the specified year.
- *
- * @param year the year in which the dayOfYear occurs.
- * @param dayOfYear the day of the year (an integer between 0 and
- * and 366)
- */
- private int getWeekDay(int year, int dayOfYear)
- {
- boolean greg = isGregorian(year, dayOfYear);
- int day = (int) getLinearDay(year, dayOfYear, greg);
- // The epoch was a thursday.
- int weekday = (day + THURSDAY) % 7;
- if (weekday <= 0)
- weekday += 7;
- return weekday;
- }
- /**
- * Returns the day of the week for the first day of a given month (0..11)
- */
- private int getFirstDayOfMonth(int year, int month)
- {
- int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
- if (month > 11)
- {
- year += (month / 12);
- month = month % 12;
- }
- if (month < 0)
- {
- year += (int) month / 12;
- month = month % 12;
- if (month < 0)
- {
- month += 12;
- year--;
- }
- }
- int dayOfYear = dayCount[month] + 1;
- if (month > 1)
- if (isLeapYear(year))
- dayOfYear++;
- boolean greg = isGregorian(year, dayOfYear);
- int day = (int) getLinearDay(year, dayOfYear, greg);
- // The epoch was a thursday.
- int weekday = (day + THURSDAY) % 7;
- if (weekday <= 0)
- weekday += 7;
- return weekday;
- }
- /**
- * Takes a year, and a (zero based) day of year and determines
- * if it is gregorian or not.
- */
- private boolean isGregorian(int year, int dayOfYear)
- {
- int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
- - EPOCH_DAYS; // gregorian days from 1 to epoch.
- int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
- - (int) Math.floor((double) (year - 1) / 100.);
- return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover);
- }
- /**
- * Check set fields for validity, without leniency.
- *
- * @throws IllegalArgumentException if a field is invalid
- */
- private void nonLeniencyCheck() throws IllegalArgumentException
- {
- int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
- int year = fields[YEAR];
- int month = fields[MONTH];
- int leap = isLeapYear(year) ? 1 : 0;
- if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC)
- throw new IllegalArgumentException("Illegal ERA.");
- if (isSet[YEAR] && fields[YEAR] < 1)
- throw new IllegalArgumentException("Illegal YEAR.");
- if (isSet[MONTH] && (month < 0 || month > 11))
- throw new IllegalArgumentException("Illegal MONTH.");
- if (isSet[WEEK_OF_YEAR])
- {
- int daysInYear = 365 + leap;
- daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week
- int last = getFirstDayOfMonth(year, 11) + 4;
- if (last > 7)
- last -= 7;
- daysInYear += 7 - last;
- int weeks = daysInYear / 7;
- if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks)
- throw new IllegalArgumentException("Illegal WEEK_OF_YEAR.");
- }
- if (isSet[WEEK_OF_MONTH])
- {
- int weeks = (month == 1 && leap == 0) ? 5 : 6;
- if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks)
- throw new IllegalArgumentException("Illegal WEEK_OF_MONTH.");
- }
- if (isSet[DAY_OF_MONTH])
- if (fields[DAY_OF_MONTH] < 1
- || fields[DAY_OF_MONTH] > month_days[month]
- + ((month == 1) ? leap : 0))
- throw new IllegalArgumentException("Illegal DAY_OF_MONTH.");
- if (isSet[DAY_OF_YEAR]
- && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap))
- throw new IllegalArgumentException("Illegal DAY_OF_YEAR.");
- if (isSet[DAY_OF_WEEK]
- && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7))
- throw new IllegalArgumentException("Illegal DAY_OF_WEEK.");
- if (isSet[DAY_OF_WEEK_IN_MONTH])
- {
- int weeks = (month == 1 && leap == 0) ? 4 : 5;
- if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks
- || fields[DAY_OF_WEEK_IN_MONTH] > weeks)
- throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH.");
- }
- if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM)
- throw new IllegalArgumentException("Illegal AM_PM.");
- if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11))
- throw new IllegalArgumentException("Illegal HOUR.");
- if (isSet[HOUR_OF_DAY]
- && (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23))
- throw new IllegalArgumentException("Illegal HOUR_OF_DAY.");
- if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59))
- throw new IllegalArgumentException("Illegal MINUTE.");
- if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59))
- throw new IllegalArgumentException("Illegal SECOND.");
- if (isSet[MILLISECOND]
- && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999))
- throw new IllegalArgumentException("Illegal MILLISECOND.");
- if (isSet[ZONE_OFFSET]
- && (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L
- || fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L))
- throw new IllegalArgumentException("Illegal ZONE_OFFSET.");
- if (isSet[DST_OFFSET]
- && (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L
- || fields[DST_OFFSET] > 12 * 60 * 60 * 1000L))
- throw new IllegalArgumentException("Illegal DST_OFFSET.");
- }
- /**
- * Converts the time field values (<code>fields</code>) to
- * milliseconds since the epoch UTC (<code>time</code>).
- *
- * @throws IllegalArgumentException if any calendar fields
- * are invalid.
- */
- protected synchronized void computeTime()
- {
- int millisInDay = 0;
- int era = fields[ERA];
- int year = fields[YEAR];
- int month = fields[MONTH];
- int day = fields[DAY_OF_MONTH];
- int minute = fields[MINUTE];
- int second = fields[SECOND];
- int millis = fields[MILLISECOND];
- int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
- int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
- int hour = 0;
- if (! isLenient())
- nonLeniencyCheck();
- if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR]))
- {
- // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
- if (isSet[WEEK_OF_YEAR])
- {
- int first = getFirstDayOfMonth(year, 0);
- int offs = 1;
- int daysInFirstWeek = getFirstDayOfWeek() - first;
- if (daysInFirstWeek <= 0)
- daysInFirstWeek += 7;
- if (daysInFirstWeek < getMinimalDaysInFirstWeek())
- offs += daysInFirstWeek;
- else
- offs -= 7 - daysInFirstWeek;
- month = 0;
- day = offs + 7 * (fields[WEEK_OF_YEAR] - 1);
- offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
- if (offs < 0)
- offs += 7;
- day += offs;
- }
- else
- {
- // 4: YEAR + DAY_OF_YEAR
- month = 0;
- day = fields[DAY_OF_YEAR];
- }
- }
- else
- {
- if (isSet[DAY_OF_WEEK])
- {
- int first = getFirstDayOfMonth(year, month);
- // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
- if (isSet[DAY_OF_WEEK_IN_MONTH])
- {
- if (fields[DAY_OF_WEEK_IN_MONTH] < 0)
- {
- month++;
- first = getFirstDayOfMonth(year, month);
- day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH]);
- }
- else
- day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1);
- int offs = fields[DAY_OF_WEEK] - first;
- if (offs < 0)
- offs += 7;
- day += offs;
- }
- else
- { // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
- int offs = 1;
- int daysInFirstWeek = getFirstDayOfWeek() - first;
- if (daysInFirstWeek <= 0)
- daysInFirstWeek += 7;
- if (daysInFirstWeek < getMinimalDaysInFirstWeek())
- offs += daysInFirstWeek;
- else
- offs -= 7 - daysInFirstWeek;
- day = offs + 7 * (fields[WEEK_OF_MONTH] - 1);
- offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
- if (offs < 0)
- offs += 7;
- day += offs;
- }
- }
- // 1: YEAR + MONTH + DAY_OF_MONTH
- }
- if (era == BC && year > 0)
- year = 1 - year;
- // rest of code assumes day/month/year set
- // should negative BC years be AD?
- // get the hour (but no check for validity)
- if (isSet[HOUR])
- {
- hour = fields[HOUR];
- if (fields[AM_PM] == PM)
- hour += 12;
- }
- else
- hour = fields[HOUR_OF_DAY];
- // Read the era,year,month,day fields and convert as appropriate.
- // Calculate number of milliseconds into the day
- // This takes care of both h, m, s, ms over/underflows.
- long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis;
- day += allMillis / (24 * 60 * 60 * 1000L);
- millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
- if (month < 0)
- {
- year += (int) month / 12;
- month = month % 12;
- if (month < 0)
- {
- month += 12;
- year--;
- }
- }
- if (month > 11)
- {
- year += (month / 12);
- month = month % 12;
- }
- month_days[1] = isLeapYear(year) ? 29 : 28;
- while (day <= 0)
- {
- if (month == 0)
- {
- year--;
- month_days[1] = isLeapYear(year) ? 29 : 28;
- }
- month = (month + 11) % 12;
- day += month_days[month];
- }
- while (day > month_days[month])
- {
- day -= (month_days[month]);
- month = (month + 1) % 12;
- if (month == 0)
- {
- year++;
- month_days[1] = isLeapYear(year) ? 29 : 28;
- }
- }
- // ok, by here we have valid day,month,year,era and millisinday
- int dayOfYear = dayCount[month] + day - 1; // (day starts on 1)
- if (isLeapYear(year) && month > 1)
- dayOfYear++;
- int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
- - EPOCH_DAYS; // gregorian days from 1 to epoch.
- int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
- - (int) Math.floor((double) (year - 1) / 100.);
- if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover)
- relativeDay += gregFactor;
- else
- relativeDay -= 2;
- time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay;
- // the epoch was a Thursday.
- int weekday = (int) (relativeDay + THURSDAY) % 7;
- if (weekday <= 0)
- weekday += 7;
- fields[DAY_OF_WEEK] = weekday;
- // Time zone corrections.
- TimeZone zone = getTimeZone();
- int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET]
- : zone.getRawOffset();
- int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET]
- : (zone.getOffset((year < 0) ? BC : AD,
- (year < 0) ? 1 - year
- : year,
- month, day, weekday,
- millisInDay)
- - zone.getRawOffset());
- time -= rawOffset + dstOffset;
- isTimeSet = true;
- }
- /**
- * Get the linear day in days since the epoch, using the
- * Julian or Gregorian calendar as specified. If you specify a
- * nonpositive year it is interpreted as BC as following: 0 is 1
- * BC, -1 is 2 BC and so on.
- *
- * @param year the year of the date.
- * @param dayOfYear the day of year of the date; 1 based.
- * @param gregorian <code>true</code>, if we should use the Gregorian rules.
- * @return the days since the epoch, may be negative.
- */
- private long getLinearDay(int year, int dayOfYear, boolean gregorian)
- {
- // The 13 is the number of days, that were omitted in the Gregorian
- // Calender until the epoch.
- // We shift right by 2 instead of dividing by 4, to get correct
- // results for negative years (and this is even more efficient).
- long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1)
- - EPOCH_DAYS; // gregorian days from 1 to epoch.
- if (gregorian)
- {
- // subtract the days that are missing in gregorian calendar
- // with respect to julian calendar.
- //
- // Okay, here we rely on the fact that the gregorian
- // calendar was introduced in the AD era. This doesn't work
- // with negative years.
- //
- // The additional leap year factor accounts for the fact that
- // a leap day is not seen on Jan 1 of the leap year.
- int gregOffset = (int) Math.floor((double) (year - 1) / 400.)
- - (int) Math.floor((double) (year - 1) / 100.);
- return julianDay + gregOffset;
- }
- else
- julianDay -= 2;
- return julianDay;
- }
- /**
- * Converts the given linear day into era, year, month,
- * day_of_year, day_of_month, day_of_week, and writes the result
- * into the fields array.
- *
- * @param day the linear day.
- * @param gregorian true, if we should use Gregorian rules.
- */
- private void calculateDay(int[] fields, long day, boolean gregorian)
- {
- // the epoch was a Thursday.
- int weekday = (int) (day + THURSDAY) % 7;
- if (weekday <= 0)
- weekday += 7;
- fields[DAY_OF_WEEK] = weekday;
- // get a first approximation of the year. This may be one
- // year too big.
- int year = 1970
- + (int) (gregorian
- ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L
- + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L));
- if (day >= 0)
- year++;
- long firstDayOfYear = getLinearDay(year, 1, gregorian);
- // Now look in which year day really lies.
- if (day < firstDayOfYear)
- {
- year--;
- firstDayOfYear = getLinearDay(year, 1, gregorian);
- }
- day -= firstDayOfYear - 1; // day of year, one based.
- fields[DAY_OF_YEAR] = (int) day;
- if (year <= 0)
- {
- fields[ERA] = BC;
- fields[YEAR] = 1 - year;
- }
- else
- {
- fields[ERA] = AD;
- fields[YEAR] = year;
- }
- int leapday = isLeapYear(year) ? 1 : 0;
- if (day <= 31 + 28 + leapday)
- {
- fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY
- fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH];
- }
- else
- {
- // A few more magic formulas
- int scaledDay = ((int) day - leapday) * 5 + 8;
- fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
- fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
- }
- }
- /**
- * Converts the milliseconds since the epoch UTC
- * (<code>time</code>) to time fields
- * (<code>fields</code>).
- */
- protected synchronized void computeFields()
- {
- boolean gregorian = (time >= gregorianCutover);
- TimeZone zone = getTimeZone();
- fields[ZONE_OFFSET] = zone.getRawOffset();
- long localTime = time + fields[ZONE_OFFSET];
- long day = localTime / (24 * 60 * 60 * 1000L);
- int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
- if (millisInDay < 0)
- {
- millisInDay += (24 * 60 * 60 * 1000);
- day--;
- }
- calculateDay(fields, day, gregorian);
- fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR],
- fields[MONTH], fields[DAY_OF_MONTH],
- fields[DAY_OF_WEEK], millisInDay)
- - fields[ZONE_OFFSET];
- millisInDay += fields[DST_OFFSET];
- if (millisInDay >= 24 * 60 * 60 * 1000)
- {
- millisInDay -= 24 * 60 * 60 * 1000;
- calculateDay(fields, ++day, gregorian);
- }
- fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
- // which day of the week are we (0..6), relative to getFirstDayOfWeek
- int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7;
- // which day of the week is the first of this month?
- // nb 35 is the smallest multiple of 7 that ensures that
- // the left hand side of the modulo operator is positive.
- int relativeWeekdayOfFirst = (relativeWeekday - fields[DAY_OF_MONTH]
- + 1 + 35) % 7;
- // which week of the month is the first of this month in?
- int minDays = getMinimalDaysInFirstWeek();
- int weekOfFirst = ((7 - relativeWeekdayOfFirst) >= minDays) ? 1 : 0;
- // which week of the month is this day in?
- fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH]
- + relativeWeekdayOfFirst - 1) / 7 + weekOfFirst;
- int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7;
- // Do the Correction: getMinimalDaysInFirstWeek() is always in the
- // first week.
- int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays)
- - getFirstDayOfWeek()) % 7;
- if (minDays - firstWeekday < 1)
- weekOfYear++;
- fields[WEEK_OF_YEAR] = weekOfYear;
- int hourOfDay = millisInDay / (60 * 60 * 1000);
- fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
- int hour = hourOfDay % 12;
- fields[HOUR] = hour;
- fields[HOUR_OF_DAY] = hourOfDay;
- millisInDay %= (60 * 60 * 1000);
- fields[MINUTE] = millisInDay / (60 * 1000);
- millisInDay %= (60 * 1000);
- fields[SECOND] = millisInDay / (1000);
- fields[MILLISECOND] = millisInDay % 1000;
- 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;
- }
- /**
- * Return a hash code for this object, following the general contract
- * specified by {@link Object#hashCode()}.
- * @return the hash code
- */
- public int hashCode()
- {
- int val = (int) ((gregorianCutover >>> 32) ^ (gregorianCutover & 0xffffffff));
- return super.hashCode() ^ val;
- }
- /**
- * Compares the given calendar with this. An object, o, is
- * equivalent to this if it is also a <code>GregorianCalendar</code>
- * with the same time since the epoch under the same conditions
- * (same change date and same time zone).
- *
- * @param o the object to that we should compare.
- * @return true, if the given object is a calendar, that represents
- * the same time (but doesn't necessarily have the same fields).
- * @throws IllegalArgumentException if one of the fields
- * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
- * specified, if an unknown field is specified or if one
- * of the calendar fields receives an illegal value when
- * leniancy is not enabled.
- */
- public boolean equals(Object o)
- {
- if (! (o instanceof GregorianCalendar))
- return false;
- GregorianCalendar cal = (GregorianCalendar) o;
- return (cal.gregorianCutover == gregorianCutover
- && super.equals(o));
- }
- /**
- * Adds the specified amount of time to the given time field. The
- * amount may be negative to subtract the time. If the field overflows
- * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
- * @param field one of the time field constants.
- * @param amount the amount of time to add.
- * @exception IllegalArgumentException if <code>field</code> is
- * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or
- * if <code>amount</code> contains an out-of-range value and the calendar
- * is not in lenient mode.
- */
- public void add(int field, int amount)
- {
- switch (field)
- {
- case YEAR:
- complete();
- fields[YEAR] += amount;
- isTimeSet = false;
- break;
- case MONTH:
- complete();
- int months = fields[MONTH] + amount;
- fields[YEAR] += months / 12;
- fields[MONTH] = months % 12;
- if (fields[MONTH] < 0)
- {
- fields[MONTH] += 12;
- fields[YEAR]--;
- }
- int maxDay = getActualMaximum(DAY_OF_MONTH);
- if (fields[DAY_OF_MONTH] > maxDay)
- fields[DAY_OF_MONTH] = maxDay;
- set(YEAR, fields[YEAR]);
- set(MONTH, fields[MONTH]);
- break;
- case DAY_OF_MONTH:
- case DAY_OF_YEAR:
- case DAY_OF_WEEK:
- if (! isTimeSet)
- computeTime();
- time += amount * (24 * 60 * 60 * 1000L);
- areFieldsSet = false;
- break;
- case WEEK_OF_YEAR:
- case WEEK_OF_MONTH:
- case DAY_OF_WEEK_IN_MONTH:
- if (! isTimeSet)
- computeTime();
- time += amount * (7 * 24 * 60 * 60 * 1000L);
- areFieldsSet = false;
- break;
- case AM_PM:
- if (! isTimeSet)
- computeTime();
- time += amount * (12 * 60 * 60 * 1000L);
- areFieldsSet = false;
- break;
- case HOUR:
- case HOUR_OF_DAY:
- if (! isTimeSet)
- computeTime();
- time += amount * (60 * 60 * 1000L);
- areFieldsSet = false;
- break;
- case MINUTE:
- if (! isTimeSet)
- computeTime();
- time += amount * (60 * 1000L);
- areFieldsSet = false;
- break;
- case SECOND:
- if (! isTimeSet)
- computeTime();
- time += amount * (1000L);
- areFieldsSet = false;
- break;
- case MILLISECOND:
- if (! isTimeSet)
- computeTime();
- time += amount;
- areFieldsSet = false;
- break;
- case ZONE_OFFSET:
- case DST_OFFSET:default:
- throw new IllegalArgumentException("Invalid or unknown field");
- }
- }
- /**
- * Rolls the specified time field up or down. This means add one
- * to the specified field, but don't change the other fields. If
- * the maximum for this field is reached, start over with the
- * minimum value.
- *
- * <strong>Note:</strong> There may be situation, where the other
- * fields must be changed, e.g rolling the month on May, 31.
- * The date June, 31 is automatically converted to July, 1.
- * This requires lenient settings.
- *
- * @param field the time field. One of the time field constants.
- * @param up the direction, true for up, false for down.
- * @throws IllegalArgumentException if one of the fields
- * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
- * specified, if an unknown field is specified or if one
- * of the calendar fields receives an illegal value when
- * leniancy is not enabled.
- */
- public void roll(int field, boolean up)
- {
- roll(field, up ? 1 : -1);
- }
- /**
- * Checks that the fields are still within their legal bounds,
- * following use of the <code>roll()</code> method.
- *
- * @param field the field to check.
- * @param delta multipler for alterations to the <code>time</code>.
- * @see #roll(int, boolean)
- * @see #roll(int, int)
- */
- private void cleanUpAfterRoll(int field, int delta)
- {
- switch (field)
- {
- case ERA:
- case YEAR:
- case MONTH:
- // check that day of month is still in correct range
- if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH))
- fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH);
- isTimeSet = false;
- isSet[WEEK_OF_MONTH] = false;
- isSet[DAY_OF_WEEK] = false;
- isSet[DAY_OF_WEEK_IN_MONTH] = false;
- isSet[DAY_OF_YEAR] = false;
- isSet[WEEK_OF_YEAR] = false;
- break;
- case DAY_OF_MONTH:
- isSet[WEEK_OF_MONTH] = false;
- isSet[DAY_OF_WEEK] = false;
- isSet[DAY_OF_WEEK_IN_MONTH] = false;
- isSet[DAY_OF_YEAR] = false;
- isSet[WEEK_OF_YEAR] = false;
- time += delta * (24 * 60 * 60 * 1000L);
- break;
- case WEEK_OF_MONTH:
- isSet[DAY_OF_MONTH] = false;
- isSet[DAY_OF_WEEK_IN_MONTH] = false;
- isSet[DAY_OF_YEAR] = false;
- isSet[WEEK_OF_YEAR] = false;
- time += delta * (7 * 24 * 60 * 60 * 1000L);
- break;
- case DAY_OF_WEEK_IN_MONTH:
- isSet[DAY_OF_MONTH] = false;
- isSet[WEEK_OF_MONTH] = false;
- isSet[DAY_OF_YEAR] = false;
- isSet[WEEK_OF_YEAR] = false;
- time += delta * (7 * 24 * 60 * 60 * 1000L);
- break;
- case DAY_OF_YEAR:
- isSet[MONTH] = false;
- isSet[DAY_OF_MONTH] = false;
- isSet[WEEK_OF_MONTH] = false;
- isSet[DAY_OF_WEEK_IN_MONTH] = false;
- isSet[DAY_OF_WEEK] = false;
- isSet[WEEK_OF_YEAR] = false;
- time += delta * (24 * 60 * 60 * 1000L);
- break;
- case WEEK_OF_YEAR:
- isSet[MONTH] = false;
- isSet[DAY_OF_MONTH] = false;
- isSet[WEEK_OF_MONTH] = false;
- isSet[DAY_OF_WEEK_IN_MONTH] = false;
- isSet[DAY_OF_YEAR] = false;
- time += delta * (7 * 24 * 60 * 60 * 1000L);
- break;
- case AM_PM:
- isSet[HOUR_OF_DAY] = false;
- time += delta * (12 * 60 * 60 * 1000L);
- break;
- case HOUR:
- isSet[HOUR_OF_DAY] = false;
- time += delta * (60 * 60 * 1000L);
- break;
- case HOUR_OF_DAY:
- isSet[HOUR] = false;
- isSet[AM_PM] = false;
- time += delta * (60 * 60 * 1000L);
- break;
- case MINUTE:
- time += delta * (60 * 1000L);
- break;
- case SECOND:
- time += delta * (1000L);
- break;
- case MILLISECOND:
- time += delta;
- break;
- }
- }
- /**
- * Rolls the specified time field by the given amount. This means
- * add amount to the specified field, but don't change the other
- * fields. If the maximum for this field is reached, start over
- * with the minimum value and vice versa for negative amounts.
- *
- * <strong>Note:</strong> There may be situation, where the other
- * fields must be changed, e.g rolling the month on May, 31.
- * The date June, 31 is automatically corrected to June, 30.
- *
- * @param field the time field. One of the time field constants.
- * @param amount the amount by which we should roll.
- * @throws IllegalArgumentException if one of the fields
- * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
- * specified, if an unknown field is specified or if one
- * of the calendar fields receives an illegal value when
- * leniancy is not enabled.
- */
- public void roll(int field, int amount)
- {
- switch (field)
- {
- case DAY_OF_WEEK:
- // day of week is special: it rolls automatically
- add(field, amount);
- return;
- case ZONE_OFFSET:
- case DST_OFFSET:
- throw new IllegalArgumentException("Can't roll time zone");
- }
- complete();
- int min = getActualMinimum(field);
- int range = getActualMaximum(field) - min + 1;
- int oldval = fields[field];
- int newval = (oldval - min + range + amount) % range + min;
- if (newval < min)
- newval += range;
- fields[field] = newval;
- cleanUpAfterRoll(field, newval - oldval);
- }
- /**
- * The minimum values for the calendar fields.
- */
- private static final int[] minimums =
- {
- BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM,
- 1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000),
- 0
- };
- /**
- * The maximum values for the calendar fields.
- */
- private static final int[] maximums =
- {
- AD, 5000000, 11, 53, 6, 31, 366,
- SATURDAY, 5, PM, 12, 23, 59, 59, 999,
- +(12 * 60 * 60 * 1000),
- (12 * 60 * 60 * 1000)
- };
- /**
- * Gets the smallest value that is allowed for the specified field.
- *
- * @param field one of the time field constants.
- * @return the smallest value for the specified field.
- */
- public int getMinimum(int field)
- {
- return minimums[field];
- }
- /**
- * Gets the biggest value that is allowed for the specified field.
- *
- * @param field one of the time field constants.
- * @return the biggest value.
- */
- public int getMaximum(int field)
- {
- return maximums[field];
- }
- /**
- * Gets the greatest minimum value that is allowed for the specified field.
- * This is the largest value returned by the <code>getActualMinimum(int)</code>
- * method.
- *
- * @param field the time field. One of the time field constants.
- * @return the greatest minimum value.
- * @see #getActualMinimum(int)
- */
- public int getGreatestMinimum(int field)
- {
- if (field == WEEK_OF_YEAR)
- return 1;
- return minimums[field];
- }
- /**
- * Gets the smallest maximum value that is allowed for the
- * specified field. This is the smallest value returned
- * by the <code>getActualMaximum(int)</code>. For example,
- * this is 28 for DAY_OF_MONTH (as all months have at least
- * 28 days).
- *
- * @param field the time field. One of the time field constants.
- * @return the least maximum value.
- * @see #getActualMaximum(int)
- * @since 1.2
- */
- public int getLeastMaximum(int field)
- {
- switch (field)
- {
- case WEEK_OF_YEAR:
- return 52;
- case DAY_OF_MONTH:
- return 28;
- case DAY_OF_YEAR:
- return 365;
- case DAY_OF_WEEK_IN_MONTH:
- case WEEK_OF_MONTH:
- return 4;
- default:
- return maximums[field];
- }
- }
- /**
- * Gets the actual minimum value that is allowed for the specified field.
- * This value is dependent on the values of the other fields. Note that
- * this calls <code>complete()</code> if not enough fields are set. This
- * can have ugly side effects. The value given depends on the current
- * time used by this instance.
- *
- * @param field the time field. One of the time field constants.
- * @return the actual minimum value.
- * @since 1.2
- */
- public int getActualMinimum(int field)
- {
- if (field == WEEK_OF_YEAR)
- {
- int min = getMinimalDaysInFirstWeek();
- if (min == 0)
- return 1;
- if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
- complete();
- int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
- int weekday = getWeekDay(year, min);
- if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1)
- return 1;
- return 0;
- }
- return minimums[field];
- }
- /**
- * Gets the actual maximum value that is allowed for the specified field.
- * This value is dependent on the values of the other fields. Note that
- * this calls <code>complete()</code> if not enough fields are set. This
- * can have ugly side effects. The value given depends on the current time
- * used by this instance; thus, leap years have a maximum day of month value of
- * 29, rather than 28.
- *
- * @param field the time field. One of the time field constants.
- * @return the actual maximum value.
- */
- public int getActualMaximum(int field)
- {
- switch (field)
- {
- case WEEK_OF_YEAR:
- {
- if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
- complete();
- // This is wrong for the year that contains the gregorian change.
- // I.e it gives the weeks in the julian year or in the gregorian
- // year in that case.
- int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
- int lastDay = isLeapYear(year) ? 366 : 365;
- int weekday = getWeekDay(year, lastDay);
- int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
- int minimalDays = getMinimalDaysInFirstWeek();
- int firstWeekday = getWeekDay(year, minimalDays);
- /*
- * Is there a set of days at the beginning of the year, before the
- * first day of the week, equal to or greater than the minimum number
- * of days required in the first week?
- */
- if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
- return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */
- }
- case DAY_OF_MONTH:
- {
- if (! areFieldsSet || ! isSet[MONTH])
- complete();
- int month = fields[MONTH];
- // If you change this, you should also change
- // SimpleTimeZone.getDaysInMonth();
- if (month == FEBRUARY)
- {
- if (! isSet[YEAR] || ! isSet[ERA])
- complete();
- int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
- return isLeapYear(year) ? 29 : 28;
- }
- else if (month < AUGUST)
- return 31 - (month & 1);
- else
- return 30 + (month & 1);
- }
- case DAY_OF_YEAR:
- {
- if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
- complete();
- int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
- return isLeapYear(year) ? 366 : 365;
- }
- case DAY_OF_WEEK_IN_MONTH:
- {
- // This is wrong for the month that contains the gregorian change.
- int daysInMonth = getActualMaximum(DAY_OF_MONTH);
- // That's black magic, I know
- return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
- }
- case WEEK_OF_MONTH:
- {
- int daysInMonth = getActualMaximum(DAY_OF_MONTH);
- int weekday = (daysInMonth - fields[DAY_OF_MONTH]
- + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
- return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
- }
- default:
- return maximums[field];
- }
- }
- }
|