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) 1996-2014, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9
10package com.ibm.icu.util;
11
12import java.util.Date;
13import java.util.Locale;
14
15import com.ibm.icu.util.ULocale.Category;
16
17/**
18 * <code>PersianCalendar</code> is a subclass of <code>Calendar</code> that
19 * that implements the Persian calendar.  It is used as the main civil
20 * calendar in Iran and Afghanistan, and by Iranians and Afghans worldwide.
21 * <p>
22 * The Persian calendar is solar, and is similar to the Gregorian calendar
23 * in various ways, except its leap year rule, which is determined
24 * astronomically.  The Persian year starts around the March equinox.
25 * <p>
26 * The modern Persian calendar (used in Iran since 1925 CE and in
27 * Afghanistan since 1957 CE), has the lengths of the months fixed.  The
28 * first six months are 31 days each, the next five months are 30 days each,
29 * and the final month is 29 days in non-leap years and 30 days in leap
30 * ones.  Historically, the lengths of the month differed in different
31 * years, but they were finally fixed at the times mentioned above.  Partial
32 * information is available about the historical lengths.
33 * <p>
34 * The official rule for determination of the beginning of the Persian year
35 * is locale dependent, but at the same time, it has not specified a locale.
36 * Iranians around the world traditionally follow the calendar authorities
37 * of Iran, which haven't officially specified the locale.  Some
38 * calendarists use some point in Tehran as the locale, while others have
39 * tried the more neutral 52.5 degrees east meridian.  It is not clear which
40 * locale should be used for the Persian calendar of Afghanistan, but it is
41 * expected that for about one year in every twenty-four years, the Afghan
42 * calendar may become different from the Iranian one.
43 * <p>
44 * The exact locale to be used for the Iranian calendar starts to make a
45 * difference at around 2090 CE.  The specific arithmetic method implemented
46 * here, commonly known as the 33-year cycle rule, matches the astronomical
47 * calendar at least for the whole period that the calendar has been both
48 * well-defined and official, from 1925 to around 2090 CE.  The other
49 * commonly known algorithm, the 2820-year cycle, has been incorrectly
50 * designed to follow the tropical year instead of the spring equinoctial
51 * year, and fails to match the astronomical one as early as 2025 CE.
52 * <p>
53 * This class should not be subclassed.</p>
54 * <p>
55 * PersianCalendar usually should be instantiated using
56 * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a
57 * <code>ULocale</code> with the tag <code>"@calendar=persian"</code>.</p>
58 *
59 * @see com.ibm.icu.util.GregorianCalendar
60 * @see com.ibm.icu.util.Calendar
61 *
62 * @author Roozbeh Pournader
63 *
64 * @internal
65 * @deprecated This API is ICU internal only.
66 */
67@Deprecated
68public class PersianCalendar extends Calendar {
69    private static final long serialVersionUID = -6727306982975111643L;
70
71    //-------------------------------------------------------------------------
72    // Constants...
73    //-------------------------------------------------------------------------
74
75    private static final int[][] MONTH_COUNT = {
76        //len len2   st
77        {  31,  31,   0 }, // Farvardin
78        {  31,  31,  31 }, // Ordibehesht
79        {  31,  31,  62 }, // Khordad
80        {  31,  31,  93 }, // Tir
81        {  31,  31, 124 }, // Mordad
82        {  31,  31, 155 }, // Shahrivar
83        {  30,  30, 186 }, // Mehr
84        {  30,  30, 216 }, // Aban
85        {  30,  30, 246 }, // Azar
86        {  30,  30, 276 }, // Dey
87        {  30,  30, 306 }, // Bahman
88        {  29,  30, 336 }  // Esfand
89        // len  length of month
90        // len2 length of month in a leap year
91        // st   days in year before start of month
92    };
93
94    private static final int PERSIAN_EPOCH = 1948320;
95
96    //-------------------------------------------------------------------------
97    // Constructors...
98    //-------------------------------------------------------------------------
99
100    /**
101     * Constructs a default <code>PersianCalendar</code> using the current time
102     * in the default time zone with the default <code>FORMAT</code> locale.
103     * @see Category#FORMAT
104     *
105     * @internal
106     * @deprecated This API is ICU internal only.
107     */
108    @Deprecated
109    public PersianCalendar()
110    {
111        this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
112    }
113
114    /**
115     * Constructs a <code>PersianCalendar</code> based on the current time
116     * in the given time zone with the default <code>FORMAT</code> locale.
117     * @param zone the given time zone.
118     * @see Category#FORMAT
119     *
120     * @internal
121     * @deprecated This API is ICU internal only.
122     */
123    @Deprecated
124    public PersianCalendar(TimeZone zone)
125    {
126        this(zone, ULocale.getDefault(Category.FORMAT));
127    }
128
129    /**
130     * Constructs a <code>PersianCalendar</code> based on the current time
131     * in the default time zone with the given locale.
132     *
133     * @param aLocale the given locale.
134     *
135     * @internal
136     * @deprecated This API is ICU internal only.
137     */
138    @Deprecated
139    public PersianCalendar(Locale aLocale)
140    {
141        this(TimeZone.getDefault(), aLocale);
142    }
143
144    /**
145     * Constructs a <code>PersianCalendar</code> based on the current time
146     * in the default time zone with the given locale.
147     *
148     * @param locale the given ulocale.
149     *
150     * @internal
151     * @deprecated This API is ICU internal only.
152     */
153    @Deprecated
154    public PersianCalendar(ULocale locale)
155    {
156        this(TimeZone.getDefault(), locale);
157    }
158
159    /**
160     * Constructs a <code>PersianCalendar</code> based on the current time
161     * in the given time zone with the given locale.
162     *
163     * @param zone the given time zone.
164     * @param aLocale the given locale.
165     *
166     * @internal
167     * @deprecated This API is ICU internal only.
168     */
169    @Deprecated
170    public PersianCalendar(TimeZone zone, Locale aLocale)
171    {
172        super(zone, aLocale);
173        setTimeInMillis(System.currentTimeMillis());
174    }
175
176    /**
177     * Constructs a <code>PersianCalendar</code> based on the current time
178     * in the given time zone with the given locale.
179     *
180     * @param zone the given time zone.
181     * @param locale the given ulocale.
182     *
183     * @internal
184     * @deprecated This API is ICU internal only.
185     */
186    @Deprecated
187    public PersianCalendar(TimeZone zone, ULocale locale)
188    {
189        super(zone, locale);
190        setTimeInMillis(System.currentTimeMillis());
191    }
192
193    /**
194     * Constructs a <code>PersianCalendar</code> with the given date set
195     * in the default time zone with the default <code>FORMAT</code> locale.
196     *
197     * @param date      The date to which the new calendar is set.
198     * @see Category#FORMAT
199     *
200     * @internal
201     * @deprecated This API is ICU internal only.
202     */
203    @Deprecated
204    public PersianCalendar(Date date) {
205        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
206        this.setTime(date);
207    }
208
209    /**
210     * Constructs a <code>PersianCalendar</code> with the given date set
211     * in the default time zone with the default <code>FORMAT</code> locale.
212     *
213     * @param year the value used to set the {@link #YEAR YEAR} time field in the calendar.
214     * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
215     *              Note that the month value is 0-based. e.g., 0 for Farvardin.
216     * @param date the value used to set the {@link #DATE DATE} time field in the calendar.
217     * @see Category#FORMAT
218     *
219     * @internal
220     * @deprecated This API is ICU internal only.
221     */
222    @Deprecated
223    public PersianCalendar(int year, int month, int date)
224    {
225        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
226        this.set(Calendar.YEAR, year);
227        this.set(Calendar.MONTH, month);
228        this.set(Calendar.DATE, date);
229    }
230
231    /**
232     * Constructs a <code>PersianCalendar</code> with the given date
233     * and time set for the default time zone with the default <code>FORMAT</code> locale.
234     *
235     * @param year  the value used to set the {@link #YEAR YEAR} time field in the calendar.
236     * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
237     *              Note that the month value is 0-based. e.g., 0 for Farvardin.
238     * @param date  the value used to set the {@link #DATE DATE} time field in the calendar.
239     * @param hour  the value used to set the {@link #HOUR_OF_DAY HOUR_OF_DAY} time field
240     *              in the calendar.
241     * @param minute the value used to set the {@link #MINUTE MINUTE} time field
242     *              in the calendar.
243     * @param second the value used to set the {@link #SECOND SECOND} time field
244     *              in the calendar.
245     * @see Category#FORMAT
246     *
247     * @internal
248     * @deprecated This API is ICU internal only.
249     */
250    @Deprecated
251    public PersianCalendar(int year, int month, int date, int hour,
252                           int minute, int second)
253    {
254        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
255        this.set(Calendar.YEAR, year);
256        this.set(Calendar.MONTH, month);
257        this.set(Calendar.DATE, date);
258        this.set(Calendar.HOUR_OF_DAY, hour);
259        this.set(Calendar.MINUTE, minute);
260        this.set(Calendar.SECOND, second);
261    }
262
263    //-------------------------------------------------------------------------
264    // Minimum / Maximum access functions
265    //-------------------------------------------------------------------------
266
267    private static final int LIMITS[][] = {
268        // Minimum  Greatest     Least   Maximum
269        //           Minimum   Maximum
270        {        0,        0,        0,        0}, // ERA
271        { -5000000, -5000000,  5000000,  5000000}, // YEAR
272        {        0,        0,       11,       11}, // MONTH
273        {        1,        1,       52,       53}, // WEEK_OF_YEAR
274        {/*                                   */}, // WEEK_OF_MONTH
275        {        1,        1,       29,       31}, // DAY_OF_MONTH
276        {        1,        1,      365,      366}, // DAY_OF_YEAR
277        {/*                                   */}, // DAY_OF_WEEK
278        {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
279        {/*                                   */}, // AM_PM
280        {/*                                   */}, // HOUR
281        {/*                                   */}, // HOUR_OF_DAY
282        {/*                                   */}, // MINUTE
283        {/*                                   */}, // SECOND
284        {/*                                   */}, // MILLISECOND
285        {/*                                   */}, // ZONE_OFFSET
286        {/*                                   */}, // DST_OFFSET
287        { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
288        {/*                                   */}, // DOW_LOCAL
289        { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
290        {/*                                   */}, // JULIAN_DAY
291        {/*                                   */}, // MILLISECONDS_IN_DAY
292    };
293
294    /**
295     * @internal
296     * @deprecated This API is ICU internal only.
297     */
298    @Deprecated
299    protected int handleGetLimit(int field, int limitType) {
300        return LIMITS[field][limitType];
301    }
302
303    //-------------------------------------------------------------------------
304    // Assorted calculation utilities
305    //
306
307    /**
308     * Determine whether a year is a leap year in the Persian calendar
309     */
310    private final static boolean isLeapYear(int year)
311    {
312        int[] remainder = new int[1];
313        floorDivide(25 * year + 11, 33, remainder);
314        return remainder[0] < 8;
315
316    }
317
318    //----------------------------------------------------------------------
319    // Calendar framework
320    //----------------------------------------------------------------------
321
322    /**
323     * Return the length (in days) of the given month.
324     *
325     * @param extendedYear  The Persian year
326     * @param month The Persian month, 0-based
327     *
328     * @internal
329     * @deprecated This API is ICU internal only.
330     */
331    @Deprecated
332    protected int handleGetMonthLength(int extendedYear, int month) {
333        // If the month is out of range, adjust it into range, and
334        // modify the extended year value accordingly.
335        if (month < 0 || month > 11) {
336            int[] rem = new int[1];
337            extendedYear += floorDivide(month, 12, rem);
338            month = rem[0];
339        }
340
341        return MONTH_COUNT[month][isLeapYear(extendedYear)?1:0];
342    }
343
344    /**
345     * Return the number of days in the given Persian year
346     *
347     * @internal
348     * @deprecated This API is ICU internal only.
349     */
350    @Deprecated
351    protected int handleGetYearLength(int extendedYear) {
352        return isLeapYear(extendedYear) ? 366 : 365;
353    }
354
355    //-------------------------------------------------------------------------
356    // Functions for converting from field values to milliseconds....
357    //-------------------------------------------------------------------------
358
359    /**
360     * Return JD of start of given month/year
361     *
362     * @internal
363     * @deprecated This API is ICU internal only.
364     */
365    @Deprecated
366    protected int handleComputeMonthStart(int eyear, int month, boolean useMonth) {
367        // If the month is out of range, adjust it into range, and
368        // modify the extended year value accordingly.
369        if (month < 0 || month > 11) {
370            int[] rem = new int[1];
371            eyear += floorDivide(month, 12, rem);
372            month = rem[0];
373        }
374
375        int julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + floorDivide(8 * eyear + 21, 33);
376        if (month != 0) {
377            julianDay += MONTH_COUNT[month][2];
378        }
379        return julianDay;
380    }
381
382    //-------------------------------------------------------------------------
383    // Functions for converting from milliseconds to field values
384    //-------------------------------------------------------------------------
385
386    /**
387     * @internal
388     * @deprecated This API is ICU internal only.
389     */
390    @Deprecated
391    protected int handleGetExtendedYear() {
392        int year;
393        if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR) {
394            year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
395        } else {
396            year = internalGet(YEAR, 1); // Default to year 1
397        }
398        return year;
399    }
400
401    /**
402     * Override Calendar to compute several fields specific to the Persian
403     * calendar system.  These are:
404     *
405     * <ul><li>ERA
406     * <li>YEAR
407     * <li>MONTH
408     * <li>DAY_OF_MONTH
409     * <li>DAY_OF_YEAR
410     * <li>EXTENDED_YEAR</ul>
411     *
412     * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
413     * method is called.
414     *
415     * @internal
416     * @deprecated This API is ICU internal only.
417     */
418    @Deprecated
419    protected void handleComputeFields(int julianDay) {
420        int year, month, dayOfMonth, dayOfYear;
421
422        long daysSinceEpoch = julianDay - PERSIAN_EPOCH;
423        year = 1 + (int) floorDivide(33 * daysSinceEpoch + 3, 12053);
424
425        long farvardin1 = 365L * (year - 1L) + floorDivide(8L * year + 21, 33L);
426        dayOfYear = (int)(daysSinceEpoch - farvardin1); // 0-based
427        if (dayOfYear < 216) { // Compute 0-based month
428            month = dayOfYear / 31;
429        } else {
430            month = (dayOfYear - 6) / 30;
431        }
432        dayOfMonth = dayOfYear - MONTH_COUNT[month][2] + 1;
433        ++dayOfYear; // Make it 1-based now
434
435        internalSet(ERA, 0);
436        internalSet(YEAR, year);
437        internalSet(EXTENDED_YEAR, year);
438        internalSet(MONTH, month);
439        internalSet(DAY_OF_MONTH, dayOfMonth);
440        internalSet(DAY_OF_YEAR, dayOfYear);
441    }
442
443    /**
444     * {@inheritDoc}
445     *
446     * @internal
447     * @deprecated This API is ICU internal only.
448     */
449    @Deprecated
450    public String getType() {
451        return "persian";
452    }
453}
454