--- /dev/null
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2012, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+ */
+
+package com.ibm.icu.util;
+
+import java.util.Date;
+import java.util.Locale;
+
+import com.ibm.icu.util.ULocale.Category;
+
+/**
+ * <code>PersianCalendar</code> is a subclass of <code>Calendar</code> that
+ * that implements the Persian calendar. It is used as the main civil
+ * calendar in Iran and Afghanistan, and by Iranians and Afghans worldwide.
+ * <p>
+ * The Persian calendar is solar, and is similar to the Gregorian calendar
+ * in various ways, except its leap year rule, which is determined
+ * astronomically. The Persian year starts around the March equinox.
+ * <p>
+ * The modern Persian calendar (used in Iran since 1925 CE and in
+ * Afghanistan since 1957 CE), has the lengths of the months fixed. The
+ * first six months are 31 days each, the next five months are 30 days each,
+ * and the final month is 29 days in non-leap years and 30 days in leap
+ * ones. Historically, the lengths of the month differed in different
+ * years, but they were finally fixed at the times mentioned above. Partial
+ * information is available about the historical lengths.
+ * <p>
+ * The official rule for determination of the beginning of the Persian year
+ * is locale dependent, but at the same time, it has not specified a locale.
+ * Iranians around the world traditionally follow the calendar authorities
+ * of Iran, which haven't officially specified the locale. Some
+ * calendarists use some point in Tehran as the locale, while others have
+ * tried the more neutral 52.5 degrees east meridian. It is not clear which
+ * locale should be used for the Persian calendar of Afghanistan, but it is
+ * expected that for about one year in every twenty-four years, the Afghan
+ * calendar may become different from the Iranian one.
+ * <p>
+ * The exact locale to be used for the Iranian calendar starts to make a
+ * difference at around 2090 CE. The specific arithmetic method implemented
+ * here, commonly known as the 33-year cycle rule, matches the astronomical
+ * calendar at least for the whole period that the calendar has been both
+ * well-defined and official, from 1925 to around 2090 CE. The other
+ * commonly known algorithm, the 2820-year cycle, has been incorrectly
+ * designed to follow the tropical year instead of the spring equinoctial
+ * year, and fails to match the astronomical one as early as 2025 CE.
+ * <p>
+ * This class should not be subclassed.</p>
+ * <p>
+ * PersianCalendar usually should be instantiated using
+ * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a
+ * <code>ULocale</code> with the tag <code>"@calendar=persian"</code>.</p>
+ *
+ * @see com.ibm.icu.util.GregorianCalendar
+ * @see com.ibm.icu.util.Calendar
+ *
+ * @author Roozbeh Pournader
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+public class PersianCalendar extends Calendar {
+ private static final long serialVersionUID = -6727306982975111643L;
+
+ //-------------------------------------------------------------------------
+ // Constants...
+ //-------------------------------------------------------------------------
+
+ private static final int[][] MONTH_COUNT = {
+ //len len2 st
+ { 31, 31, 0 }, // Farvardin
+ { 31, 31, 31 }, // Ordibehesht
+ { 31, 31, 62 }, // Khordad
+ { 31, 31, 93 }, // Tir
+ { 31, 31, 124 }, // Mordad
+ { 31, 31, 155 }, // Shahrivar
+ { 30, 30, 186 }, // Mehr
+ { 30, 30, 216 }, // Aban
+ { 30, 30, 246 }, // Azar
+ { 30, 30, 276 }, // Dey
+ { 30, 30, 306 }, // Bahman
+ { 29, 30, 336 } // Esfand
+ // len length of month
+ // len2 length of month in a leap year
+ // st days in year before start of month
+ };
+
+ private static final int PERSIAN_EPOCH = 1948320;
+
+ //-------------------------------------------------------------------------
+ // Constructors...
+ //-------------------------------------------------------------------------
+
+ /**
+ * Constructs a default <code>PersianCalendar</code> using the current time
+ * in the default time zone with the default <code>FORMAT</code> locale.
+ * @see Category#FORMAT
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar()
+ {
+ this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
+ }
+
+ /**
+ * Constructs a <code>PersianCalendar</code> based on the current time
+ * in the given time zone with the default <code>FORMAT</code> locale.
+ * @param zone the given time zone.
+ * @see Category#FORMAT
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar(TimeZone zone)
+ {
+ this(zone, ULocale.getDefault(Category.FORMAT));
+ }
+
+ /**
+ * Constructs a <code>PersianCalendar</code> based on the current time
+ * in the default time zone with the given locale.
+ *
+ * @param aLocale the given locale.
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar(Locale aLocale)
+ {
+ this(TimeZone.getDefault(), aLocale);
+ }
+
+ /**
+ * Constructs a <code>PersianCalendar</code> based on the current time
+ * in the default time zone with the given locale.
+ *
+ * @param locale the given ulocale.
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar(ULocale locale)
+ {
+ this(TimeZone.getDefault(), locale);
+ }
+
+ /**
+ * Constructs a <code>PersianCalendar</code> based on the current time
+ * in the given time zone with the given locale.
+ *
+ * @param zone the given time zone.
+ * @param aLocale the given locale.
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar(TimeZone zone, Locale aLocale)
+ {
+ super(zone, aLocale);
+ setTimeInMillis(System.currentTimeMillis());
+ }
+
+ /**
+ * Constructs a <code>PersianCalendar</code> based on the current time
+ * in the given time zone with the given locale.
+ *
+ * @param zone the given time zone.
+ * @param locale the given ulocale.
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar(TimeZone zone, ULocale locale)
+ {
+ super(zone, locale);
+ setTimeInMillis(System.currentTimeMillis());
+ }
+
+ /**
+ * Constructs a <code>PersianCalendar</code> with the given date set
+ * in the default time zone with the default <code>FORMAT</code> locale.
+ *
+ * @param date The date to which the new calendar is set.
+ * @see Category#FORMAT
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar(Date date) {
+ super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
+ this.setTime(date);
+ }
+
+ /**
+ * Constructs a <code>PersianCalendar</code> with the given date set
+ * in the default time zone with the default <code>FORMAT</code> locale.
+ *
+ * @param year the value used to set the {@link #YEAR YEAR} time field in the calendar.
+ * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
+ * Note that the month value is 0-based. e.g., 0 for Farvardin.
+ * @param date the value used to set the {@link #DATE DATE} time field in the calendar.
+ * @see Category#FORMAT
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar(int year, int month, int date)
+ {
+ super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
+ this.set(Calendar.YEAR, year);
+ this.set(Calendar.MONTH, month);
+ this.set(Calendar.DATE, date);
+ }
+
+ /**
+ * Constructs a <code>PersianCalendar</code> with the given date
+ * and time set for the default time zone with the default <code>FORMAT</code> locale.
+ *
+ * @param year the value used to set the {@link #YEAR YEAR} time field in the calendar.
+ * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
+ * Note that the month value is 0-based. e.g., 0 for Farvardin.
+ * @param date the value used to set the {@link #DATE DATE} time field in the calendar.
+ * @param hour the value used to set the {@link #HOUR_OF_DAY HOUR_OF_DAY} time field
+ * in the calendar.
+ * @param minute the value used to set the {@link #MINUTE MINUTE} time field
+ * in the calendar.
+ * @param second the value used to set the {@link #SECOND SECOND} time field
+ * in the calendar.
+ * @see Category#FORMAT
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public PersianCalendar(int year, int month, int date, int hour,
+ int minute, int second)
+ {
+ super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
+ this.set(Calendar.YEAR, year);
+ this.set(Calendar.MONTH, month);
+ this.set(Calendar.DATE, date);
+ this.set(Calendar.HOUR_OF_DAY, hour);
+ this.set(Calendar.MINUTE, minute);
+ this.set(Calendar.SECOND, second);
+ }
+
+ //-------------------------------------------------------------------------
+ // Minimum / Maximum access functions
+ //-------------------------------------------------------------------------
+
+ private static final int LIMITS[][] = {
+ // Minimum Greatest Least Maximum
+ // Minimum Maximum
+ { 0, 0, 0, 0}, // ERA
+ { -5000000, -5000000, 5000000, 5000000}, // YEAR
+ { 0, 0, 11, 11}, // MONTH
+ { 1, 1, 52, 53}, // WEEK_OF_YEAR
+ {/* */}, // WEEK_OF_MONTH
+ { 1, 1, 29, 31}, // DAY_OF_MONTH
+ { 1, 1, 365, 366}, // DAY_OF_YEAR
+ {/* */}, // DAY_OF_WEEK
+ { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
+ {/* */}, // AM_PM
+ {/* */}, // HOUR
+ {/* */}, // HOUR_OF_DAY
+ {/* */}, // MINUTE
+ {/* */}, // SECOND
+ {/* */}, // MILLISECOND
+ {/* */}, // ZONE_OFFSET
+ {/* */}, // DST_OFFSET
+ { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY
+ {/* */}, // DOW_LOCAL
+ { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
+ {/* */}, // JULIAN_DAY
+ {/* */}, // MILLISECONDS_IN_DAY
+ };
+
+ /**
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected int handleGetLimit(int field, int limitType) {
+ return LIMITS[field][limitType];
+ }
+
+ //-------------------------------------------------------------------------
+ // Assorted calculation utilities
+ //
+
+ /**
+ * Determine whether a year is a leap year in the Persian calendar
+ */
+ private final static boolean isLeapYear(int year)
+ {
+ int[] remainder = new int[1];
+ floorDivide(25 * year + 11, 33, remainder);
+ return remainder[0] < 8;
+
+ }
+
+ //----------------------------------------------------------------------
+ // Calendar framework
+ //----------------------------------------------------------------------
+
+ /**
+ * Return the length (in days) of the given month.
+ *
+ * @param extendedYear The Persian year
+ * @param month The Persian month, 0-based
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected int handleGetMonthLength(int extendedYear, int month) {
+ // If the month is out of range, adjust it into range, and
+ // modify the extended year value accordingly.
+ if (month < 0 || month > 11) {
+ int[] rem = new int[1];
+ extendedYear += floorDivide(month, 12, rem);
+ month = rem[0];
+ }
+
+ return MONTH_COUNT[month][isLeapYear(extendedYear)?1:0];
+ }
+
+ /**
+ * Return the number of days in the given Persian year
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected int handleGetYearLength(int extendedYear) {
+ return isLeapYear(extendedYear) ? 366 : 365;
+ }
+
+ //-------------------------------------------------------------------------
+ // Functions for converting from field values to milliseconds....
+ //-------------------------------------------------------------------------
+
+ /**
+ * Return JD of start of given month/year
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected int handleComputeMonthStart(int eyear, int month, boolean useMonth) {
+ // If the month is out of range, adjust it into range, and
+ // modify the extended year value accordingly.
+ if (month < 0 || month > 11) {
+ int[] rem = new int[1];
+ eyear += floorDivide(month, 12, rem);
+ month = rem[0];
+ }
+
+ int julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + floorDivide(8 * eyear + 21, 33);
+ if (month != 0) {
+ julianDay += MONTH_COUNT[month][2];
+ }
+ return julianDay;
+ }
+
+ //-------------------------------------------------------------------------
+ // Functions for converting from milliseconds to field values
+ //-------------------------------------------------------------------------
+
+ /**
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected int handleGetExtendedYear() {
+ int year;
+ if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR) {
+ year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
+ } else {
+ year = internalGet(YEAR, 1); // Default to year 1
+ }
+ return year;
+ }
+
+ /**
+ * Override Calendar to compute several fields specific to the Persian
+ * calendar system. These are:
+ *
+ * <ul><li>ERA
+ * <li>YEAR
+ * <li>MONTH
+ * <li>DAY_OF_MONTH
+ * <li>DAY_OF_YEAR
+ * <li>EXTENDED_YEAR</ul>
+ *
+ * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
+ * method is called.
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ protected void handleComputeFields(int julianDay) {
+ int year, month, dayOfMonth, dayOfYear;
+
+ long daysSinceEpoch = julianDay - PERSIAN_EPOCH;
+ year = 1 + (int) floorDivide(33 * daysSinceEpoch + 3, 12053);
+
+ long farvardin1 = 365 * (year - 1) + floorDivide(8 * year + 21, 33);
+ dayOfYear = (int)(daysSinceEpoch - farvardin1); // 0-based
+ if (dayOfYear < 216) { // Compute 0-based month
+ month = dayOfYear / 31;
+ } else {
+ month = (dayOfYear - 6) / 30;
+ }
+ dayOfMonth = dayOfYear - MONTH_COUNT[month][2] + 1;
+ ++dayOfYear; // Make it 1-based now
+
+ internalSet(ERA, 0);
+ internalSet(YEAR, year);
+ internalSet(EXTENDED_YEAR, year);
+ internalSet(MONTH, month);
+ internalSet(DAY_OF_MONTH, dayOfMonth);
+ internalSet(DAY_OF_YEAR, dayOfYear);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ public String getType() {
+ return "persian";
+ }
+}