gregoimp.h (15351B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ********************************************************************** 5 * Copyright (c) 2003-2008, International Business Machines 6 * Corporation and others. All Rights Reserved. 7 ********************************************************************** 8 * Author: Alan Liu 9 * Created: September 2 2003 10 * Since: ICU 2.8 11 ********************************************************************** 12 */ 13 14 #ifndef GREGOIMP_H 15 #define GREGOIMP_H 16 #include "unicode/utypes.h" 17 #include "unicode/calendar.h" 18 #if !UCONFIG_NO_FORMATTING 19 20 #include "unicode/ures.h" 21 #include "unicode/locid.h" 22 #include "putilimp.h" 23 24 U_NAMESPACE_BEGIN 25 26 /** 27 * A utility class providing mathematical functions used by time zone 28 * and calendar code. Do not instantiate. Formerly just named 'Math'. 29 * @internal 30 */ 31 class ClockMath { 32 public: 33 /** 34 * Divide two integers, returning the floor of the quotient. 35 * Unlike the built-in division, this is mathematically 36 * well-behaved. E.g., <code>-1/4</code> => 0 but 37 * <code>floorDivide(-1,4)</code> => -1. 38 * @param numerator the numerator 39 * @param denominator a divisor which must be != 0 40 * @return the floor of the quotient 41 */ 42 static int32_t floorDivide(int32_t numerator, int32_t denominator); 43 44 /** 45 * Divide two integers, returning the floor of the quotient. 46 * Unlike the built-in division, this is mathematically 47 * well-behaved. E.g., <code>-1/4</code> => 0 but 48 * <code>floorDivide(-1,4)</code> => -1. 49 * @param numerator the numerator 50 * @param denominator a divisor which must be != 0 51 * @return the floor of the quotient 52 */ 53 static int64_t floorDivideInt64(int64_t numerator, int64_t denominator); 54 55 /** 56 * Divide two numbers, returning the floor of the quotient. 57 * Unlike the built-in division, this is mathematically 58 * well-behaved. E.g., <code>-1/4</code> => 0 but 59 * <code>floorDivide(-1,4)</code> => -1. 60 * @param numerator the numerator 61 * @param denominator a divisor which must be != 0 62 * @return the floor of the quotient 63 */ 64 static inline double floorDivide(double numerator, double denominator); 65 66 /** 67 * Divide two numbers, returning the floor of the quotient and 68 * the modulus remainder. Unlike the built-in division, this is 69 * mathematically well-behaved. E.g., <code>-1/4</code> => 0 and 70 * <code>-1%4</code> => -1, but <code>floorDivide(-1,4)</code> => 71 * -1 with <code>remainder</code> => 3. NOTE: If numerator is 72 * too large, the returned quotient may overflow. 73 * @param numerator the numerator 74 * @param denominator a divisor which must be != 0 75 * @param remainder output parameter to receive the 76 * remainder. Unlike <code>numerator % denominator</code>, this 77 * will always be non-negative, in the half-open range <code>[0, 78 * |denominator|)</code>. 79 * @return the floor of the quotient 80 */ 81 static int32_t floorDivide(int32_t numerator, int32_t denominator, 82 int32_t* remainder); 83 84 /** 85 * Divide two numbers, returning the floor of the quotient and 86 * the modulus remainder. Unlike the built-in division, this is 87 * mathematically well-behaved. E.g., <code>-1/4</code> => 0 and 88 * <code>-1%4</code> => -1, but <code>floorDivide(-1,4)</code> => 89 * -1 with <code>remainder</code> => 3. NOTE: If numerator is 90 * too large, the returned quotient may overflow. 91 * @param numerator the numerator 92 * @param denominator a divisor which must be != 0 93 * @param remainder output parameter to receive the 94 * remainder. Unlike <code>numerator % denominator</code>, this 95 * will always be non-negative, in the half-open range <code>[0, 96 * |denominator|)</code>. 97 * @return the floor of the quotient 98 */ 99 static double floorDivide(double numerator, int32_t denominator, 100 int32_t* remainder); 101 102 /** 103 * For a positive divisor, return the quotient and remainder 104 * such that dividend = quotient*divisor + remainder and 105 * 0 <= remainder < divisor. 106 * 107 * Works around edge-case bugs. Handles pathological input 108 * (dividend >> divisor) reasonably. 109 * 110 * Calling with a divisor <= 0 is disallowed. 111 */ 112 static double floorDivide(double dividend, double divisor, 113 double* remainder); 114 }; 115 116 // Useful millisecond constants 117 #define kOneDay (1.0 * U_MILLIS_PER_DAY) // 86,400,000 118 #define kOneHour (60*60*1000) 119 #define kOneMinute 60000 120 #define kOneSecond 1000 121 #define kOneMillisecond 1 122 #define kOneWeek (7.0 * kOneDay) // 604,800,000 123 124 // Epoch constants 125 #define kJan1_1JulianDay 1721426 // January 1, year 1 (Gregorian) 126 127 #define kEpochStartAsJulianDay 2440588 // January 1, 1970 (Gregorian) 128 129 #define kEpochYear 1970 130 131 132 #define kEarliestViableMillis -185331720384000000.0 // minimum representable by julian day -1e17 133 134 #define kLatestViableMillis 185753453990400000.0 // max representable by julian day +1e17 135 136 /** 137 * The minimum supported Julian day. This value is equivalent to 138 * MIN_MILLIS. 139 */ 140 #define MIN_JULIAN (-0x7F000000) 141 142 /** 143 * The minimum supported epoch milliseconds. This value is equivalent 144 * to MIN_JULIAN. 145 */ 146 #define MIN_MILLIS ((MIN_JULIAN - kEpochStartAsJulianDay) * kOneDay) 147 148 /** 149 * The maximum supported Julian day. This value is equivalent to 150 * MAX_MILLIS. 151 */ 152 #define MAX_JULIAN (+0x7F000000) 153 154 /** 155 * The maximum supported epoch milliseconds. This value is equivalent 156 * to MAX_JULIAN. 157 */ 158 #define MAX_MILLIS ((MAX_JULIAN - kEpochStartAsJulianDay) * kOneDay) 159 160 /** 161 * A utility class providing proleptic Gregorian calendar functions 162 * used by time zone and calendar code. Do not instantiate. 163 * 164 * Note: Unlike GregorianCalendar, all computations performed by this 165 * class occur in the pure proleptic GregorianCalendar. 166 */ 167 class Grego { 168 public: 169 /** 170 * Return true if the given year is a leap year. 171 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. 172 * @return true if the year is a leap year 173 */ 174 static inline UBool isLeapYear(int32_t year); 175 176 /** 177 * Return the number of days in the given month. 178 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. 179 * @param month 0-based month, with 0==Jan 180 * @return the number of days in the given month 181 */ 182 static inline int8_t monthLength(int32_t year, int32_t month); 183 184 /** 185 * Return the length of a previous month of the Gregorian calendar. 186 * @param y the extended year 187 * @param m the 0-based month number 188 * @return the number of days in the month previous to the given month 189 */ 190 static inline int8_t previousMonthLength(int y, int m); 191 192 /** 193 * Convert a year, month, and day-of-month, given in the proleptic 194 * Gregorian calendar, to 1970 epoch days. 195 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. 196 * @param month 0-based month, with 0==Jan 197 * @param dom 1-based day of month 198 * @return the day number, with day 0 == Jan 1 1970 199 */ 200 static int64_t fieldsToDay(int32_t year, int32_t month, int32_t dom); 201 202 /** 203 * Convert a 1970-epoch day number to proleptic Gregorian year, 204 * month, day-of-month, and day-of-week. 205 * @param day 1970-epoch day 206 * @param year output parameter to receive year 207 * @param month output parameter to receive month (0-based, 0==Jan) 208 * @param dom output parameter to receive day-of-month (1-based) 209 * @param dow output parameter to receive day-of-week (1-based, 1==Sun) 210 * @param doy output parameter to receive day-of-year (1-based) 211 * @param status error code. 212 */ 213 static void dayToFields(int32_t day, int32_t& year, int8_t& month, 214 int8_t& dom, int8_t& dow, int16_t& doy, UErrorCode& status); 215 216 /** 217 * Convert a 1970-epoch day number to proleptic Gregorian year. 218 * @param day 1970-epoch day 219 * @param status error code. 220 * @return year. 221 */ 222 static int32_t dayToYear(int32_t day, UErrorCode& status); 223 /** 224 * Convert a 1970-epoch day number to proleptic Gregorian year. 225 * @param day 1970-epoch day 226 * @param doy output parameter to receive day-of-year (1-based) 227 * @param status error code. 228 * @return year. 229 */ 230 static int32_t dayToYear(int32_t day, int16_t& doy, UErrorCode& status); 231 232 /** 233 * Convert a 1970-epoch milliseconds to proleptic Gregorian year, 234 * month, day-of-month, and day-of-week, day of year and millis-in-day. 235 * @param time 1970-epoch milliseconds 236 * @param year output parameter to receive year 237 * @param month output parameter to receive month (0-based, 0==Jan) 238 * @param dom output parameter to receive day-of-month (1-based) 239 * @param dow output parameter to receive day-of-week (1-based, 1==Sun) 240 * @param doy output parameter to receive day-of-year (1-based) 241 * @param mid output parameter to receive millis-in-day 242 * @param status error code. 243 */ 244 static void timeToFields(UDate time, int32_t& year, int8_t& month, 245 int8_t& dom, int8_t& dow, int16_t& doy, int32_t& mid, UErrorCode& status); 246 247 /** 248 * Convert a 1970-epoch milliseconds to proleptic Gregorian year, 249 * month, day-of-month, and day-of-week, day of year and millis-in-day. 250 * @param time 1970-epoch milliseconds 251 * @param year output parameter to receive year 252 * @param month output parameter to receive month (0-based, 0==Jan) 253 * @param dom output parameter to receive day-of-month (1-based) 254 * @param dow output parameter to receive day-of-week (1-based, 1==Sun) 255 * @param mid output parameter to receive millis-in-day 256 * @param status error code. 257 */ 258 static void timeToFields(UDate time, int32_t& year, int8_t& month, 259 int8_t& dom, int8_t& dow, int32_t& mid, UErrorCode& status); 260 261 /** 262 * Convert a 1970-epoch milliseconds to proleptic Gregorian year, 263 * month, day-of-month, and day-of-week, day of year and millis-in-day. 264 * @param time 1970-epoch milliseconds 265 * @param year output parameter to receive year 266 * @param month output parameter to receive month (0-based, 0==Jan) 267 * @param dom output parameter to receive day-of-month (1-based) 268 * @param mid output parameter to receive millis-in-day 269 * @param status error code. 270 */ 271 static void timeToFields(UDate time, int32_t& year, int8_t& month, 272 int8_t& dom, int32_t& mid, UErrorCode& status); 273 274 /** 275 * Convert a 1970-epoch milliseconds to proleptic Gregorian year. 276 * @param time 1970-epoch milliseconds 277 * @param status error code. 278 * @return year. 279 */ 280 static int32_t timeToYear(UDate time, UErrorCode& status); 281 282 /** 283 * Return the day of week on the 1970-epoch day 284 * @param day the 1970-epoch day 285 * @return the day of week 286 */ 287 static int32_t dayOfWeek(int32_t day); 288 289 /** 290 * Returns the ordinal number for the specified day of week within the month. 291 * The valid return value is 1, 2, 3, 4 or -1. 292 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. 293 * @param month 0-based month, with 0==Jan 294 * @param dom 1-based day of month 295 * @return The ordinal number for the specified day of week within the month 296 */ 297 static int32_t dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom); 298 299 /** 300 * Converts Julian day to time as milliseconds. 301 * @param julian the given Julian day number. 302 * @return time as milliseconds. 303 * @internal 304 */ 305 static inline double julianDayToMillis(int32_t julian); 306 307 /** 308 * Converts time as milliseconds to Julian day. 309 * @param millis the given milliseconds. 310 * @return the Julian day number. 311 * @internal 312 */ 313 static inline int32_t millisToJulianDay(double millis); 314 315 /** 316 * Calculates the Gregorian day shift value for an extended year. 317 * @param eyear Extended year 318 * @returns number of days to ADD to Julian in order to convert from J->G 319 */ 320 static inline int32_t gregorianShift(int32_t eyear); 321 322 private: 323 static const int16_t DAYS_BEFORE[24]; 324 static const int8_t MONTH_LENGTH[24]; 325 }; 326 327 inline double ClockMath::floorDivide(double numerator, double denominator) { 328 return uprv_floor(numerator / denominator); 329 } 330 331 inline UBool Grego::isLeapYear(int32_t year) { 332 // year&0x3 == year%4 333 return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0)); 334 } 335 336 inline int8_t 337 Grego::monthLength(int32_t year, int32_t month) { 338 return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)]; 339 } 340 341 inline int8_t 342 Grego::previousMonthLength(int y, int m) { 343 return (m > 0) ? monthLength(y, m-1) : 31; 344 } 345 346 inline double Grego::julianDayToMillis(int32_t julian) 347 { 348 return (static_cast<double>(julian) - kEpochStartAsJulianDay) * kOneDay; 349 } 350 351 inline int32_t Grego::millisToJulianDay(double millis) { 352 return static_cast<int32_t>(kEpochStartAsJulianDay + ClockMath::floorDivide(millis, kOneDay)); 353 } 354 355 inline int32_t Grego::gregorianShift(int32_t eyear) { 356 int64_t y = static_cast<int64_t>(eyear) - 1; 357 int64_t gregShift = ClockMath::floorDivideInt64(y, 400LL) - ClockMath::floorDivideInt64(y, 100LL) + 2; 358 return static_cast<int32_t>(gregShift); 359 } 360 361 #define IMPL_SYSTEM_DEFAULT_CENTURY(T, U) \ 362 /** \ 363 * The system maintains a static default century start date and Year. They \ 364 * are initialized the first time they are used. Once the system default \ 365 * century date and year are set, they do not change \ 366 */ \ 367 namespace { \ 368 static UDate gSystemDefaultCenturyStart = DBL_MIN; \ 369 static int32_t gSystemDefaultCenturyStartYear = -1; \ 370 static icu::UInitOnce gSystemDefaultCenturyInit {}; \ 371 static void U_CALLCONV \ 372 initializeSystemDefaultCentury() { \ 373 UErrorCode status = U_ZERO_ERROR; \ 374 T calendar(U, status); \ 375 /* initialize systemDefaultCentury and systemDefaultCenturyYear based */ \ 376 /* on the current time. They'll be set to 80 years before */ \ 377 /* the current time. */ \ 378 if (U_FAILURE(status)) { \ 379 return; \ 380 } \ 381 calendar.setTime(Calendar::getNow(), status); \ 382 calendar.add(UCAL_YEAR, -80, status); \ 383 gSystemDefaultCenturyStart = calendar.getTime(status); \ 384 gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); \ 385 /* We have no recourse upon failure unless we want to propagate the */ \ 386 /* failure out. */ \ 387 } \ 388 } /* namespace */ \ 389 UDate T::defaultCenturyStart() const { \ 390 /* lazy-evaluate systemDefaultCenturyStart */ \ 391 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); \ 392 return gSystemDefaultCenturyStart; \ 393 } \ 394 int32_t T::defaultCenturyStartYear() const { \ 395 /* lazy-evaluate systemDefaultCenturyStart */ \ 396 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); \ 397 return gSystemDefaultCenturyStartYear; \ 398 } \ 399 UBool T::haveDefaultCentury() const { return true; } 400 401 U_NAMESPACE_END 402 403 #endif // !UCONFIG_NO_FORMATTING 404 #endif // GREGOIMP_H 405 406 //eof