indiancal.cpp (9683B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 * Copyright (C) 2003-2014, International Business Machines Corporation 5 * and others. All Rights Reserved. 6 ****************************************************************************** 7 * 8 * File INDIANCAL.CPP 9 ***************************************************************************** 10 */ 11 12 #include "indiancal.h" 13 #include <stdlib.h> 14 #if !UCONFIG_NO_FORMATTING 15 16 #include "mutex.h" 17 #include <float.h> 18 #include "gregoimp.h" // Math 19 #include "uhash.h" 20 21 // Debugging 22 #ifdef U_DEBUG_INDIANCAL 23 #include <stdio.h> 24 #include <stdarg.h> 25 26 #endif 27 28 U_NAMESPACE_BEGIN 29 30 // Implementation of the IndianCalendar class 31 32 //------------------------------------------------------------------------- 33 // Constructors... 34 //------------------------------------------------------------------------- 35 36 37 IndianCalendar* IndianCalendar::clone() const { 38 return new IndianCalendar(*this); 39 } 40 41 IndianCalendar::IndianCalendar(const Locale& aLocale, UErrorCode& success) 42 : Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) 43 { 44 } 45 46 IndianCalendar::IndianCalendar(const IndianCalendar& other) : Calendar(other) { 47 } 48 49 IndianCalendar::~IndianCalendar() 50 { 51 } 52 const char *IndianCalendar::getType() const { 53 return "indian"; 54 } 55 56 static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { 57 // Minimum Greatest Least Maximum 58 // Minimum Maximum 59 { 0, 0, 0, 0}, // ERA 60 { -5000000, -5000000, 5000000, 5000000}, // YEAR 61 { 0, 0, 11, 11}, // MONTH 62 { 1, 1, 52, 53}, // WEEK_OF_YEAR 63 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 64 { 1, 1, 30, 31}, // DAY_OF_MONTH 65 { 1, 1, 365, 366}, // DAY_OF_YEAR 66 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 67 { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH 68 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 69 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 70 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 71 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 72 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 73 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 74 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 75 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 76 { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY 77 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 78 { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR 79 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 80 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 81 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH 82 { 0, 0, 11, 11}, // ORDINAL_MONTH 83 }; 84 85 static const int32_t INDIAN_ERA_START = 78; 86 static const int32_t INDIAN_YEAR_START = 80; 87 88 int32_t IndianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { 89 return LIMITS[field][limitType]; 90 } 91 92 /* 93 * Determine whether the given gregorian year is a Leap year 94 */ 95 static UBool isGregorianLeap(int32_t year) 96 { 97 return Grego::isLeapYear(year); 98 } 99 100 //---------------------------------------------------------------------- 101 // Calendar framework 102 //---------------------------------------------------------------------- 103 104 /* 105 * Return the length (in days) of the given month. 106 * 107 * @param eyear The year in Saka Era 108 * @param month The month(0-based) in Indian calendar 109 */ 110 int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month, UErrorCode& /* status */) const { 111 if (month < 0 || month > 11) { 112 eyear += ClockMath::floorDivide(month, 12, &month); 113 } 114 115 if (isGregorianLeap(eyear + INDIAN_ERA_START) && month == 0) { 116 return 31; 117 } 118 119 if (month >= 1 && month <= 5) { 120 return 31; 121 } 122 123 return 30; 124 } 125 126 /* 127 * Return the number of days in the given Indian year 128 * 129 * @param eyear The year in Saka Era. 130 */ 131 int32_t IndianCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const { 132 if (U_FAILURE(status)) return 0; 133 return isGregorianLeap(eyear + INDIAN_ERA_START) ? 366 : 365; 134 } 135 /* 136 * Returns the Julian Day corresponding to gregorian date 137 * 138 * @param year The Gregorian year 139 * @param month The month in Gregorian Year, 0 based. 140 * @param date The date in Gregorian day in month 141 */ 142 static double gregorianToJD(int32_t year, int32_t month, int32_t date) { 143 return Grego::fieldsToDay(year, month, date) + kEpochStartAsJulianDay - 0.5; 144 } 145 146 147 //------------------------------------------------------------------------- 148 // Functions for converting from field values to milliseconds.... 149 //------------------------------------------------------------------------- 150 static double IndianToJD(int32_t year, int32_t month, int32_t date) { 151 int32_t leapMonth, gyear, m; 152 double start, jd; 153 154 gyear = year + INDIAN_ERA_START; 155 156 157 if(isGregorianLeap(gyear)) { 158 leapMonth = 31; 159 start = gregorianToJD(gyear, 2 /* The third month in 0 based month */, 21); 160 } 161 else { 162 leapMonth = 30; 163 start = gregorianToJD(gyear, 2 /* The third month in 0 based month */, 22); 164 } 165 166 if (month == 1) { 167 jd = start + (date - 1); 168 } else { 169 jd = start + leapMonth; 170 m = month - 2; 171 172 //m = Math.min(m, 5); 173 if (m > 5) { 174 m = 5; 175 } 176 177 jd += m * 31; 178 179 if (month >= 8) { 180 m = month - 7; 181 jd += m * 30; 182 } 183 jd += date - 1; 184 } 185 186 return jd; 187 } 188 189 /* 190 * Return JD of start of given month/year of Indian Calendar 191 * @param eyear The year in Indian Calendar measured from Saka Era (78 AD). 192 * @param month The month in Indian calendar 193 */ 194 int64_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */, UErrorCode& status) const { 195 if (U_FAILURE(status)) { 196 return 0; 197 } 198 199 //month is 0 based; converting it to 1-based 200 int32_t imonth; 201 202 // If the month is out of range, adjust it into range, and adjust the extended year accordingly 203 if (month < 0 || month > 11) { 204 if (uprv_add32_overflow(eyear, ClockMath::floorDivide(month, 12, &month), &eyear)) { 205 status = U_ILLEGAL_ARGUMENT_ERROR; 206 return 0; 207 } 208 } 209 210 if(month == 12){ 211 imonth = 1; 212 } else { 213 imonth = month + 1; 214 } 215 216 int64_t jd = IndianToJD(eyear ,imonth, 1); 217 218 return jd; 219 } 220 221 //------------------------------------------------------------------------- 222 // Functions for converting from milliseconds to field values 223 //------------------------------------------------------------------------- 224 225 int32_t IndianCalendar::handleGetExtendedYear(UErrorCode& status) { 226 if (U_FAILURE(status)) { 227 return 0; 228 } 229 int32_t year; 230 231 if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { 232 year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 233 } else { 234 year = internalGet(UCAL_YEAR, 1); // Default to year 1 235 } 236 237 return year; 238 } 239 240 /* 241 * Override Calendar to compute several fields specific to the Indian 242 * calendar system. These are: 243 * 244 * <ul><li>ERA 245 * <li>YEAR 246 * <li>MONTH 247 * <li>DAY_OF_MONTH 248 * <li>EXTENDED_YEAR</ul> 249 * 250 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this 251 * method is called. The getGregorianXxx() methods return Gregorian 252 * calendar equivalents for the given Julian day. 253 */ 254 void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& /* status */) { 255 double jdAtStartOfGregYear; 256 int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday; 257 // Stores gregorian date corresponding to Julian day; 258 int32_t gregorianYear = getGregorianYear(); 259 260 IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era 261 jdAtStartOfGregYear = gregorianToJD(gregorianYear, 0, 1); // JD at start of Gregorian year 262 yday = static_cast<int32_t>(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0) 263 264 if (yday < INDIAN_YEAR_START) { 265 // Day is at the end of the preceding Saka year 266 IndianYear -= 1; 267 leapMonth = isGregorianLeap(gregorianYear - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year 268 yday += leapMonth + (31 * 5) + (30 * 3) + 10; 269 } else { 270 leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMonth this year 271 yday -= INDIAN_YEAR_START; 272 } 273 274 if (yday < leapMonth) { 275 IndianMonth = 0; 276 IndianDayOfMonth = yday + 1; 277 } else { 278 mday = yday - leapMonth; 279 if (mday < (31 * 5)) { 280 IndianMonth = static_cast<int32_t>(uprv_floor(mday / 31)) + 1; 281 IndianDayOfMonth = (mday % 31) + 1; 282 } else { 283 mday -= 31 * 5; 284 IndianMonth = static_cast<int32_t>(uprv_floor(mday / 30)) + 6; 285 IndianDayOfMonth = (mday % 30) + 1; 286 } 287 } 288 289 internalSet(UCAL_ERA, 0); 290 internalSet(UCAL_EXTENDED_YEAR, IndianYear); 291 internalSet(UCAL_YEAR, IndianYear); 292 internalSet(UCAL_MONTH, IndianMonth); 293 internalSet(UCAL_ORDINAL_MONTH, IndianMonth); 294 internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth); 295 internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based 296 } 297 298 IMPL_SYSTEM_DEFAULT_CENTURY(IndianCalendar, "@calendar=indian") 299 300 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar) 301 302 int32_t IndianCalendar::getRelatedYearDifference() const { 303 constexpr int32_t kIndianCalendarRelatedYearDifference = 79; 304 return kIndianCalendarRelatedYearDifference; 305 } 306 307 U_NAMESPACE_END 308 309 #endif