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