1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 *******************************************************************************
5 * Copyright (C) 2005-2011, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9package com.ibm.icu.util;
10
11import java.util.Date;
12import java.util.Locale;
13
14import com.ibm.icu.util.ULocale.Category;
15
16/**
17 * Base class for EthiopicCalendar and CopticCalendar.
18 */
19abstract class CECalendar extends Calendar {
20    // jdk1.4.2 serialver
21    private static final long serialVersionUID = -999547623066414271L;
22
23    private static final int LIMITS[][] = {
24        // Minimum  Greatest    Least  Maximum
25        //           Minimum  Maximum
26        {        0,        0,       1,       1 }, // ERA
27        {        1,        1, 5000000, 5000000 }, // YEAR
28        {        0,        0,      12,      12 }, // MONTH
29        {        1,        1,      52,      53 }, // WEEK_OF_YEAR
30        {/*                                  */}, // WEEK_OF_MONTH
31        {        1,        1,       5,      30 }, // DAY_OF_MONTH
32        {        1,        1,     365,     366 }, // DAY_OF_YEAR
33        {/*                                  */}, // DAY_OF_WEEK
34        {       -1,       -1,       1,       5 }, // DAY_OF_WEEK_IN_MONTH
35        {/*                                  */}, // AM_PM
36        {/*                                  */}, // HOUR
37        {/*                                  */}, // HOUR_OF_DAY
38        {/*                                  */}, // MINUTE
39        {/*                                  */}, // SECOND
40        {/*                                  */}, // MILLISECOND
41        {/*                                  */}, // ZONE_OFFSET
42        {/*                                  */}, // DST_OFFSET
43        { -5000000, -5000000, 5000000, 5000000 }, // YEAR_WOY
44        {/*                                  */}, // DOW_LOCAL
45        { -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
46        {/*                                  */}, // JULIAN_DAY
47        {/*                                  */}, // MILLISECONDS_IN_DAY
48    };
49
50    //-------------------------------------------------------------------------
51    // Constructors...
52    //-------------------------------------------------------------------------
53
54    /**
55     * Constructs a default <code>CECalendar</code> using the current time
56     * in the default time zone with the default <code>FORMAT</code> locale.
57     */
58    protected CECalendar() {
59        this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
60    }
61
62    /**
63     * Constructs a <code>CECalendar</code> based on the current time
64     * in the given time zone with the default <code>FORMAT</code> locale.
65     *
66     * @param zone The time zone for the new calendar.
67     */
68    protected CECalendar(TimeZone zone) {
69        this(zone, ULocale.getDefault(Category.FORMAT));
70    }
71
72    /**
73     * Constructs a <code>CECalendar</code> based on the current time
74     * in the default time zone with the given locale.
75     *
76     * @param aLocale The locale for the new calendar.
77     */
78    protected CECalendar(Locale aLocale) {
79        this(TimeZone.getDefault(), aLocale);
80    }
81
82    /**
83     * Constructs a <code>CECalendar</code> based on the current time
84     * in the default time zone with the given locale.
85     *
86     * @param locale The locale for the new calendar.
87     */
88    protected CECalendar(ULocale locale) {
89        this(TimeZone.getDefault(), locale);
90    }
91
92    /**
93     * Constructs a <code>CECalendar</code> based on the current time
94     * in the given time zone with the given locale.
95     *
96     * @param zone The time zone for the new calendar.
97     *
98     * @param aLocale The locale for the new calendar.
99     */
100    protected CECalendar(TimeZone zone, Locale aLocale) {
101        super(zone, aLocale);
102        setTimeInMillis(System.currentTimeMillis());
103    }
104
105    /**
106     * Constructs a <code>CECalendar</code> based on the current time
107     * in the given time zone with the given locale.
108     *
109     * @param zone The time zone for the new calendar.
110     *
111     * @param locale The locale for the new calendar.
112     */
113    protected CECalendar(TimeZone zone, ULocale locale) {
114        super(zone, locale);
115        setTimeInMillis(System.currentTimeMillis());
116    }
117
118    /**
119     * Constructs a <code>CECalendar</code> with the given date set
120     * in the default time zone with the default <code>FORMAT</code> locale.
121     *
122     * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
123     *
124     * @param month     The value used to set the calendar's {@link #MONTH MONTH} time field.
125     *                  The value is 0-based. e.g., 0 for Tishri.
126     *
127     * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
128     */
129    protected CECalendar(int year, int month, int date) {
130        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
131        this.set(year, month, date);
132    }
133
134    /**
135     * Constructs a <code>CECalendar</code> with the given date set
136     * in the default time zone with the default <code>FORMAT</code> locale.
137     *
138     * @param date      The date to which the new calendar is set.
139     */
140    protected CECalendar(Date date) {
141        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
142        this.setTime(date);
143    }
144
145    /**
146     * Constructs a <code>CECalendar</code> with the given date
147     * and time set for the default time zone with the default <code>FORMAT</code> locale.
148     *
149     * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
150     * @param month     The value used to set the calendar's {@link #MONTH MONTH} time field.
151     *                  The value is 0-based. e.g., 0 for Tishri.
152     * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
153     * @param hour      The value used to set the calendar's {@link #HOUR_OF_DAY HOUR_OF_DAY} time field.
154     * @param minute    The value used to set the calendar's {@link #MINUTE MINUTE} time field.
155     * @param second    The value used to set the calendar's {@link #SECOND SECOND} time field.
156     */
157    protected CECalendar(int year, int month, int date, int hour,
158                         int minute, int second)
159    {
160        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
161        this.set(year, month, date, hour, minute, second);
162    }
163
164    //-------------------------------------------------------------------------
165    // Calendar framework
166    //-------------------------------------------------------------------------
167
168    /**
169     * The Coptic and Ethiopic calendars differ only in their epochs.
170     * This method must be implemented by CECalendar subclasses to
171     * return the date offset from Julian.
172     */
173    abstract protected int getJDEpochOffset();
174
175    /**
176     * Return JD of start of given month/extended year
177     */
178    protected int handleComputeMonthStart(int eyear,
179                                          int emonth,
180                                          boolean useMonth) {
181        return ceToJD(eyear, emonth, 0, getJDEpochOffset());
182    }
183
184    /**
185     * Calculate the limit for a specified type of limit and field
186     */
187    protected int handleGetLimit(int field, int limitType) {
188        return LIMITS[field][limitType];
189    }
190
191    // (The following method is not called because all existing subclasses
192    // override it.
193    ///CLOVER:OFF
194    /**
195     * Return the number of days in the given month of the given extended
196     * year of this calendar system.  Subclasses should override this
197     * method if they can provide a more correct or more efficient
198     * implementation than the default implementation in Calendar.
199     */
200    protected int handleGetMonthLength(int extendedYear, int month)
201    {
202
203        // The Ethiopian and Coptic calendars have 13 months, 12 of 30 days each and
204        // an intercalary month at the end of the year of 5 or 6 days, depending whether
205        // the year is a leap year or not. The Leap Year follows the same rules as the
206        // Julian Calendar so that the extra month always has six days in the year before
207        // a Julian Leap Year.
208        if ((month + 1) % 13 != 0)
209        {
210            // not intercalary month
211            return 30;
212        }
213        else
214        {
215            // intercalary month 5 days + possible leap day
216            return ((extendedYear % 4) / 3) + 5;
217        }
218
219    }
220    ///CLOVER:ON
221
222    //-------------------------------------------------------------------------
223    // Calendar framework
224    //-------------------------------------------------------------------------
225
226    /**
227     * Convert an Coptic/Ethiopic year, month and day to a Julian day
228     * @param year the extended year
229     * @param month the month
230     * @param day the day
231     * @return Julian day
232     */
233    public static int ceToJD(long year, int month, int day, int jdEpochOffset) {
234
235        // Julian<->Ethiopic algorithms from:
236        // "Calendars in Ethiopia", Berhanu Beyene, Manfred Kudlek, International Conference
237        // of Ethiopian Studies XV, Hamburg, 2003
238
239        // handle month > 12, < 0 (e.g. from add/set)
240        if ( month >= 0 ) {
241            year += month/13;
242            month %= 13;
243        } else {
244            ++month;
245            year += month/13 - 1;
246            month = month%13 + 12;
247        }
248        return (int) (
249            jdEpochOffset           // difference from Julian epoch to 1,1,1
250            + 365 * year            // number of days from years
251            + floorDivide(year, 4)  // extra day of leap year
252            + 30 * month            // number of days from months (months are 0-based)
253            + day - 1               // number of days for present month (1 based)
254            );
255    }
256
257    /**
258     * Convert a Julian day to an Coptic/Ethiopic year, month and day
259     */
260    public static void jdToCE(int julianDay, int jdEpochOffset, int[] fields) {
261        int c4; // number of 4 year cycle (1461 days)
262        int[] r4 = new int[1]; // remainder of 4 year cycle, always positive
263
264        c4 = floorDivide(julianDay - jdEpochOffset, 1461, r4);
265
266        // exteded year
267        fields[0] = 4 * c4 + (r4[0]/365 - r4[0]/1460); // 4 * <number of 4year cycle> + <years within the last cycle>
268
269        int doy = (r4[0] == 1460) ? 365 : (r4[0] % 365); // days in present year
270
271        // month
272        fields[1] = doy / 30; // 30 -> Coptic/Ethiopic month length up to 12th month
273        // day
274        fields[2] = (doy % 30) + 1; // 1-based days in a month
275    }
276}
277