1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/persncal.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,293 @@ 1.4 +/* 1.5 + ****************************************************************************** 1.6 + * Copyright (C) 2003-2013, International Business Machines Corporation 1.7 + * and others. All Rights Reserved. 1.8 + ****************************************************************************** 1.9 + * 1.10 + * File PERSNCAL.CPP 1.11 + * 1.12 + * Modification History: 1.13 + * 1.14 + * Date Name Description 1.15 + * 9/23/2003 mehran posted to icu-design 1.16 + * 10/1/2012 roozbeh Fixed algorithm and heavily refactored and rewrote 1.17 + * based on the implementation of Gregorian 1.18 + ***************************************************************************** 1.19 + */ 1.20 + 1.21 +#include "persncal.h" 1.22 + 1.23 +#if !UCONFIG_NO_FORMATTING 1.24 + 1.25 +#include "umutex.h" 1.26 +#include "gregoimp.h" // Math 1.27 +#include <float.h> 1.28 + 1.29 +static const int16_t kPersianNumDays[] 1.30 += {0,31,62,93,124,155,186,216,246,276,306,336}; // 0-based, for day-in-year 1.31 +static const int8_t kPersianMonthLength[] 1.32 += {31,31,31,31,31,31,30,30,30,30,30,29}; // 0-based 1.33 +static const int8_t kPersianLeapMonthLength[] 1.34 += {31,31,31,31,31,31,30,30,30,30,30,30}; // 0-based 1.35 + 1.36 +static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = { 1.37 + // Minimum Greatest Least Maximum 1.38 + // Minimum Maximum 1.39 + { 0, 0, 0, 0}, // ERA 1.40 + { -5000000, -5000000, 5000000, 5000000}, // YEAR 1.41 + { 0, 0, 11, 11}, // MONTH 1.42 + { 1, 1, 52, 53}, // WEEK_OF_YEAR 1.43 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 1.44 + { 1, 1, 29, 31}, // DAY_OF_MONTH 1.45 + { 1, 1, 365, 366}, // DAY_OF_YEAR 1.46 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 1.47 + { 1, 1, 5, 5}, // DAY_OF_WEEK_IN_MONTH 1.48 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 1.49 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 1.50 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 1.51 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 1.52 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 1.53 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 1.54 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 1.55 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 1.56 + { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY 1.57 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 1.58 + { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR 1.59 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 1.60 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 1.61 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH 1.62 +}; 1.63 + 1.64 +U_NAMESPACE_BEGIN 1.65 + 1.66 +static const int32_t PERSIAN_EPOCH = 1948320; 1.67 + 1.68 +// Implementation of the PersianCalendar class 1.69 + 1.70 +//------------------------------------------------------------------------- 1.71 +// Constructors... 1.72 +//------------------------------------------------------------------------- 1.73 + 1.74 +const char *PersianCalendar::getType() const { 1.75 + return "persian"; 1.76 +} 1.77 + 1.78 +Calendar* PersianCalendar::clone() const { 1.79 + return new PersianCalendar(*this); 1.80 +} 1.81 + 1.82 +PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success) 1.83 + : Calendar(TimeZone::createDefault(), aLocale, success) 1.84 +{ 1.85 + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. 1.86 +} 1.87 + 1.88 +PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) { 1.89 +} 1.90 + 1.91 +PersianCalendar::~PersianCalendar() 1.92 +{ 1.93 +} 1.94 + 1.95 +//------------------------------------------------------------------------- 1.96 +// Minimum / Maximum access functions 1.97 +//------------------------------------------------------------------------- 1.98 + 1.99 + 1.100 +int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { 1.101 + return kPersianCalendarLimits[field][limitType]; 1.102 +} 1.103 + 1.104 +//------------------------------------------------------------------------- 1.105 +// Assorted calculation utilities 1.106 +// 1.107 + 1.108 +/** 1.109 + * Determine whether a year is a leap year in the Persian calendar 1.110 + */ 1.111 +UBool PersianCalendar::isLeapYear(int32_t year) 1.112 +{ 1.113 + int32_t remainder; 1.114 + ClockMath::floorDivide(25 * year + 11, 33, remainder); 1.115 + return (remainder < 8); 1.116 +} 1.117 + 1.118 +/** 1.119 + * Return the day # on which the given year starts. Days are counted 1.120 + * from the Persian epoch, origin 0. 1.121 + */ 1.122 +int32_t PersianCalendar::yearStart(int32_t year) { 1.123 + return handleComputeMonthStart(year,0,FALSE); 1.124 +} 1.125 + 1.126 +/** 1.127 + * Return the day # on which the given month starts. Days are counted 1.128 + * from the Persian epoch, origin 0. 1.129 + * 1.130 + * @param year The Persian year 1.131 + * @param year The Persian month, 0-based 1.132 + */ 1.133 +int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const { 1.134 + return handleComputeMonthStart(year,month,TRUE); 1.135 +} 1.136 + 1.137 +//---------------------------------------------------------------------- 1.138 +// Calendar framework 1.139 +//---------------------------------------------------------------------- 1.140 + 1.141 +/** 1.142 + * Return the length (in days) of the given month. 1.143 + * 1.144 + * @param year The Persian year 1.145 + * @param year The Persian month, 0-based 1.146 + */ 1.147 +int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { 1.148 + // If the month is out of range, adjust it into range, and 1.149 + // modify the extended year value accordingly. 1.150 + if (month < 0 || month > 11) { 1.151 + extendedYear += ClockMath::floorDivide(month, 12, month); 1.152 + } 1.153 + 1.154 + return isLeapYear(extendedYear) ? kPersianLeapMonthLength[month] : kPersianMonthLength[month]; 1.155 +} 1.156 + 1.157 +/** 1.158 + * Return the number of days in the given Persian year 1.159 + */ 1.160 +int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const { 1.161 + return isLeapYear(extendedYear) ? 366 : 365; 1.162 +} 1.163 + 1.164 +//------------------------------------------------------------------------- 1.165 +// Functions for converting from field values to milliseconds.... 1.166 +//------------------------------------------------------------------------- 1.167 + 1.168 +// Return JD of start of given month/year 1.169 +int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const { 1.170 + // If the month is out of range, adjust it into range, and 1.171 + // modify the extended year value accordingly. 1.172 + if (month < 0 || month > 11) { 1.173 + eyear += ClockMath::floorDivide(month, 12, month); 1.174 + } 1.175 + 1.176 + int32_t julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + ClockMath::floorDivide(8 * eyear + 21, 33); 1.177 + 1.178 + if (month != 0) { 1.179 + julianDay += kPersianNumDays[month]; 1.180 + } 1.181 + 1.182 + return julianDay; 1.183 +} 1.184 + 1.185 +//------------------------------------------------------------------------- 1.186 +// Functions for converting from milliseconds to field values 1.187 +//------------------------------------------------------------------------- 1.188 + 1.189 +int32_t PersianCalendar::handleGetExtendedYear() { 1.190 + int32_t year; 1.191 + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { 1.192 + year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 1.193 + } else { 1.194 + year = internalGet(UCAL_YEAR, 1); // Default to year 1 1.195 + } 1.196 + return year; 1.197 +} 1.198 + 1.199 +/** 1.200 + * Override Calendar to compute several fields specific to the Persian 1.201 + * calendar system. These are: 1.202 + * 1.203 + * <ul><li>ERA 1.204 + * <li>YEAR 1.205 + * <li>MONTH 1.206 + * <li>DAY_OF_MONTH 1.207 + * <li>DAY_OF_YEAR 1.208 + * <li>EXTENDED_YEAR</ul> 1.209 + * 1.210 + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this 1.211 + * method is called. 1.212 + */ 1.213 +void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { 1.214 + int32_t year, month, dayOfMonth, dayOfYear; 1.215 + 1.216 + int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH; 1.217 + year = 1 + ClockMath::floorDivide(33 * daysSinceEpoch + 3, 12053); 1.218 + 1.219 + int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33); 1.220 + dayOfYear = (daysSinceEpoch - farvardin1); // 0-based 1.221 + if (dayOfYear < 216) { // Compute 0-based month 1.222 + month = dayOfYear / 31; 1.223 + } else { 1.224 + month = (dayOfYear - 6) / 30; 1.225 + } 1.226 + dayOfMonth = dayOfYear - kPersianNumDays[month] + 1; 1.227 + ++dayOfYear; // Make it 1-based now 1.228 + 1.229 + internalSet(UCAL_ERA, 0); 1.230 + internalSet(UCAL_YEAR, year); 1.231 + internalSet(UCAL_EXTENDED_YEAR, year); 1.232 + internalSet(UCAL_MONTH, month); 1.233 + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); 1.234 + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); 1.235 +} 1.236 + 1.237 +UBool 1.238 +PersianCalendar::inDaylightTime(UErrorCode& status) const 1.239 +{ 1.240 + // copied from GregorianCalendar 1.241 + if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) 1.242 + return FALSE; 1.243 + 1.244 + // Force an update of the state of the Calendar. 1.245 + ((PersianCalendar*)this)->complete(status); // cast away const 1.246 + 1.247 + return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE); 1.248 +} 1.249 + 1.250 +// default century 1.251 + 1.252 +static UDate gSystemDefaultCenturyStart = DBL_MIN; 1.253 +static int32_t gSystemDefaultCenturyStartYear = -1; 1.254 +static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER; 1.255 + 1.256 +UBool PersianCalendar::haveDefaultCentury() const 1.257 +{ 1.258 + return TRUE; 1.259 +} 1.260 + 1.261 +static void U_CALLCONV initializeSystemDefaultCentury() { 1.262 + // initialize systemDefaultCentury and systemDefaultCenturyYear based 1.263 + // on the current time. They'll be set to 80 years before 1.264 + // the current time. 1.265 + UErrorCode status = U_ZERO_ERROR; 1.266 + PersianCalendar calendar(Locale("@calendar=persian"),status); 1.267 + if (U_SUCCESS(status)) 1.268 + { 1.269 + calendar.setTime(Calendar::getNow(), status); 1.270 + calendar.add(UCAL_YEAR, -80, status); 1.271 + 1.272 + gSystemDefaultCenturyStart = calendar.getTime(status); 1.273 + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); 1.274 + } 1.275 + // We have no recourse upon failure unless we want to propagate the failure 1.276 + // out. 1.277 +} 1.278 + 1.279 +UDate PersianCalendar::defaultCenturyStart() const { 1.280 + // lazy-evaluate systemDefaultCenturyStart 1.281 + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 1.282 + return gSystemDefaultCenturyStart; 1.283 +} 1.284 + 1.285 +int32_t PersianCalendar::defaultCenturyStartYear() const { 1.286 + // lazy-evaluate systemDefaultCenturyStartYear 1.287 + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 1.288 + return gSystemDefaultCenturyStartYear; 1.289 +} 1.290 + 1.291 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar) 1.292 + 1.293 +U_NAMESPACE_END 1.294 + 1.295 +#endif 1.296 +