1 5 6 package com.ibm.icu.util; 7 8 import java.io.IOException ; 9 import java.io.ObjectInputStream ; 10 import java.io.ObjectOutputStream ; 11 import java.io.Serializable ; 12 import java.util.Date ; 13 import java.util.Hashtable ; 14 import java.util.Locale ; 15 import java.util.MissingResourceException ; 16 import java.util.Set ; 17 18 import com.ibm.icu.impl.CalendarData; 19 import com.ibm.icu.impl.ICUCache; 20 import com.ibm.icu.impl.ICUResourceBundle; 21 import com.ibm.icu.impl.SimpleCache; 22 import com.ibm.icu.text.DateFormat; 23 import com.ibm.icu.text.DateFormatSymbols; 24 import com.ibm.icu.text.MessageFormat; 25 import com.ibm.icu.text.SimpleDateFormat; 26 27 634 public abstract class Calendar implements Serializable , Cloneable , Comparable { 635 636 639 649 655 665 669 679 686 694 public final static int ERA = 0; 695 696 701 public final static int YEAR = 1; 702 703 722 public final static int MONTH = 2; 723 724 735 public final static int WEEK_OF_YEAR = 3; 736 737 748 public final static int WEEK_OF_MONTH = 4; 749 750 757 public final static int DATE = 5; 758 759 766 public final static int DAY_OF_MONTH = 5; 767 768 773 public final static int DAY_OF_YEAR = 6; 774 775 789 public final static int DAY_OF_WEEK = 7; 790 791 814 public final static int DAY_OF_WEEK_IN_MONTH = 8; 815 816 825 public final static int AM_PM = 9; 826 827 836 public final static int HOUR = 10; 837 838 845 public final static int HOUR_OF_DAY = 11; 846 847 853 public final static int MINUTE = 12; 854 855 861 public final static int SECOND = 13; 862 863 869 public final static int MILLISECOND = 14; 870 871 876 public final static int ZONE_OFFSET = 15; 877 878 883 public final static int DST_OFFSET = 16; 884 885 892 public static final int YEAR_WOY = 17; 893 894 900 public static final int DOW_LOCAL = 18; 901 902 912 public static final int EXTENDED_YEAR = 19; 913 914 924 public static final int JULIAN_DAY = 20; 925 926 938 public static final int MILLISECONDS_IN_DAY = 21; 939 940 945 protected static final int BASE_FIELD_COUNT = 22; 946 947 952 protected static final int MAX_FIELD_COUNT = 32; 953 954 959 public final static int SUNDAY = 1; 960 961 966 public final static int MONDAY = 2; 967 968 973 public final static int TUESDAY = 3; 974 975 980 public final static int WEDNESDAY = 4; 981 982 987 public final static int THURSDAY = 5; 988 989 994 public final static int FRIDAY = 6; 995 996 1001 public final static int SATURDAY = 7; 1002 1003 1008 public final static int JANUARY = 0; 1009 1010 1015 public final static int FEBRUARY = 1; 1016 1017 1022 public final static int MARCH = 2; 1023 1024 1029 public final static int APRIL = 3; 1030 1031 1036 public final static int MAY = 4; 1037 1038 1043 public final static int JUNE = 5; 1044 1045 1050 public final static int JULY = 6; 1051 1052 1057 public final static int AUGUST = 7; 1058 1059 1064 public final static int SEPTEMBER = 8; 1065 1066 1071 public final static int OCTOBER = 9; 1072 1073 1078 public final static int NOVEMBER = 10; 1079 1080 1085 public final static int DECEMBER = 11; 1086 1087 1093 public final static int UNDECIMBER = 12; 1094 1095 1100 public final static int AM = 0; 1101 1102 1107 public final static int PM = 1; 1108 1109 1118 public static final int WEEKDAY = 0; 1119 1120 1129 public static final int WEEKEND = 1; 1130 1131 1141 public static final int WEEKEND_ONSET = 2; 1142 1143 1153 public static final int WEEKEND_CEASE = 3; 1154 1155 1159 protected static final int ONE_SECOND = 1000; 1160 1161 1165 protected static final int ONE_MINUTE = 60*ONE_SECOND; 1166 1167 1171 protected static final int ONE_HOUR = 60*ONE_MINUTE; 1172 1173 1179 protected static final long ONE_DAY = 24*ONE_HOUR; 1180 1181 1187 protected static final long ONE_WEEK = 7*ONE_DAY; 1188 1189 1194 protected static final int JAN_1_1_JULIAN_DAY = 1721426; 1195 1196 1201 protected static final int EPOCH_JULIAN_DAY = 2440588; 1202 1203 1209 protected static final int MIN_JULIAN = -0x7F000000; 1210 1211 1216 protected static final long MIN_MILLIS = -184303902528000000L; 1217 1218 1221 1226 protected static final Date MIN_DATE = new Date (MIN_MILLIS); 1227 1228 1234 protected static final int MAX_JULIAN = +0x7F000000; 1235 1236 1241 protected static final long MAX_MILLIS = (MAX_JULIAN - EPOCH_JULIAN_DAY) * ONE_DAY; 1242 1243 1248 protected static final Date MAX_DATE = new Date (MAX_MILLIS); 1249 1250 1261 1267 private transient int fields[]; 1268 1269 1274 private transient int stamp[]; 1275 1276 1282 private long time; 1283 1284 1290 private transient boolean isTimeSet; 1291 1292 1299 private transient boolean areFieldsSet; 1300 1301 1309 private transient boolean areAllFieldsSet; 1310 1311 1317 private transient boolean areFieldsVirtuallySet; 1318 1319 1325 private boolean lenient = true; 1326 1327 1332 private TimeZone zone; 1333 1334 1339 private int firstDayOfWeek; 1340 1341 1346 private int minimalDaysInFirstWeek; 1347 1348 1354 private int weekendOnset; 1355 1356 1363 private int weekendOnsetMillis; 1364 1365 1372 private int weekendCease; 1373 1374 1381 private int weekendCeaseMillis; 1382 1383 1387 private static Hashtable cachedLocaleData = new Hashtable (3); 1388 1389 1396 protected static final int UNSET = 0; 1397 1398 1405 protected static final int INTERNALLY_SET = 1; 1406 1407 1415 protected static final int MINIMUM_USER_STAMP = 2; 1416 1417 1424 private transient int nextStamp = MINIMUM_USER_STAMP; 1425 1426 1436 1456 1458 1461 private static final long serialVersionUID = 6222646104888790989L; 1464 1465 1471 private transient int internalSetMask; 1472 1473 1477 private transient int gregorianYear; 1478 1479 1483 private transient int gregorianMonth; 1484 1485 1489 private transient int gregorianDayOfYear; 1490 1491 1495 private transient int gregorianDayOfMonth; 1496 1497 1503 protected Calendar() 1504 { 1505 this(TimeZone.getDefault(), ULocale.getDefault()); 1506 } 1507 1508 1514 protected Calendar(TimeZone zone, Locale aLocale) 1515 { 1516 this(zone, ULocale.forLocale(aLocale)); 1517 } 1518 1519 1526 protected Calendar(TimeZone zone, ULocale locale) 1527 { 1528 this.zone = zone; 1529 setWeekData(locale); 1530 initInternal(); 1531 } 1532 1533 private void initInternal() 1534 { 1535 fields = handleCreateFields(); 1538 if (fields == null || fields.length < BASE_FIELD_COUNT || 1541 fields.length > MAX_FIELD_COUNT) { 1542 throw new IllegalStateException ("Invalid fields[]"); 1543 } 1544 stamp = new int[fields.length]; 1546 int mask = (1 << ERA) | 1547 (1 << YEAR) | 1548 (1 << MONTH) | 1549 (1 << DAY_OF_MONTH) | 1550 (1 << DAY_OF_YEAR) | 1551 (1 << EXTENDED_YEAR); 1552 for (int i=BASE_FIELD_COUNT; i<fields.length; ++i) { 1553 mask |= (1 << i); 1554 } 1555 internalSetMask = mask; 1556 } 1557 1558 1563 public static synchronized Calendar getInstance() 1564 { 1565 return getInstanceInternal(null, null); 1566 } 1567 1568 1574 public static synchronized Calendar getInstance(TimeZone zone) 1575 { 1576 return getInstanceInternal(zone, null); 1577 } 1578 1579 1585 public static synchronized Calendar getInstance(Locale aLocale) 1586 { 1587 return getInstanceInternal(null, ULocale.forLocale(aLocale)); 1588 } 1589 1590 1597 public static synchronized Calendar getInstance(ULocale locale) 1598 { 1599 return getInstanceInternal(null, locale); 1600 } 1601 1602 1609 public static synchronized Calendar getInstance(TimeZone zone, 1610 Locale aLocale) { 1611 return getInstanceInternal(zone, ULocale.forLocale(aLocale)); 1612 } 1613 1614 1622 public static synchronized Calendar getInstance(TimeZone zone, 1623 ULocale locale) { 1624 return getInstanceInternal(zone, locale); 1625 } 1626 1627 1631 private static Calendar getInstanceInternal(TimeZone tz, ULocale locale) { 1632 if (locale == null) { 1633 locale = ULocale.getDefault(); 1634 } 1635 if (tz == null) { 1636 tz = TimeZone.getDefault(); 1637 } 1638 Calendar cal = getShim().createInstance(locale); 1639 cal.setTimeZone(tz); 1640 cal.setTimeInMillis(System.currentTimeMillis()); 1641 return cal; 1642 } 1643 1644 private static final int BUDDHIST = 0; 1645 private static final int CHINESE = 1; 1646 private static final int COPTIC = 2; 1647 private static final int ETHIOPIC = 3; 1648 private static final int GREGORIAN = 4; 1649 private static final int HEBREW = 5; 1650 private static final int ISLAMIC = 6; 1651 private static final int ISLAMIC_CIVIL = 7; 1652 private static final int JAPANESE = 8; 1653 1654 private static final String [] calTypes = { 1655 "buddhist", "chinese", "coptic", "ethiopic", "gregorian", "hebrew", 1656 "islamic", "islamic-civil", "japanese", 1657 }; 1658 1659 private static int getCalendarType(ULocale l) { 1660 String s = l.getKeywordValue("calendar"); 1661 if (s == null) { 1662 l = ICUResourceBundle.getFunctionalEquivalent( 1663 ICUResourceBundle.ICU_BASE_NAME, "calendar", "calendar", l, null); 1664 s = l.getKeywordValue("calendar"); 1665 } 1666 return getCalendarType(s); 1667 } 1668 1669 private static int getCalendarType(String s) { 1670 if (s != null) { 1671 s = s.toLowerCase(); 1672 for (int i = 0; i < calTypes.length; ++i) { 1673 if (s.equals(calTypes[i])) { 1674 return i; 1675 } 1676 } 1677 } 1678 return GREGORIAN; 1679 } 1680 1681 1686 public static Locale [] getAvailableLocales() 1687 { 1688 if (shim == null) { 1689 return ICUResourceBundle.getAvailableLocales(ICUResourceBundle.ICU_BASE_NAME); 1690 } 1691 return getShim().getAvailableLocales(); 1692 } 1693 1694 1700 public static ULocale[] getAvailableULocales() 1701 { 1702 if (shim == null) { 1703 return ICUResourceBundle.getAvailableULocales(ICUResourceBundle.ICU_BASE_NAME); 1704 } 1705 return getShim().getAvailableULocales(); 1706 } 1707 1708 1716 static abstract class CalendarFactory { 1717 public boolean visible() { 1718 return true; 1719 } 1720 1721 public abstract Set getSupportedLocaleNames(); 1722 1723 public Calendar createCalendar(ULocale loc) { 1724 return null; 1725 } 1726 1727 protected CalendarFactory() { 1728 } 1729 } 1730 1731 static abstract class CalendarShim { 1733 abstract Locale [] getAvailableLocales(); 1734 abstract ULocale[] getAvailableULocales(); 1735 abstract Object registerFactory(CalendarFactory factory); 1736 abstract boolean unregister(Object k); 1737 abstract Calendar createInstance(ULocale l); 1738 } 1739 1740 private static CalendarShim shim; 1741 private static CalendarShim getShim() { 1742 if (shim == null) { 1743 try { 1744 Class cls = Class.forName("com.ibm.icu.util.CalendarServiceShim"); 1745 shim = (CalendarShim)cls.newInstance(); 1746 } 1747 catch (MissingResourceException e) { 1748 throw e; 1749 } 1750 catch (Exception e) { 1751 throw new RuntimeException (e.getMessage()); 1752 } 1753 } 1754 return shim; 1755 } 1756 1757 static Calendar createInstance(ULocale locale) { 1758 int calType = getCalendarType(locale); 1759 TimeZone zone = TimeZone.getDefault(); 1760 1761 switch (calType) { 1762 case BUDDHIST: 1763 return new BuddhistCalendar(zone, locale); 1764 case CHINESE: 1765 return new ChineseCalendar(zone, locale); 1766 case COPTIC: 1767 return new CopticCalendar(zone, locale); 1768 case ETHIOPIC: 1769 return new EthiopicCalendar(zone, locale); 1770 case GREGORIAN: 1771 return new GregorianCalendar(zone, locale); 1772 case HEBREW: 1773 return new HebrewCalendar(zone, locale); 1774 case ISLAMIC: 1775 case ISLAMIC_CIVIL: { 1776 IslamicCalendar result = new IslamicCalendar(zone, locale); 1777 result.setCivil(calType == ISLAMIC_CIVIL); 1778 return result; 1779 } 1780 case JAPANESE: 1781 return new JapaneseCalendar(zone, locale); 1782 default: 1783 throw new IllegalStateException (); 1784 } 1785 } 1786 1787 1794 static Object registerFactory(CalendarFactory factory) { 1795 if (factory == null) { 1796 throw new IllegalArgumentException ("factory must not be null"); 1797 } 1798 return getShim().registerFactory(factory); 1799 } 1800 1801 1806 static boolean unregister(Object registryKey) { 1807 if (registryKey == null) { 1808 throw new IllegalArgumentException ("registryKey must not be null"); 1809 } 1810 1811 if (shim == null) { 1812 return false; 1813 } 1814 1815 return shim.unregister(registryKey); 1816 } 1817 1818 1821 1826 public final Date getTime() { 1827 return new Date ( getTimeInMillis() ); 1828 } 1829 1830 1839 public final void setTime(Date date) { 1840 setTimeInMillis( date.getTime() ); 1841 } 1842 1843 1848 public long getTimeInMillis() { 1849 if (!isTimeSet) updateTime(); 1850 return time; 1851 } 1852 1853 1858 public void setTimeInMillis( long millis ) { 1859 if (millis > MAX_MILLIS) { 1860 millis = MAX_MILLIS; 1861 } else if (millis < MIN_MILLIS) { 1862 millis = MIN_MILLIS; 1863 } 1864 time = millis; 1865 areFieldsSet = areAllFieldsSet = false; 1866 isTimeSet = areFieldsVirtuallySet = true; 1867 } 1868 1869 1875 public final int get(int field) 1876 { 1877 complete(); 1878 return fields[field]; 1879 } 1880 1881 1888 protected final int internalGet(int field) 1889 { 1890 return fields[field]; 1891 } 1892 1893 1903 protected final int internalGet(int field, int defaultValue) { 1904 return (stamp[field] > UNSET) ? fields[field] : defaultValue; 1905 } 1906 1907 1913 public final void set(int field, int value) 1914 { 1915 if (areFieldsVirtuallySet) { 1916 computeFields(); 1917 } 1918 fields[field] = value; 1919 stamp[field] = nextStamp++; 1920 isTimeSet = areFieldsSet = areFieldsVirtuallySet = false; 1921 } 1922 1923 1933 public final void set(int year, int month, int date) 1934 { 1935 set(YEAR, year); 1936 set(MONTH, month); 1937 set(DATE, date); 1938 } 1939 1940 1952 public final void set(int year, int month, int date, int hour, int minute) 1953 { 1954 set(YEAR, year); 1955 set(MONTH, month); 1956 set(DATE, date); 1957 set(HOUR_OF_DAY, hour); 1958 set(MINUTE, minute); 1959 } 1960 1961 1974 public final void set(int year, int month, int date, int hour, int minute, 1975 int second) 1976 { 1977 set(YEAR, year); 1978 set(MONTH, month); 1979 set(DATE, date); 1980 set(HOUR_OF_DAY, hour); 1981 set(MINUTE, minute); 1982 set(SECOND, second); 1983 } 1984 1985 1989 public final void clear() 1990 { 1991 for (int i=0; i<fields.length; ++i) { 1992 fields[i] = stamp[i] = 0; } 1994 isTimeSet = areFieldsSet = areAllFieldsSet = areFieldsVirtuallySet = false; 1995 } 1996 1997 2002 public final void clear(int field) 2003 { 2004 if (areFieldsVirtuallySet) { 2005 computeFields(); 2006 } 2007 fields[field] = 0; 2008 stamp[field] = UNSET; 2009 isTimeSet = areFieldsSet = areAllFieldsSet = areFieldsVirtuallySet = false; 2010 } 2011 2012 2017 public final boolean isSet(int field) 2018 { 2019 return areFieldsVirtuallySet || (stamp[field] != UNSET); 2020 } 2021 2022 2026 protected void complete() 2027 { 2028 if (!isTimeSet) updateTime(); 2029 if (!areFieldsSet) { 2030 computeFields(); areFieldsSet = true; 2032 areAllFieldsSet = true; 2033 } 2034 } 2035 2036 2046 public boolean equals(Object obj) { 2047 if (this == obj) { 2048 return true; 2049 } 2050 if (this.getClass() != obj.getClass()) { 2051 return false; 2052 } 2053 2054 Calendar that = (Calendar) obj; 2055 2056 return isEquivalentTo(that) && 2057 getTimeInMillis() == that.getTime().getTime(); 2058 } 2059 2060 2070 public boolean isEquivalentTo(Calendar other) { 2071 return this.getClass() == other.getClass() && 2072 isLenient() == other.isLenient() && 2073 getFirstDayOfWeek() == other.getFirstDayOfWeek() && 2074 getMinimalDaysInFirstWeek() == other.getMinimalDaysInFirstWeek() && 2075 getTimeZone().equals(other.getTimeZone()); 2076 } 2077 2078 2083 public int hashCode() { 2084 2089 return (lenient ? 1 : 0) 2090 | (firstDayOfWeek << 1) 2091 | (minimalDaysInFirstWeek << 4) 2092 | (zone.hashCode() << 7); 2093 } 2094 2095 2100 private long compare(Object that) { 2101 long thatMs; 2102 if (that instanceof Calendar) { 2103 thatMs = ((Calendar)that).getTimeInMillis(); 2104 } else if (that instanceof Date ) { 2105 thatMs = ((Date )that).getTime(); 2106 } else { 2107 throw new IllegalArgumentException (that + "is not a Calendar or Date"); 2108 } 2109 return getTimeInMillis() - thatMs; 2110 } 2111 2112 2120 public boolean before(Object when) { 2121 return compare(when) < 0; 2122 } 2123 2124 2132 public boolean after(Object when) { 2133 return compare(when) > 0; 2134 } 2135 2136 2159 public int getActualMaximum(int field) { 2160 int result; 2161 2162 switch (field) { 2163 case DAY_OF_MONTH: 2164 { 2165 Calendar cal = (Calendar) clone(); 2166 cal.prepareGetActual(field, false); 2167 result = handleGetMonthLength(cal.get(EXTENDED_YEAR), cal.get(MONTH)); 2168 } 2169 break; 2170 2171 case DAY_OF_YEAR: 2172 { 2173 Calendar cal = (Calendar) clone(); 2174 cal.prepareGetActual(field, false); 2175 result = handleGetYearLength(cal.get(EXTENDED_YEAR)); 2176 } 2177 break; 2178 2179 case DAY_OF_WEEK: 2180 case AM_PM: 2181 case HOUR: 2182 case HOUR_OF_DAY: 2183 case MINUTE: 2184 case SECOND: 2185 case MILLISECOND: 2186 case ZONE_OFFSET: 2187 case DST_OFFSET: 2188 case DOW_LOCAL: 2189 case JULIAN_DAY: 2190 case MILLISECONDS_IN_DAY: 2191 result = getMaximum(field); 2193 break; 2194 2195 default: 2196 result = getActualHelper(field, getLeastMaximum(field), getMaximum(field)); 2198 break; 2199 } 2200 return result; 2201 } 2202 2203 2225 public int getActualMinimum(int field) { 2226 int result; 2227 2228 switch (field) { 2229 case DAY_OF_WEEK: 2230 case AM_PM: 2231 case HOUR: 2232 case HOUR_OF_DAY: 2233 case MINUTE: 2234 case SECOND: 2235 case MILLISECOND: 2236 case ZONE_OFFSET: 2237 case DST_OFFSET: 2238 case DOW_LOCAL: 2239 case JULIAN_DAY: 2240 case MILLISECONDS_IN_DAY: 2241 result = getMinimum(field); 2243 break; 2244 2245 default: 2246 result = getActualHelper(field, getGreatestMinimum(field), getMinimum(field)); 2248 break; 2249 } 2250 return result; 2251 } 2252 2253 2274 protected void prepareGetActual(int field, boolean isMinimum) { 2275 set(MILLISECONDS_IN_DAY, 0); 2276 2277 switch (field) { 2278 case YEAR: 2279 case YEAR_WOY: 2280 case EXTENDED_YEAR: 2281 set(DAY_OF_YEAR, getGreatestMinimum(DAY_OF_YEAR)); 2282 break; 2283 2284 case MONTH: 2285 set(DAY_OF_MONTH, getGreatestMinimum(DAY_OF_MONTH)); 2286 break; 2287 2288 case DAY_OF_WEEK_IN_MONTH: 2289 set(DAY_OF_MONTH, 1); 2292 set(DAY_OF_WEEK, get(DAY_OF_WEEK)); break; 2294 2295 case WEEK_OF_MONTH: 2296 case WEEK_OF_YEAR: 2297 { 2302 int dow = firstDayOfWeek; 2303 if (isMinimum) { 2304 dow = (dow + 6) % 7; if (dow < SUNDAY) { 2306 dow += 7; 2307 } 2308 } 2309 set(DAY_OF_WEEK, dow); 2310 } 2311 break; 2312 } 2313 2314 set(field, getGreatestMinimum(field)); 2316 } 2317 2318 private int getActualHelper(int field, int startValue, int endValue) { 2319 2320 if (startValue == endValue) { 2321 return startValue; 2323 } 2324 2325 final int delta = (endValue > startValue) ? 1 : -1; 2326 2327 Calendar work = (Calendar) clone(); 2330 work.setLenient(true); 2331 work.prepareGetActual(field, delta < 0); 2332 2333 int result = startValue; 2337 do { 2338 work.set(field, startValue); 2339 if (work.get(field) != startValue) { 2340 break; 2341 } else { 2342 result = startValue; 2343 startValue += delta; 2344 } 2345 } while (result != endValue); 2346 2347 return result; 2348 } 2349 2350 2396 public final void roll(int field, boolean up) 2397 { 2398 roll(field, up ? +1 : -1); 2399 } 2400 2401 2457 public void roll(int field, int amount) { 2458 2459 if (amount == 0) { 2460 return; } 2462 2463 complete(); 2464 2465 switch (field) { 2466 case DAY_OF_MONTH: 2467 case AM_PM: 2468 case MINUTE: 2469 case SECOND: 2470 case MILLISECOND: 2471 case MILLISECONDS_IN_DAY: 2472 case ERA: 2473 { 2477 int min = getActualMinimum(field); 2478 int max = getActualMaximum(field); 2479 int gap = max - min + 1; 2480 2481 int value = internalGet(field) + amount; 2482 value = (value - min) % gap; 2483 if (value < 0) { 2484 value += gap; 2485 } 2486 value += min; 2487 2488 set(field, value); 2489 return; 2490 } 2491 2492 case HOUR: 2493 case HOUR_OF_DAY: 2494 { 2502 long start = getTimeInMillis(); 2504 int oldHour = internalGet(field); 2505 int max = getMaximum(field); 2506 int newHour = (oldHour + amount) % (max + 1); 2507 if (newHour < 0) { 2508 newHour += max + 1; 2509 } 2510 setTimeInMillis(start + ONE_HOUR * (newHour - oldHour)); 2511 return; 2512 } 2513 2514 case MONTH: 2515 { 2520 int max = getActualMaximum(MONTH); 2521 int mon = (internalGet(MONTH) + amount) % (max+1); 2522 2523 if (mon < 0) { 2524 mon += (max + 1); 2525 } 2526 set(MONTH, mon); 2527 2528 pinField(DAY_OF_MONTH); 2532 return; 2533 } 2534 2535 case YEAR: 2536 case YEAR_WOY: 2537 case EXTENDED_YEAR: 2538 set(field, internalGet(field) + amount); 2540 pinField(MONTH); 2541 pinField(DAY_OF_MONTH); 2542 return; 2543 2544 case WEEK_OF_MONTH: 2545 { 2546 2549 2553 2557 2563 2573 int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek(); 2576 if (dow < 0) dow += 7; 2577 2578 int fdm = (dow - internalGet(DAY_OF_MONTH) + 1) % 7; 2581 if (fdm < 0) fdm += 7; 2582 2583 int start; 2588 if ((7 - fdm) < getMinimalDaysInFirstWeek()) 2589 start = 8 - fdm; else 2591 start = 1 - fdm; 2593 int monthLen = getActualMaximum(DAY_OF_MONTH); 2596 int ldm = (monthLen - internalGet(DAY_OF_MONTH) + dow) % 7; 2597 2599 int limit = monthLen + 7 - ldm; 2604 2605 int gap = limit - start; 2607 int day_of_month = (internalGet(DAY_OF_MONTH) + amount*7 - 2608 start) % gap; 2609 if (day_of_month < 0) day_of_month += gap; 2610 day_of_month += start; 2611 2612 if (day_of_month < 1) day_of_month = 1; 2614 if (day_of_month > monthLen) day_of_month = monthLen; 2615 2616 set(DAY_OF_MONTH, day_of_month); 2623 return; 2624 } 2625 case WEEK_OF_YEAR: 2626 { 2627 2631 int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek(); 2634 if (dow < 0) dow += 7; 2635 2636 int fdy = (dow - internalGet(DAY_OF_YEAR) + 1) % 7; 2639 if (fdy < 0) fdy += 7; 2640 2641 int start; 2646 if ((7 - fdy) < getMinimalDaysInFirstWeek()) 2647 start = 8 - fdy; else 2649 start = 1 - fdy; 2651 int yearLen = getActualMaximum(DAY_OF_YEAR); 2654 int ldy = (yearLen - internalGet(DAY_OF_YEAR) + dow) % 7; 2655 2657 int limit = yearLen + 7 - ldy; 2662 2663 int gap = limit - start; 2665 int day_of_year = (internalGet(DAY_OF_YEAR) + amount*7 - 2666 start) % gap; 2667 if (day_of_year < 0) day_of_year += gap; 2668 day_of_year += start; 2669 2670 if (day_of_year < 1) day_of_year = 1; 2672 if (day_of_year > yearLen) day_of_year = yearLen; 2673 2674 set(DAY_OF_YEAR, day_of_year); 2679 clear(MONTH); 2680 return; 2681 } 2682 case DAY_OF_YEAR: 2683 { 2684 long delta = amount * ONE_DAY; long min2 = time - (internalGet(DAY_OF_YEAR) - 1) * ONE_DAY; 2688 int yearLength = getActualMaximum(DAY_OF_YEAR); 2689 time = (time + delta - min2) % (yearLength*ONE_DAY); 2690 if (time < 0) time += yearLength*ONE_DAY; 2691 setTimeInMillis(time + min2); 2692 return; 2693 } 2694 case DAY_OF_WEEK: 2695 case DOW_LOCAL: 2696 { 2697 long delta = amount * ONE_DAY; int leadDays = internalGet(field); 2704 leadDays -= (field == DAY_OF_WEEK) ? getFirstDayOfWeek() : 1; 2705 if (leadDays < 0) leadDays += 7; 2706 long min2 = time - leadDays * ONE_DAY; 2707 time = (time + delta - min2) % ONE_WEEK; 2708 if (time < 0) time += ONE_WEEK; 2709 setTimeInMillis(time + min2); 2710 return; 2711 } 2712 case DAY_OF_WEEK_IN_MONTH: 2713 { 2714 long delta = amount * ONE_WEEK; int preWeeks = (internalGet(DAY_OF_MONTH) - 1) / 7; 2721 int postWeeks = (getActualMaximum(DAY_OF_MONTH) - 2724 internalGet(DAY_OF_MONTH)) / 7; 2725 long min2 = time - preWeeks * ONE_WEEK; 2727 long gap2 = ONE_WEEK * (preWeeks + postWeeks + 1); time = (time + delta - min2) % gap2; 2730 if (time < 0) time += gap2; 2731 setTimeInMillis(time + min2); 2732 return; 2733 } 2734 case JULIAN_DAY: 2735 set(field, internalGet(field) + amount); 2736 return; 2737 default: 2738 throw new IllegalArgumentException ("Calendar.roll(" + fieldName(field) + 2740 ") not supported"); 2741 } 2742 } 2743 2744 2793 public void add(int field, int amount) { 2794 2795 if (amount == 0) { 2796 return; } 2798 2799 2807 2815 2819 long delta = amount; boolean keepHourInvariant = true; 2821 2822 switch (field) { 2823 case ERA: 2824 set(field, get(field) + amount); 2825 pinField(ERA); 2826 return; 2827 2828 case YEAR: 2829 case EXTENDED_YEAR: 2830 case YEAR_WOY: 2831 case MONTH: 2832 set(field, get(field) + amount); 2833 pinField(DAY_OF_MONTH); 2834 return; 2835 2836 case WEEK_OF_YEAR: 2837 case WEEK_OF_MONTH: 2838 case DAY_OF_WEEK_IN_MONTH: 2839 delta *= ONE_WEEK; 2840 break; 2841 2842 case AM_PM: 2843 delta *= 12 * ONE_HOUR; 2844 break; 2845 2846 case DAY_OF_MONTH: 2847 case DAY_OF_YEAR: 2848 case DAY_OF_WEEK: 2849 case DOW_LOCAL: 2850 case JULIAN_DAY: 2851 delta *= ONE_DAY; 2852 break; 2853 2854 case HOUR_OF_DAY: 2855 case HOUR: 2856 delta *= ONE_HOUR; 2857 keepHourInvariant = false; 2858 break; 2859 2860 case MINUTE: 2861 delta *= ONE_MINUTE; 2862 keepHourInvariant = false; 2863 break; 2864 2865 case SECOND: 2866 delta *= ONE_SECOND; 2867 keepHourInvariant = false; 2868 break; 2869 2870 case MILLISECOND: 2871 case MILLISECONDS_IN_DAY: 2872 keepHourInvariant = false; 2873 break; 2874 2875 default: 2876 throw new IllegalArgumentException ("Calendar.add(" + fieldName(field) + 2877 ") not supported"); 2878 } 2879 2880 int dst = 0; 2885 int hour = 0; 2886 if (keepHourInvariant) { 2887 dst = get(DST_OFFSET); 2888 hour = internalGet(HOUR_OF_DAY); 2889 } 2890 2891 setTimeInMillis(getTimeInMillis() + delta); 2892 2893 if (keepHourInvariant) { 2894 dst -= get(DST_OFFSET); 2895 if (dst != 0) { 2896 long t = time; 2904 setTimeInMillis(time + dst); 2905 if (get(HOUR_OF_DAY) != hour) { 2906 setTimeInMillis(t); 2907 } 2908 } 2909 } 2910 } 2911 2912 2916 public String getDisplayName(Locale loc) { 2917 return this.getClass().getName(); 2918 } 2919 2920 2925 public String getDisplayName(ULocale loc) { 2926 return this.getClass().getName(); 2927 } 2928 2929 2949 public int compareTo(Calendar that) { 2950 long v = getTimeInMillis() - that.getTimeInMillis(); 2951 return v < 0 ? -1 : (v > 0 ? 1 : 0); 2952 } 2953 2954 2960 public int compareTo(Object that) { 2961 return compareTo((Calendar)that); 2962 } 2963 2964 2968 2975 public DateFormat getDateTimeFormat(int dateStyle, int timeStyle, Locale loc) { 2976 return formatHelper(this, ULocale.forLocale(loc), dateStyle, timeStyle); 2977 } 2978 2979 2987 public DateFormat getDateTimeFormat(int dateStyle, int timeStyle, ULocale loc) { 2988 return formatHelper(this, loc, dateStyle, timeStyle); 2989 } 2990 2991 3002 protected DateFormat handleGetDateFormat(String pattern, Locale locale) { 3003 return handleGetDateFormat(pattern, ULocale.forLocale(locale)); 3004 } 3005 3006 3018 protected DateFormat handleGetDateFormat(String pattern, ULocale locale) { 3019 FormatConfiguration fmtConfig = new FormatConfiguration(); 3020 fmtConfig.pattern = pattern; 3021 fmtConfig.formatData = new DateFormatSymbols(this, locale); 3022 fmtConfig.loc = locale; 3023 fmtConfig.cal = this; 3024 3025 return SimpleDateFormat.getInstance(fmtConfig); 3026 } 3027 3028 private static final ICUCache PATTERN_CACHE = new SimpleCache(); 3030 private static final String [] DEFAULT_PATTERNS = { 3032 "HH:mm:ss z", 3033 "HH:mm:ss z", 3034 "HH:mm:ss", 3035 "HH:mm", 3036 "EEEE, yyyy MMMM dd", 3037 "yyyy MMMM d", 3038 "yyyy MMM d", 3039 "yy/MM/dd", 3040 "{1} {0}" 3041 }; 3042 3043 static private DateFormat formatHelper(Calendar cal, ULocale loc, int dateStyle, int timeStyle) { 3044 String key = loc.toString() + cal.getType(); 3046 String [] patterns = (String [])PATTERN_CACHE.get(key); 3047 if (patterns == null) { 3048 try { 3050 CalendarData calData = new CalendarData(loc, cal.getType()); 3051 patterns = calData.get("DateTimePatterns").getStringArray(); 3052 } catch (MissingResourceException e) { 3053 patterns = DEFAULT_PATTERNS; 3054 } 3055 PATTERN_CACHE.put(key, patterns); 3056 } 3057 String pattern = null; 3059 if ((timeStyle >= 0) && (dateStyle >= 0)) { 3060 pattern = MessageFormat.format(patterns[8], 3061 new Object [] {patterns[timeStyle], patterns[dateStyle + 4]}); 3062 } else if (timeStyle >= 0) { 3063 pattern = patterns[timeStyle]; 3064 } else if (dateStyle >= 0) { 3065 pattern = patterns[dateStyle + 4]; 3066 } else { 3067 throw new IllegalArgumentException ("No date or time style specified"); 3068 } 3069 DateFormat result = cal.handleGetDateFormat(pattern, loc); 3070 result.setCalendar(cal); 3071 return result; 3072 } 3073 3074 3082 public static class FormatConfiguration { 3083 private String pattern; 3084 private DateFormatSymbols formatData; 3085 private Calendar cal; 3086 private ULocale loc; 3087 3088 private FormatConfiguration() { 3090 } 3091 3092 public String getPatternString() { 3093 return pattern; 3094 } 3095 3096 public Calendar getCalendar() { 3097 return cal; 3098 } 3099 3100 public ULocale getLocale() { 3101 return loc; 3102 } 3103 3104 public DateFormatSymbols getDateFormatSymbols() { 3105 return formatData; 3106 } 3107 } 3108 3109 3114 3141 protected void pinField(int field) { 3142 int max = getActualMaximum(field); 3143 int min = getActualMinimum(field); 3144 3145 if (fields[field] > max) { 3146 set(field, max); 3147 } else if (fields[field] < min) { 3148 set(field, min); 3149 } 3150 } 3151 3152 3194 protected int weekNumber(int desiredDay, int dayOfPeriod, int dayOfWeek) 3195 { 3196 int periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7; 3200 if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7; 3201 3202 int weekNo = (desiredDay + periodStartDayOfWeek - 1)/7; 3206 3207 if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo; 3211 3212 return weekNo; 3213 } 3214 3215 3245 protected final int weekNumber(int dayOfPeriod, int dayOfWeek) 3246 { 3247 return weekNumber(dayOfPeriod, dayOfPeriod, dayOfWeek); 3248 } 3249 3250 3254 3307 public int fieldDifference(Date when, int field) { 3308 int min = 0; 3309 long startMs = getTimeInMillis(); 3310 long targetMs = when.getTime(); 3311 if (startMs < targetMs) { 3318 int max = 1; 3319 for (;;) { 3321 setTimeInMillis(startMs); 3322 add(field, max); 3323 long ms = getTimeInMillis(); 3324 if (ms == targetMs) { 3325 return max; 3326 } else if (ms > targetMs) { 3327 break; 3328 } else { 3329 max <<= 1; 3330 if (max < 0) { 3331 throw new RuntimeException (); 3333 } 3334 } 3335 } 3336 while ((max - min) > 1) { 3338 int t = (min + max) / 2; 3339 setTimeInMillis(startMs); 3340 add(field, t); 3341 long ms = getTimeInMillis(); 3342 if (ms == targetMs) { 3343 return t; 3344 } else if (ms > targetMs) { 3345 max = t; 3346 } else { 3347 min = t; 3348 } 3349 } 3350 } else if (startMs > targetMs) { 3351 if (false) { 3352 setTimeInMillis(targetMs); 3356 min = -fieldDifference(new Date (startMs), field); 3357 } 3358 int max = -1; 3359 for (;;) { 3361 setTimeInMillis(startMs); 3362 add(field, max); 3363 long ms = getTimeInMillis(); 3364 if (ms == targetMs) { 3365 return max; 3366 } else if (ms < targetMs) { 3367 break; 3368 } else { 3369 max <<= 1; 3370 if (max == 0) { 3371 throw new RuntimeException (); 3373 } 3374 } 3375 } 3376 while ((min - max) > 1) { 3378 int t = (min + max) / 2; 3379 setTimeInMillis(startMs); 3380 add(field, t); 3381 long ms = getTimeInMillis(); 3382 if (ms == targetMs) { 3383 return t; 3384 } else if (ms < targetMs) { 3385 max = t; 3386 } else { 3387 min = t; 3388 } 3389 } 3390 } 3391 setTimeInMillis(startMs); 3393 add(field, min); 3394 return min; 3395 } 3396 3397 3402 public void setTimeZone(TimeZone value) 3403 { 3404 zone = value; 3405 3414 areFieldsSet = false; 3415 } 3416 3417 3422 public TimeZone getTimeZone() 3423 { 3424 return zone; 3425 } 3426 3427 3437 public void setLenient(boolean lenient) 3438 { 3439 this.lenient = lenient; 3440 } 3441 3442 3446 public boolean isLenient() 3447 { 3448 return lenient; 3449 } 3450 3451 3457 public void setFirstDayOfWeek(int value) 3458 { 3459 if (firstDayOfWeek != value) { 3460 if (value < SUNDAY || value > SATURDAY) { 3461 throw new IllegalArgumentException ("Invalid day of week"); 3462 } 3463 firstDayOfWeek = value; 3464 areFieldsSet = false; 3465 } 3466 } 3467 3468 3474 public int getFirstDayOfWeek() 3475 { 3476 return firstDayOfWeek; 3477 } 3478 3479 3488 public void setMinimalDaysInFirstWeek(int value) 3489 { 3490 if (value < 1) { 3494 value = 1; 3495 } else if (value > 7) { 3496 value = 7; 3497 } 3498 if (minimalDaysInFirstWeek != value) { 3499 minimalDaysInFirstWeek = value; 3500 areFieldsSet = false; 3501 } 3502 } 3503 3504 3513 public int getMinimalDaysInFirstWeek() 3514 { 3515 return minimalDaysInFirstWeek; 3516 } 3517 3518 private static final int LIMITS[][] = { 3519 {}, {}, {}, {}, {}, {}, {}, { 1, 1, 7, 7 }, {}, { 0, 0, 1, 1 }, { 0, 0, 11, 11 }, { 0, 0, 23, 23 }, { 0, 0, 59, 59 }, { 0, 0, 59, 59 }, { 0, 0, 999, 999 }, {-12*ONE_HOUR, -12*ONE_HOUR, 12*ONE_HOUR, 12*ONE_HOUR }, { 0, 0, 1*ONE_HOUR, 1*ONE_HOUR }, {}, { 1, 1, 7, 7 }, {}, { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, { 0, 0, 24*ONE_HOUR-1, 24*ONE_HOUR-1 }, }; 3543 3544 3565 abstract protected int handleGetLimit(int field, int limitType); 3566 3567 3577 protected int getLimit(int field, int limitType) { 3578 switch (field) { 3579 case DAY_OF_WEEK: 3580 case AM_PM: 3581 case HOUR: 3582 case HOUR_OF_DAY: 3583 case MINUTE: 3584 case SECOND: 3585 case MILLISECOND: 3586 case ZONE_OFFSET: 3587 case DST_OFFSET: 3588 case DOW_LOCAL: 3589 case JULIAN_DAY: 3590 case MILLISECONDS_IN_DAY: 3591 return LIMITS[field][limitType]; 3592 } 3593 return handleGetLimit(field, limitType); 3594 } 3595 3596 3603 protected static final int MINIMUM = 0; 3604 3605 3612 protected static final int GREATEST_MINIMUM = 1; 3613 3614 3621 protected static final int LEAST_MAXIMUM = 2; 3622 3623 3630 protected static final int MAXIMUM = 3; 3631 3632 3639 public final int getMinimum(int field) { 3640 return getLimit(field, MINIMUM); 3641 } 3642 3643 3650 public final int getMaximum(int field) { 3651 return getLimit(field, MAXIMUM); 3652 } 3653 3654 3661 public final int getGreatestMinimum(int field) { 3662 return getLimit(field, GREATEST_MINIMUM); 3663 } 3664 3665 3672 public final int getLeastMaximum(int field) { 3673 return getLimit(field, LEAST_MAXIMUM); 3674 } 3675 3676 3681 3706 public int getDayOfWeekType(int dayOfWeek) { 3707 if (dayOfWeek < SUNDAY || dayOfWeek > SATURDAY) { 3708 throw new IllegalArgumentException ("Invalid day of week"); 3709 } 3710 if (weekendOnset < weekendCease) { 3711 if (dayOfWeek < weekendOnset || dayOfWeek > weekendCease) { 3712 return WEEKDAY; 3713 } 3714 } else { 3715 if (dayOfWeek > weekendCease && dayOfWeek < weekendOnset) { 3716 return WEEKDAY; 3717 } 3718 } 3719 if (dayOfWeek == weekendOnset) { 3720 return (weekendOnsetMillis == 0) ? WEEKEND : WEEKEND_ONSET; 3721 } 3722 if (dayOfWeek == weekendCease) { 3723 return (weekendCeaseMillis == 0) ? WEEKDAY : WEEKEND_CEASE; 3724 } 3725 return WEEKEND; 3726 } 3727 3728 3746 public int getWeekendTransition(int dayOfWeek) { 3747 if (dayOfWeek == weekendOnset) { 3748 return weekendOnsetMillis; 3749 } else if (dayOfWeek == weekendCease) { 3750 return weekendCeaseMillis; 3751 } 3752 throw new IllegalArgumentException ("Not weekend transition day"); 3753 } 3754 3755 3768 public boolean isWeekend(Date date) { 3769 setTime(date); 3770 return isWeekend(); 3771 } 3772 3773 3783 public boolean isWeekend() { 3784 int dow = get(DAY_OF_WEEK); 3785 int dowt = getDayOfWeekType(dow); 3786 switch (dowt) { 3787 case WEEKDAY: 3788 return false; 3789 case WEEKEND: 3790 return true; 3791 default: int millisInDay = internalGet(MILLISECOND) + 1000 * (internalGet(SECOND) + 3798 60 * (internalGet(MINUTE) + 60 * internalGet(HOUR_OF_DAY))); 3799 int transition = getWeekendTransition(dow); 3800 return (dowt == WEEKEND_ONSET) 3801 ? (millisInDay >= transition) 3802 : (millisInDay < transition); 3803 } 3804 } 3806 3807 3811 3815 public Object clone() 3816 { 3817 try { 3818 Calendar other = (Calendar) super.clone(); 3819 3820 other.fields = new int[fields.length]; 3821 other.stamp = new int[fields.length]; 3822 System.arraycopy(this.fields, 0, other.fields, 0, fields.length); 3823 System.arraycopy(this.stamp, 0, other.stamp, 0, fields.length); 3824 3825 other.zone = (TimeZone) zone.clone(); 3826 return other; 3827 } 3828 catch (CloneNotSupportedException e) { 3829 throw new IllegalStateException (); 3831 } 3832 } 3833 3834 3843 public String toString() { 3844 StringBuffer buffer = new StringBuffer (); 3845 buffer.append(getClass().getName()); 3846 buffer.append("[time="); 3847 buffer.append(isTimeSet ? String.valueOf(time) : "?"); 3848 buffer.append(",areFieldsSet="); 3849 buffer.append(areFieldsSet); 3850 buffer.append(",areAllFieldsSet="); 3851 buffer.append(areAllFieldsSet); 3852 buffer.append(",lenient="); 3853 buffer.append(lenient); 3854 buffer.append(",zone="); 3855 buffer.append(zone); 3856 buffer.append(",firstDayOfWeek="); 3857 buffer.append(firstDayOfWeek); 3858 buffer.append(",minimalDaysInFirstWeek="); 3859 buffer.append(minimalDaysInFirstWeek); 3860 for (int i=0; i<fields.length; ++i) { 3861 buffer.append(',').append(FIELD_NAME[i]).append('='); 3862 buffer.append(isSet(i) ? String.valueOf(fields[i]) : "?"); 3863 } 3864 buffer.append(']'); 3865 return buffer.toString(); 3866 } 3867 3868 3870 3873 private static class WeekData { 3874 public int firstDayOfWeek; 3875 public int minimalDaysInFirstWeek; 3876 public int weekendOnset; 3877 public int weekendOnsetMillis; 3878 public int weekendCease; 3879 public int weekendCeaseMillis; 3880 public ULocale actualLocale; 3881 public WeekData(int fdow, int mdifw, 3882 int weekendOnset, int weekendOnsetMillis, 3883 int weekendCease, int weekendCeaseMillis, 3884 ULocale actualLoc) { 3885 this.firstDayOfWeek = fdow; 3886 this.minimalDaysInFirstWeek = mdifw; 3887 this.actualLocale = actualLoc; 3888 this.weekendOnset = weekendOnset; 3889 this.weekendOnsetMillis = weekendOnsetMillis; 3890 this.weekendCease = weekendCease; 3891 this.weekendCeaseMillis = weekendCeaseMillis; 3892 } 3893 } 3894 3895 3900 private void setWeekData(ULocale locale) 3901 { 3902 3903 WeekData data = (WeekData) cachedLocaleData.get(locale); 3904 3905 if (data == null) { 3906 3907 CalendarData calData = new CalendarData(locale, getType()); 3908 int[] dateTimeElements = calData.get("DateTimeElements").getIntVector(); 3909 int[] weekend = calData.get("weekend").getIntVector(); 3910 data = new WeekData(dateTimeElements[0],dateTimeElements[1], 3911 weekend[0], 3912 weekend[1], 3913 weekend[2], 3914 weekend[3], 3915 calData.getULocale()); 3916 3917 cachedLocaleData.put(locale, data); 3918 } 3919 setFirstDayOfWeek(data.firstDayOfWeek); 3920 setMinimalDaysInFirstWeek(data.minimalDaysInFirstWeek); 3921 weekendOnset = data.weekendOnset; 3922 weekendOnsetMillis = data.weekendOnsetMillis; 3923 weekendCease = data.weekendCease; 3924 weekendCeaseMillis = data.weekendCeaseMillis; 3925 3926 ULocale uloc = data.actualLocale; 3928 setLocale(uloc, uloc); 3929 } 3930 3931 3936 private void updateTime() { 3937 computeTime(); 3938 if (isLenient() || !areAllFieldsSet) areFieldsSet = false; 3942 isTimeSet = true; 3943 areFieldsVirtuallySet = false; 3944 } 3945 3946 3959 private void writeObject(ObjectOutputStream stream) 3960 throws IOException 3961 { 3962 if (!isTimeSet) { 3965 try { 3966 updateTime(); 3967 } 3968 catch (IllegalArgumentException e) {} 3969 } 3970 3971 stream.defaultWriteObject(); 3973 } 3974 3975 3978 private void readObject(ObjectInputStream stream) 3979 throws IOException , ClassNotFoundException { 3980 3981 stream.defaultReadObject(); 3982 3983 initInternal(); 3984 3985 isTimeSet = true; 3986 areFieldsSet = areAllFieldsSet = false; 3987 nextStamp = MINIMUM_USER_STAMP; 3988 } 3989 3990 3991 3995 4004 protected void computeFields() { 4005 int offsets[] = new int[2]; 4006 getTimeZone().getOffset(time, false, offsets); 4007 long localMillis = time + offsets[0] + offsets[1]; 4008 4009 int mask = internalSetMask; 4011 for (int i=0; i<fields.length; ++i) { 4012 if ((mask & 1) == 0) { 4013 stamp[i] = INTERNALLY_SET; 4014 } else { 4015 stamp[i] = UNSET; 4016 } 4017 mask >>= 1; 4018 } 4019 4020 4029 long days = floorDivide(localMillis, ONE_DAY); 4030 4031 fields[JULIAN_DAY] = (int) days + EPOCH_JULIAN_DAY; 4032 4033 computeGregorianAndDOWFields(fields[JULIAN_DAY]); 4036 4037 handleComputeFields(fields[JULIAN_DAY]); 4042 4043 computeWeekFields(); 4046 4047 int millisInDay = (int) (localMillis - (days * ONE_DAY)); 4051 fields[MILLISECONDS_IN_DAY] = millisInDay; 4052 fields[MILLISECOND] = millisInDay % 1000; 4053 millisInDay /= 1000; 4054 fields[SECOND] = millisInDay % 60; 4055 millisInDay /= 60; 4056 fields[MINUTE] = millisInDay % 60; 4057 millisInDay /= 60; 4058 fields[HOUR_OF_DAY] = millisInDay; 4059 fields[AM_PM] = millisInDay / 12; fields[HOUR] = millisInDay % 12; 4061 fields[ZONE_OFFSET] = offsets[0]; 4062 fields[DST_OFFSET] = offsets[1]; 4063 } 4064 4065 4071 private final void computeGregorianAndDOWFields(int julianDay) { 4072 computeGregorianFields(julianDay); 4073 4074 int dow = fields[DAY_OF_WEEK] = julianDayToDayOfWeek(julianDay); 4076 4077 int dowLocal = dow - getFirstDayOfWeek() + 1; 4079 if (dowLocal < 1) { 4080 dowLocal += 7; 4081 } 4082 fields[DOW_LOCAL] = dowLocal; 4083 } 4084 4085 4096 protected final void computeGregorianFields(int julianDay) { 4097 int year, month, dayOfMonth, dayOfYear; 4098 4099 long gregorianEpochDay = julianDay - JAN_1_1_JULIAN_DAY; 4101 4102 int[] rem = new int[1]; 4107 int n400 = floorDivide(gregorianEpochDay, 146097, rem); int n100 = floorDivide(rem[0], 36524, rem); int n4 = floorDivide(rem[0], 1461, rem); int n1 = floorDivide(rem[0], 365, rem); 4111 year = 400*n400 + 100*n100 + 4*n4 + n1; 4112 dayOfYear = rem[0]; if (n100 == 4 || n1 == 4) { 4114 dayOfYear = 365; } else { 4116 ++year; 4117 } 4118 4119 boolean isLeap = ((year&0x3) == 0) && (year%100 != 0 || year%400 == 0); 4121 4122 int correction = 0; 4123 int march1 = isLeap ? 60 : 59; if (dayOfYear >= march1) correction = isLeap ? 1 : 2; 4125 month = (12 * (dayOfYear + correction) + 6) / 367; dayOfMonth = dayOfYear - 4127 GREGORIAN_MONTH_COUNT[month][isLeap?3:2] + 1; 4129 gregorianYear = year; 4130 gregorianMonth = month; gregorianDayOfMonth = dayOfMonth; gregorianDayOfYear = dayOfYear + 1; } 4134 4135 4155 private final void computeWeekFields() { 4156 int eyear = fields[EXTENDED_YEAR]; 4157 int year = fields[YEAR]; 4158 int dayOfWeek = fields[DAY_OF_WEEK]; 4159 int dayOfYear = fields[DAY_OF_YEAR]; 4160 4161 int yearOfWeekOfYear = year; 4171 int relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; int relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; int woy = (dayOfYear - 1 + relDowJan1) / 7; if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) { 4175 ++woy; 4176 } 4177 4178 if (woy == 0) { 4181 4186 int prevDoy = dayOfYear + handleGetYearLength(eyear - 1); 4187 woy = weekNumber(prevDoy, dayOfWeek); 4188 yearOfWeekOfYear--; 4189 } else { 4190 int lastDoy = handleGetYearLength(eyear); 4191 if (dayOfYear >= (lastDoy - 5)) { 4198 int lastRelDow = (relDow + lastDoy - dayOfYear) % 7; 4199 if (lastRelDow < 0) { 4200 lastRelDow += 7; 4201 } 4202 if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) && 4203 ((dayOfYear + 7 - relDow) > lastDoy)) { 4204 woy = 1; 4205 yearOfWeekOfYear++; 4206 } 4207 } 4208 } 4209 fields[WEEK_OF_YEAR] = woy; 4210 fields[YEAR_WOY] = yearOfWeekOfYear; 4211 4213 int dayOfMonth = fields[DAY_OF_MONTH]; 4214 fields[WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek); 4215 fields[DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1; 4216 } 4217 4218 4222 4227 protected static final int RESOLVE_REMAP = 32; 4228 4230 static final int[][][] DATE_PRECEDENCE = { 4232 { 4233 { DAY_OF_MONTH }, 4234 { WEEK_OF_YEAR, DAY_OF_WEEK }, 4235 { WEEK_OF_MONTH, DAY_OF_WEEK }, 4236 { DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK }, 4237 { WEEK_OF_YEAR, DOW_LOCAL }, 4238 { WEEK_OF_MONTH, DOW_LOCAL }, 4239 { DAY_OF_WEEK_IN_MONTH, DOW_LOCAL }, 4240 { DAY_OF_YEAR }, 4241 }, 4242 { 4243 { WEEK_OF_YEAR }, 4244 { WEEK_OF_MONTH }, 4245 { DAY_OF_WEEK_IN_MONTH }, 4246 { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK }, 4247 { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DOW_LOCAL }, 4248 }, 4249 }; 4250 4251 static final int[][][] DOW_PRECEDENCE = { 4252 { 4253 { DAY_OF_WEEK }, 4254 { DOW_LOCAL }, 4255 }, 4256 }; 4257 4258 4285 protected int resolveFields(int[][][] precedenceTable) { 4286 int bestField = -1; 4287 for (int g=0; g<precedenceTable.length && bestField < 0; ++g) { 4288 int[][] group = precedenceTable[g]; 4289 int bestStamp = UNSET; 4290 linesInGroup: 4291 for (int l=0; l<group.length; ++l) { 4292 int[] line= group[l]; 4293 int lineStamp = UNSET; 4294 for (int i=(line[0]>=RESOLVE_REMAP)?1:0; i<line.length; ++i) { 4296 int s = stamp[line[i]]; 4297 if (s == UNSET) { 4299 continue linesInGroup; 4300 } else { 4301 lineStamp = Math.max(lineStamp, s); 4302 } 4303 } 4304 if (lineStamp > bestStamp) { 4306 bestStamp = lineStamp; 4307 bestField = line[0]; } 4309 } 4310 } 4311 return (bestField>=RESOLVE_REMAP)?(bestField&(RESOLVE_REMAP-1)):bestField; 4312 } 4313 4314 4318 protected int newestStamp(int first, int last, int bestStampSoFar) { 4319 int bestStamp = bestStampSoFar; 4320 for (int i=first; i<=last; ++i) { 4321 if (stamp[i] > bestStamp) { 4322 bestStamp = stamp[i]; 4323 } 4324 } 4325 return bestStamp; 4326 } 4327 4328 4332 protected final int getStamp(int field) { 4333 return stamp[field]; 4334 } 4335 4336 4341 protected int newerField(int defaultField, int alternateField) { 4342 if (stamp[alternateField] > stamp[defaultField]) { 4343 return alternateField; 4344 } 4345 return defaultField; 4346 } 4347 4348 4356 protected void validateFields() { 4357 for (int field = 0; field < fields.length; field++) { 4358 if (isSet(field)) { 4359 validateField(field); 4360 } 4361 } 4362 } 4363 4364 4372 protected void validateField(int field) { 4373 int y; 4374 switch (field) { 4375 case DAY_OF_MONTH: 4376 y = handleGetExtendedYear(); 4377 validateField(field, 1, handleGetMonthLength(y, internalGet(MONTH))); 4378 break; 4379 case DAY_OF_YEAR: 4380 y = handleGetExtendedYear(); 4381 validateField(field, 1, handleGetYearLength(y)); 4382 break; 4383 case DAY_OF_WEEK_IN_MONTH: 4384 if (internalGet(field) == 0) { 4385 throw new IllegalArgumentException ("DAY_OF_WEEK_IN_MONTH cannot be zero"); 4386 } 4387 validateField(field, getMinimum(field), getMaximum(field)); 4388 break; 4389 default: 4390 validateField(field, getMinimum(field), getMaximum(field)); 4391 break; 4392 } 4393 } 4394 4395 4403 protected final void validateField(int field, int min, int max) { 4404 int value = fields[field]; 4405 if (value < min || value > max) { 4406 throw new IllegalArgumentException (fieldName(field) + 4407 '=' + value + ", valid range=" + 4408 min + ".." + max); 4409 } 4410 } 4411 4412 4417 protected void computeTime() { 4418 if (!isLenient()) { 4419 validateFields(); 4420 } 4421 4422 int julianDay = computeJulianDay(); 4424 4425 long millis = julianDayToMillis(julianDay); 4426 4427 int millisInDay; 4428 4429 if (stamp[MILLISECONDS_IN_DAY] >= MINIMUM_USER_STAMP && 4435 newestStamp(AM_PM, MILLISECOND, UNSET) <= stamp[MILLISECONDS_IN_DAY]) { 4436 millisInDay = internalGet(MILLISECONDS_IN_DAY); 4437 } else { 4438 millisInDay = computeMillisInDay(); 4439 } 4440 4441 if (stamp[ZONE_OFFSET] >= MINIMUM_USER_STAMP || 4455 stamp[DST_OFFSET] >= MINIMUM_USER_STAMP) { 4456 millisInDay -= internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET); 4457 } else { 4458 millisInDay -= computeZoneOffset(millis, millisInDay); 4459 } 4460 4461 time = millis + millisInDay; 4462 } 4463 4464 4471 protected int computeMillisInDay() { 4472 4474 int millisInDay = 0; 4475 4476 int hourOfDayStamp = stamp[HOUR_OF_DAY]; 4480 int hourStamp = Math.max(stamp[HOUR], stamp[AM_PM]); 4481 int bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp; 4482 4483 if (bestStamp != UNSET) { 4485 if (bestStamp == hourOfDayStamp) { 4486 millisInDay += internalGet(HOUR_OF_DAY); 4489 } else { 4490 millisInDay += internalGet(HOUR); 4493 millisInDay += 12 * internalGet(AM_PM); } 4495 } 4496 4497 millisInDay *= 60; 4500 millisInDay += internalGet(MINUTE); millisInDay *= 60; 4502 millisInDay += internalGet(SECOND); millisInDay *= 1000; 4504 millisInDay += internalGet(MILLISECOND); 4506 return millisInDay; 4507 } 4508 4509 4517 protected int computeZoneOffset(long millis, int millisInDay) { 4518 int offsets[] = new int[2]; 4519 zone.getOffset(millis + millisInDay, true, offsets); 4520 return offsets[0] + offsets[1]; 4521 4522 } 4527 4528 4532 protected int computeJulianDay() { 4533 4534 if (stamp[JULIAN_DAY] >= MINIMUM_USER_STAMP) { 4543 int bestStamp = newestStamp(ERA, DAY_OF_WEEK_IN_MONTH, UNSET); 4544 bestStamp = newestStamp(YEAR_WOY, EXTENDED_YEAR, bestStamp); 4545 if (bestStamp <= stamp[JULIAN_DAY]) { 4546 return internalGet(JULIAN_DAY); 4547 } 4548 } 4549 4550 int bestField = resolveFields(getFieldResolutionTable()); 4551 if (bestField < 0) { 4552 bestField = DAY_OF_MONTH; 4553 } 4554 4555 return handleComputeJulianDay(bestField); 4556 } 4557 4558 4566 protected int[][][] getFieldResolutionTable() { 4567 return DATE_PRECEDENCE; 4568 } 4569 4570 4583 abstract protected int handleComputeMonthStart(int eyear, int month, 4584 boolean useMonth); 4585 4586 4594 abstract protected int handleGetExtendedYear(); 4595 4596 4606 protected int handleGetMonthLength(int extendedYear, int month) { 4607 return handleComputeMonthStart(extendedYear, month+1, true) - 4608 handleComputeMonthStart(extendedYear, month, true); 4609 } 4610 4612 4619 protected int handleGetYearLength(int eyear) { 4620 return handleComputeMonthStart(eyear+1, 0, false) - 4621 handleComputeMonthStart(eyear, 0, false); 4622 } 4623 4624 4632 protected int[] handleCreateFields() { 4633 return new int[BASE_FIELD_COUNT]; 4634 } 4635 4636 4646 protected int getDefaultMonthInYear(int extendedYear) { 4647 return Calendar.JANUARY; 4648 } 4649 4650 4661 protected int getDefaultDayInMonth(int extendedYear, int month) { 4662 return 1; 4663 } 4664 4665 4666 4672 protected int handleComputeJulianDay(int bestField) { 4673 4674 boolean useMonth = (bestField == DAY_OF_MONTH || 4675 bestField == WEEK_OF_MONTH || 4676 bestField == DAY_OF_WEEK_IN_MONTH); 4677 4678 int year = handleGetExtendedYear(); 4679 internalSet(EXTENDED_YEAR, year); 4680 4681 int month = useMonth ? internalGet(MONTH, getDefaultMonthInYear(year)) : 0; 4682 4683 int dom = internalGet(DAY_OF_MONTH, getDefaultDayInMonth(year, month)); 4684 4685 int julianDay = handleComputeMonthStart(year, month, useMonth); 4688 4689 if (bestField == DAY_OF_MONTH) { 4690 if(isSet(DAY_OF_MONTH)) { 4691 return julianDay + internalGet(DAY_OF_MONTH, getDefaultDayInMonth(year, month)); 4692 } else { 4693 return julianDay + getDefaultDayInMonth(year, month); 4694 } 4695 } 4696 4697 if (bestField == DAY_OF_YEAR) { 4698 return julianDay + internalGet(DAY_OF_YEAR); 4699 } 4700 4701 int firstDayOfWeek = getFirstDayOfWeek(); 4703 4708 4713 int first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; 4716 if (first < 0) { 4717 first += 7; 4718 } 4719 4720 int dowLocal = 0; 4723 switch (resolveFields(DOW_PRECEDENCE)) { 4724 case DAY_OF_WEEK: 4725 dowLocal = internalGet(DAY_OF_WEEK) - firstDayOfWeek; 4726 break; 4727 case DOW_LOCAL: 4728 dowLocal = internalGet(DOW_LOCAL) - 1; 4729 break; 4730 } 4731 dowLocal = dowLocal % 7; 4732 if (dowLocal < 0) { 4733 dowLocal += 7; 4734 } 4735 4736 int date = 1 - first + dowLocal; 4740 4741 if (bestField == DAY_OF_WEEK_IN_MONTH) { 4742 4743 if (date < 1) { 4745 date += 7; 4746 } 4747 4748 int dim = internalGet(DAY_OF_WEEK_IN_MONTH, 1); 4751 if (dim >= 0) { 4752 date += 7*(dim - 1); 4753 4754 } else { 4755 int m = internalGet(MONTH, JANUARY); 4762 int monthLength = handleGetMonthLength(year, m); 4763 date += ((monthLength - date) / 7 + dim + 1) * 7; 4764 } 4765 } else { 4766 4768 if ((7 - first) < getMinimalDaysInFirstWeek()) { 4770 date += 7; 4771 } 4772 4773 date += 7 * (internalGet(bestField) - 1); 4775 } 4776 4777 return julianDay + date; 4778 } 4779 4780 4792 protected int computeGregorianMonthStart(int year, int month) { 4793 4794 if (month < 0 || month > 11) { 4797 int[] rem = new int[1]; 4798 year += floorDivide(month, 12, rem); 4799 month = rem[0]; 4800 } 4801 4802 boolean isLeap = (year%4 == 0) && ((year%100 != 0) || (year%400 == 0)); 4803 int y = year - 1; 4804 int julianDay = 365*y + floorDivide(y, 4) - floorDivide(y, 100) + 4808 floorDivide(y, 400) + JAN_1_1_JULIAN_DAY - 1; 4809 4810 if (month != 0) { 4813 julianDay += GREGORIAN_MONTH_COUNT[month][isLeap?3:2]; 4814 } 4815 4816 return julianDay; 4817 } 4818 4819 4824 4851 protected void handleComputeFields(int julianDay) { 4852 internalSet(MONTH, getGregorianMonth()); 4853 internalSet(DAY_OF_MONTH, getGregorianDayOfMonth()); 4854 internalSet(DAY_OF_YEAR, getGregorianDayOfYear()); 4855 int eyear = getGregorianYear(); 4856 internalSet(EXTENDED_YEAR, eyear); 4857 int era = GregorianCalendar.AD; 4858 if (eyear < 1) { 4859 era = GregorianCalendar.BC; 4860 eyear = 1 - eyear; 4861 } 4862 internalSet(ERA, era); 4863 internalSet(YEAR, eyear); 4864 } 4865 4867 4872 4878 protected final int getGregorianYear() { 4879 return gregorianYear; 4880 } 4881 4882 4888 protected final int getGregorianMonth() { 4889 return gregorianMonth; 4890 } 4891 4892 4898 protected final int getGregorianDayOfYear() { 4899 return gregorianDayOfYear; 4900 } 4901 4902 4908 protected final int getGregorianDayOfMonth() { 4909 return gregorianDayOfMonth; 4910 } 4911 4912 4918 public final int getFieldCount() { 4919 return fields.length; 4920 } 4921 4922 4932 protected final void internalSet(int field, int value) { 4933 if (((1 << field) & internalSetMask) == 0) { 4934 throw new IllegalStateException ("Subclass cannot set " + 4935 fieldName(field)); 4936 } 4937 fields[field] = value; 4938 stamp[field] = INTERNALLY_SET; 4939 } 4940 4941 private static final int[][] GREGORIAN_MONTH_COUNT = { 4942 { 31, 31, 0, 0 }, { 28, 29, 31, 31 }, { 31, 31, 59, 60 }, { 30, 30, 90, 91 }, { 31, 31, 120, 121 }, { 30, 30, 151, 152 }, { 31, 31, 181, 182 }, { 31, 31, 212, 213 }, { 30, 30, 243, 244 }, { 31, 31, 273, 274 }, { 30, 30, 304, 305 }, { 31, 31, 334, 335 } }; 4960 4961 4968 protected static final boolean isGregorianLeapYear(int year) { 4969 return (year%4 == 0) && ((year%100 != 0) || (year%400 == 0)); 4970 } 4971 4972 4979 protected static final int gregorianMonthLength(int y, int m) { 4980 return GREGORIAN_MONTH_COUNT[m][isGregorianLeapYear(y)?1:0]; 4981 } 4982 4983 4990 protected static final int gregorianPreviousMonthLength(int y, int m) { 4991 return (m > 0) ? gregorianMonthLength(y, m-1) : 31; 4992 } 4993 4994 5005 protected static final long floorDivide(long numerator, long denominator) { 5006 return (numerator >= 0) ? 5009 numerator / denominator : 5010 ((numerator + 1) / denominator) - 1; 5011 } 5012 5013 5024 protected static final int floorDivide(int numerator, int denominator) { 5025 return (numerator >= 0) ? 5028 numerator / denominator : 5029 ((numerator + 1) / denominator) - 1; 5030 } 5031 5032 5047 protected static final int floorDivide(int numerator, int denominator, int[] remainder) { 5048 if (numerator >= 0) { 5049 remainder[0] = numerator % denominator; 5050 return numerator / denominator; 5051 } 5052 int quotient = ((numerator + 1) / denominator) - 1; 5053 remainder[0] = numerator - (quotient * denominator); 5054 return quotient; 5055 } 5056 5057 5072 protected static final int floorDivide(long numerator, int denominator, int[] remainder) { 5073 if (numerator >= 0) { 5074 remainder[0] = (int)(numerator % denominator); 5075 return (int)(numerator / denominator); 5076 } 5077 int quotient = (int)(((numerator + 1) / denominator) - 1); 5078 remainder[0] = (int)(numerator - (quotient * denominator)); 5079 return quotient; 5080 } 5081 5082 private static final String [] FIELD_NAME = { 5083 "ERA", "YEAR", "MONTH", "WEEK_OF_YEAR", "WEEK_OF_MONTH", 5084 "DAY_OF_MONTH", "DAY_OF_YEAR", "DAY_OF_WEEK", 5085 "DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY", 5086 "MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET", 5087 "DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR", 5088 "JULIAN_DAY", "MILLISECONDS_IN_DAY", 5089 }; 5090 5091 5095 protected String fieldName(int field) { 5096 try { 5097 return FIELD_NAME[field]; 5098 } catch (ArrayIndexOutOfBoundsException e) { 5099 return "Field " + field; 5100 } 5101 } 5102 5103 5109 protected static final int millisToJulianDay(long millis) { 5110 return (int) (EPOCH_JULIAN_DAY + floorDivide(millis, ONE_DAY)); 5111 } 5112 5113 5119 protected static final long julianDayToMillis(int julian) { 5120 return (julian - EPOCH_JULIAN_DAY) * ONE_DAY; 5121 } 5122 5123 5127 protected static final int julianDayToDayOfWeek(int julian) { 5128 int dayOfWeek = (julian + MONDAY) % 7; 5131 if (dayOfWeek < SUNDAY) { 5132 dayOfWeek += 7; 5133 } 5134 return dayOfWeek; 5135 } 5136 5137 5141 protected final long internalGetTimeInMillis() { 5142 return time; 5143 } 5144 5145 5152 public String getType() { 5153 return "gregorian"; 5154 } 5155 5156 5158 5182 public final ULocale getLocale(ULocale.Type type) { 5183 return type == ULocale.ACTUAL_LOCALE ? 5184 this.actualLocale : this.validLocale; 5185 } 5186 5187 5204 final void setLocale(ULocale valid, ULocale actual) { 5205 if ((valid == null) != (actual == null)) { 5207 throw new IllegalArgumentException (); 5209 } 5211 this.validLocale = valid; 5214 this.actualLocale = actual; 5215 } 5216 5217 5222 private ULocale validLocale; 5223 5224 5230 private ULocale actualLocale; 5231 5232 } 5234 5235 | Popular Tags |