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
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
14 * These utilities are used by both OlsonTimeZone and SimpleTimeZone.
\r
17 package com.ibm.icu.impl;
\r
19 import com.ibm.icu.util.Calendar;
\r
22 * A utility class providing proleptic Gregorian calendar functions
\r
23 * used by time zone and calendar code. Do not instantiate.
\r
25 * Note: Unlike GregorianCalendar, all computations performed by this
\r
26 * class occur in the pure proleptic GregorianCalendar.
\r
28 public class Grego {
\r
30 // Max/min milliseconds
\r
31 public static final long MIN_MILLIS = -184303902528000000L;
\r
32 public static final long MAX_MILLIS = 183882168921600000L;
\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
39 // January 1, 1 CE Gregorian
\r
40 private static final int JULIAN_1_CE = 1721426;
\r
42 // January 1, 1970 CE Gregorian
\r
43 private static final int JULIAN_1970_CE = 2440588;
\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
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
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
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
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
70 public static final int monthLength(int year, int month) {
\r
71 return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)];
\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
80 public static final int previousMonthLength(int year, int month) {
\r
81 return (month > 0) ? monthLength(year, month-1) : 31;
\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
92 public static long fieldsToDay(int year, int month, int dom) {
\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
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
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
114 public static int[] dayToFields(long day, int[] fields) {
\r
115 if (fields == null || fields.length < 5) {
\r
116 fields = new int[5];
\r
118 // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
\r
119 day += JULIAN_1970_CE - JULIAN_1_CE;
\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
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
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
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
148 dayOfYear++; // 1-based day of year
\r
152 fields[2] = dayOfMonth;
\r
153 fields[3] = dayOfWeek;
\r
154 fields[4] = dayOfYear;
\r
160 * Convert long time to date/time fields
\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
169 public static int[] timeToFields(long time, int[] fields) {
\r
170 if (fields == null || fields.length < 6) {
\r
171 fields = new int[6];
\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
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
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
193 long quotient = ((numerator + 1) / denominator) - 1;
\r
194 remainder[0] = numerator - (quotient * denominator);
\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
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
208 } else if (weekInMonth == 5) {
\r
211 return weekInMonth;
\r