]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/Grego.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / Grego.java
1 /**\r
2  *******************************************************************************\r
3  * Copyright (C) 2003-2008, International Business Machines Corporation and\r
4  * others. All Rights Reserved.\r
5  *******************************************************************************\r
6  * Partial port from ICU4C's Grego class in i18n/gregoimp.h.\r
7  *\r
8  * Methods ported, or moved here from OlsonTimeZone, initially\r
9  * for work on Jitterbug 5470:\r
10  *   tzdata2006n Brazil incorrect fall-back date 2009-mar-01\r
11  * Only the methods necessary for that work are provided - this is not a full\r
12  * port of ICU4C's Grego class (yet).\r
13  *\r
14  * These utilities are used by both OlsonTimeZone and SimpleTimeZone.\r
15  */\r
16 \r
17 package com.ibm.icu.impl;\r
18 \r
19 import com.ibm.icu.util.Calendar;\r
20 \r
21 /**\r
22  * A utility class providing proleptic Gregorian calendar functions\r
23  * used by time zone and calendar code.  Do not instantiate.\r
24  *\r
25  * Note:  Unlike GregorianCalendar, all computations performed by this\r
26  * class occur in the pure proleptic GregorianCalendar.\r
27  */\r
28 public class Grego {\r
29 \r
30     // Max/min milliseconds \r
31     public static final long MIN_MILLIS = -184303902528000000L;\r
32     public static final long MAX_MILLIS = 183882168921600000L;\r
33 \r
34     public static final int MILLIS_PER_SECOND = 1000;\r
35     public static final int MILLIS_PER_MINUTE = 60*MILLIS_PER_SECOND;\r
36     public static final int MILLIS_PER_HOUR = 60*MILLIS_PER_MINUTE;\r
37     public static final int MILLIS_PER_DAY = 24*MILLIS_PER_HOUR;\r
38     \r
39     //  January 1, 1 CE Gregorian\r
40     private static final int JULIAN_1_CE = 1721426;\r
41 \r
42     //  January 1, 1970 CE Gregorian\r
43     private static final int JULIAN_1970_CE = 2440588;\r
44 \r
45     private static final int[] MONTH_LENGTH = new int[] {\r
46         31,28,31,30,31,30,31,31,30,31,30,31,\r
47         31,29,31,30,31,30,31,31,30,31,30,31\r
48     };\r
49 \r
50     private static final int[] DAYS_BEFORE = new int[] {\r
51         0,31,59,90,120,151,181,212,243,273,304,334,\r
52         0,31,60,91,121,152,182,213,244,274,305,335 };\r
53 \r
54     /**\r
55      * Return true if the given year is a leap year.\r
56      * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.\r
57      * @return true if the year is a leap year\r
58      */\r
59     public static final boolean isLeapYear(int year) {\r
60         // year&0x3 == year%4\r
61         return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0));\r
62     }\r
63 \r
64     /**\r
65      * Return the number of days in the given month.\r
66      * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.\r
67      * @param month 0-based month, with 0==Jan\r
68      * @return the number of days in the given month\r
69      */\r
70     public static final int monthLength(int year, int month) {\r
71         return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)];\r
72     }\r
73 \r
74     /**\r
75      * Return the length of a previous month of the Gregorian calendar.\r
76      * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.\r
77      * @param month 0-based month, with 0==Jan\r
78      * @return the number of days in the month previous to the given month\r
79      */\r
80     public static final int previousMonthLength(int year, int month) {\r
81         return (month > 0) ? monthLength(year, month-1) : 31;\r
82     }\r
83 \r
84     /**\r
85      * Convert a year, month, and day-of-month, given in the proleptic\r
86      * Gregorian calendar, to 1970 epoch days.\r
87      * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.\r
88      * @param month 0-based month, with 0==Jan\r
89      * @param dom 1-based day of month\r
90      * @return the day number, with day 0 == Jan 1 1970\r
91      */\r
92     public static long fieldsToDay(int year, int month, int dom) {\r
93         int y = year - 1;\r
94         long julian =\r
95             365 * y + floorDivide(y, 4) + (JULIAN_1_CE - 3) +    // Julian cal\r
96             floorDivide(y, 400) - floorDivide(y, 100) + 2 +   // => Gregorian cal\r
97             DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; // => month/dom\r
98         return julian - JULIAN_1970_CE; // JD => epoch day\r
99     }\r
100 \r
101     /**\r
102      * Return the day of week on the 1970-epoch day\r
103      * @param day the 1970-epoch day (integral value)\r
104      * @return the day of week\r
105      */\r
106     public static int dayOfWeek(long day) {\r
107         long[] remainder = new long[1];\r
108         floorDivide(day + Calendar.THURSDAY, 7, remainder);\r
109         int dayOfWeek = (int)remainder[0];\r
110         dayOfWeek = (dayOfWeek == 0) ? 7 : dayOfWeek;\r
111         return dayOfWeek;\r
112     }\r
113 \r
114     public static int[] dayToFields(long day, int[] fields) {\r
115         if (fields == null || fields.length < 5) {\r
116             fields = new int[5];\r
117         }\r
118         // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)\r
119         day += JULIAN_1970_CE - JULIAN_1_CE;\r
120 \r
121         long[] rem = new long[1];\r
122         long n400 = floorDivide(day, 146097, rem);\r
123         long n100 = floorDivide(rem[0], 36524, rem);\r
124         long n4 = floorDivide(rem[0], 1461, rem);\r
125         long n1 = floorDivide(rem[0], 365, rem);\r
126 \r
127         int year = (int)(400 * n400 + 100 * n100 + 4 * n4 + n1);\r
128         int dayOfYear = (int)rem[0];\r
129         if (n100 == 4 || n1 == 4) {\r
130             dayOfYear = 365;    // Dec 31 at end of 4- or 400-yr cycle\r
131         }\r
132         else {\r
133             ++year;\r
134         }\r
135 \r
136         boolean isLeap = isLeapYear(year);\r
137         int correction = 0;\r
138         int march1 = isLeap ? 60 : 59;  // zero-based DOY for March 1\r
139         if (dayOfYear >= march1) {\r
140             correction = isLeap ? 1 : 2;\r
141         }\r
142         int month = (12 * (dayOfYear + correction) + 6) / 367;  // zero-based month\r
143         int dayOfMonth = dayOfYear - DAYS_BEFORE[isLeap ? month + 12 : month] + 1; // one-based DOM\r
144         int dayOfWeek = (int)((day + 2) % 7);  // day 0 is Monday(2)\r
145         if (dayOfWeek < 1 /* Sunday */) {\r
146             dayOfWeek += 7;\r
147         }\r
148         dayOfYear++; // 1-based day of year\r
149 \r
150         fields[0] = year;\r
151         fields[1] = month;\r
152         fields[2] = dayOfMonth;\r
153         fields[3] = dayOfWeek;\r
154         fields[4] = dayOfYear;\r
155 \r
156         return fields;\r
157     }\r
158 \r
159     /*\r
160      * Convert long time to date/time fields\r
161      * \r
162      * result[0] : year\r
163      * result[1] : month\r
164      * result[2] : dayOfMonth\r
165      * result[3] : dayOfWeek\r
166      * result[4] : dayOfYear\r
167      * result[5] : millisecond in day\r
168      */\r
169     public static int[] timeToFields(long time, int[] fields) {\r
170         if (fields == null || fields.length < 6) {\r
171             fields = new int[6];\r
172         }\r
173         long[] remainder = new long[1];\r
174         long day = floorDivide(time, 24*60*60*1000 /* milliseconds per day */, remainder);\r
175         dayToFields(day, fields);\r
176         fields[5] = (int)remainder[0];\r
177         return fields;\r
178     }\r
179 \r
180     public static long floorDivide(long numerator, long denominator) {\r
181         // We do this computation in order to handle\r
182         // a numerator of Long.MIN_VALUE correctly\r
183         return (numerator >= 0) ?\r
184             numerator / denominator :\r
185             ((numerator + 1) / denominator) - 1;\r
186     }\r
187 \r
188     private static long floorDivide(long numerator, long denominator, long[] remainder) {\r
189         if (numerator >= 0) {\r
190             remainder[0] = numerator % denominator;\r
191             return numerator / denominator;\r
192         }\r
193         long quotient = ((numerator + 1) / denominator) - 1;\r
194         remainder[0] = numerator - (quotient * denominator);\r
195         return quotient;\r
196     }\r
197 \r
198     /*\r
199      * Returns the ordinal number for the specified day of week in the month.\r
200      * The valid return value is 1, 2, 3, 4 or -1.\r
201      */\r
202     public static int getDayOfWeekInMonth(int year, int month, int dayOfMonth) {\r
203         int weekInMonth = (dayOfMonth + 6)/7;\r
204         if (weekInMonth == 4) {\r
205             if (dayOfMonth + 7 > monthLength(year, month)) {\r
206                 weekInMonth = -1;\r
207             }\r
208         } else if (weekInMonth == 5) {\r
209             weekInMonth = -1;\r
210         }\r
211         return weekInMonth;\r
212     }\r
213 }\r