intl/icu/source/i18n/persncal.cpp

changeset 0
6474c204b198
     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 +

mercurial