ICU-8965 Add Persian Calendar support
X-SVN-Rev: 32500
This commit is contained in:
parent
a3a4207b24
commit
8158ae5725
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -248,6 +248,7 @@ icu4j/main/classes/core/.project -text
|
||||
icu4j/main/classes/core/.settings/org.eclipse.core.resources.prefs -text
|
||||
icu4j/main/classes/core/.settings/org.eclipse.jdt.core.prefs -text
|
||||
icu4j/main/classes/core/manifest.stub -text
|
||||
icu4j/main/classes/core/src/com/ibm/icu/util/PersianCalendar.java -text
|
||||
icu4j/main/classes/currdata/.externalToolBuilders/copy-data-currdata.launch -text
|
||||
icu4j/main/classes/currdata/.settings/org.eclipse.core.resources.prefs -text
|
||||
icu4j/main/classes/currdata/.settings/org.eclipse.jdt.core.prefs -text
|
||||
|
@ -1747,7 +1747,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
private static final int CALTYPE_JAPANESE = 1;
|
||||
private static final int CALTYPE_BUDDHIST = 2;
|
||||
private static final int CALTYPE_ROC = 3;
|
||||
private static final int CALTYPE_PERSIAN = 4; // not yet implemented
|
||||
private static final int CALTYPE_PERSIAN = 4;
|
||||
private static final int CALTYPE_ISLAMIC_CIVIL = 5;
|
||||
private static final int CALTYPE_ISLAMIC = 6;
|
||||
private static final int CALTYPE_HEBREW = 7;
|
||||
@ -1874,8 +1874,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
cal = new TaiwanCalendar(zone, locale);
|
||||
break;
|
||||
case CALTYPE_PERSIAN:
|
||||
// Not yet implemented in ICU4J
|
||||
cal = new GregorianCalendar(zone, locale);
|
||||
cal = new PersianCalendar(zone, locale);
|
||||
break;
|
||||
case CALTYPE_ISLAMIC_CIVIL:
|
||||
cal = new IslamicCalendar(zone, locale);
|
||||
|
@ -0,0 +1,434 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* 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";
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import com.ibm.icu.util.HebrewCalendar;
|
||||
import com.ibm.icu.util.IndianCalendar;
|
||||
import com.ibm.icu.util.IslamicCalendar;
|
||||
import com.ibm.icu.util.JapaneseCalendar;
|
||||
import com.ibm.icu.util.PersianCalendar;
|
||||
import com.ibm.icu.util.TaiwanCalendar;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
@ -223,6 +224,22 @@ public class CalendarTests
|
||||
}
|
||||
}
|
||||
|
||||
static class PersianCalendarHandler extends CalendarHandler
|
||||
{
|
||||
public Object[] getTestObjects()
|
||||
{
|
||||
Locale locales[] = SerializableTest.getLocales();
|
||||
TimeZone kst = TimeZone.getTimeZone("Asia/Tehran");
|
||||
PersianCalendar calendars[] = new PersianCalendar[locales.length];
|
||||
|
||||
for (int i = 0; i < locales.length; i += 1) {
|
||||
calendars[i] = new PersianCalendar(kst, ULocale.forLocale(locales[i]));
|
||||
}
|
||||
|
||||
return calendars;
|
||||
}
|
||||
}
|
||||
|
||||
static class TaiwanCalendarHandler extends CalendarHandler {
|
||||
public Object[] getTestObjects() {
|
||||
Locale locales[] = SerializableTest.getLocales();
|
||||
|
@ -689,6 +689,7 @@ public class SerializableTest extends TestFmwk.TestGroup
|
||||
map.put("com.ibm.icu.util.IndianCalendar", new CalendarTests.IndianCalendarHandler());
|
||||
map.put("com.ibm.icu.util.IslamicCalendar", new CalendarTests.IslamicCalendarHandler());
|
||||
map.put("com.ibm.icu.util.JapaneseCalendar", new CalendarTests.JapaneseCalendarHandler());
|
||||
map.put("com.ibm.icu.util.PersianCalendar", new CalendarTests.PersianCalendarHandler());
|
||||
map.put("com.ibm.icu.util.TaiwanCalendar", new CalendarTests.TaiwanCalendarHandler());
|
||||
|
||||
map.put("com.ibm.icu.text.ArabicShapingException", new ExceptionTests.ArabicShapingExceptionHandler());
|
||||
|
Loading…
Reference in New Issue
Block a user