1 // Copyright (C) 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 #if !UCONFIG_NO_FORMATTING 18 19 #include "unicode/ures.h" 20 #include "unicode/locid.h" 21 #include "putilimp.h" 22 23 U_NAMESPACE_BEGIN 24 25 /** 26 * A utility class providing mathematical functions used by time zone 27 * and calendar code. Do not instantiate. Formerly just named 'Math'. 28 * @internal 29 */ 30 class ClockMath { 31 public: 32 /** 33 * Divide two integers, returning the floor of the quotient. 34 * Unlike the built-in division, this is mathematically 35 * well-behaved. E.g., <code>-1/4</code> => 0 but 36 * <code>floorDivide(-1,4)</code> => -1. 37 * @param numerator the numerator 38 * @param denominator a divisor which must be != 0 39 * @return the floor of the quotient 40 */ 41 static int32_t floorDivide(int32_t numerator, int32_t denominator); 42 43 /** 44 * Divide two numbers, returning the floor of the quotient. 45 * Unlike the built-in division, this is mathematically 46 * well-behaved. E.g., <code>-1/4</code> => 0 but 47 * <code>floorDivide(-1,4)</code> => -1. 48 * @param numerator the numerator 49 * @param denominator a divisor which must be != 0 50 * @return the floor of the quotient 51 */ 52 static inline double floorDivide(double numerator, double denominator); 53 54 /** 55 * Divide two numbers, returning the floor of the quotient and 56 * the modulus remainder. Unlike the built-in division, this is 57 * mathematically well-behaved. E.g., <code>-1/4</code> => 0 and 58 * <code>-1%4</code> => -1, but <code>floorDivide(-1,4)</code> => 59 * -1 with <code>remainder</code> => 3. NOTE: If numerator is 60 * too large, the returned quotient may overflow. 61 * @param numerator the numerator 62 * @param denominator a divisor which must be != 0 63 * @param remainder output parameter to receive the 64 * remainder. Unlike <code>numerator % denominator</code>, this 65 * will always be non-negative, in the half-open range <code>[0, 66 * |denominator|)</code>. 67 * @return the floor of the quotient 68 */ 69 static int32_t floorDivide(double numerator, int32_t denominator, 70 int32_t& remainder); 71 72 /** 73 * For a positive divisor, return the quotient and remainder 74 * such that dividend = quotient*divisor + remainder and 75 * 0 <= remainder < divisor. 76 * 77 * Works around edge-case bugs. Handles pathological input 78 * (divident >> divisor) reasonably. 79 * 80 * Calling with a divisor <= 0 is disallowed. 81 */ 82 static double floorDivide(double dividend, double divisor, 83 double& remainder); 84 }; 85 86 // Useful millisecond constants 87 #define kOneDay (1.0 * U_MILLIS_PER_DAY) // 86,400,000 88 #define kOneHour (60*60*1000) 89 #define kOneMinute 60000 90 #define kOneSecond 1000 91 #define kOneMillisecond 1 92 #define kOneWeek (7.0 * kOneDay) // 604,800,000 93 94 // Epoch constants 95 #define kJan1_1JulianDay 1721426 // January 1, year 1 (Gregorian) 96 97 #define kEpochStartAsJulianDay 2440588 // January 1, 1970 (Gregorian) 98 99 #define kEpochYear 1970 100 101 102 #define kEarliestViableMillis -185331720384000000.0 // minimum representable by julian day -1e17 103 104 #define kLatestViableMillis 185753453990400000.0 // max representable by julian day +1e17 105 106 /** 107 * The minimum supported Julian day. This value is equivalent to 108 * MIN_MILLIS. 109 */ 110 #define MIN_JULIAN (-0x7F000000) 111 112 /** 113 * The minimum supported epoch milliseconds. This value is equivalent 114 * to MIN_JULIAN. 115 */ 116 #define MIN_MILLIS ((MIN_JULIAN - kEpochStartAsJulianDay) * kOneDay) 117 118 /** 119 * The maximum supported Julian day. This value is equivalent to 120 * MAX_MILLIS. 121 */ 122 #define MAX_JULIAN (+0x7F000000) 123 124 /** 125 * The maximum supported epoch milliseconds. This value is equivalent 126 * to MAX_JULIAN. 127 */ 128 #define MAX_MILLIS ((MAX_JULIAN - kEpochStartAsJulianDay) * kOneDay) 129 130 /** 131 * A utility class providing proleptic Gregorian calendar functions 132 * used by time zone and calendar code. Do not instantiate. 133 * 134 * Note: Unlike GregorianCalendar, all computations performed by this 135 * class occur in the pure proleptic GregorianCalendar. 136 */ 137 class Grego { 138 public: 139 /** 140 * Return TRUE if the given year is a leap year. 141 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. 142 * @return TRUE if the year is a leap year 143 */ 144 static inline UBool isLeapYear(int32_t year); 145 146 /** 147 * Return the number of days in the given month. 148 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. 149 * @param month 0-based month, with 0==Jan 150 * @return the number of days in the given month 151 */ 152 static inline int8_t monthLength(int32_t year, int32_t month); 153 154 /** 155 * Return the length of a previous month of the Gregorian calendar. 156 * @param y the extended year 157 * @param m the 0-based month number 158 * @return the number of days in the month previous to the given month 159 */ 160 static inline int8_t previousMonthLength(int y, int m); 161 162 /** 163 * Convert a year, month, and day-of-month, given in the proleptic 164 * Gregorian calendar, to 1970 epoch days. 165 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. 166 * @param month 0-based month, with 0==Jan 167 * @param dom 1-based day of month 168 * @return the day number, with day 0 == Jan 1 1970 169 */ 170 static double fieldsToDay(int32_t year, int32_t month, int32_t dom); 171 172 /** 173 * Convert a 1970-epoch day number to proleptic Gregorian year, 174 * month, day-of-month, and day-of-week. 175 * @param day 1970-epoch day (integral value) 176 * @param year output parameter to receive year 177 * @param month output parameter to receive month (0-based, 0==Jan) 178 * @param dom output parameter to receive day-of-month (1-based) 179 * @param dow output parameter to receive day-of-week (1-based, 1==Sun) 180 * @param doy output parameter to receive day-of-year (1-based) 181 */ 182 static void dayToFields(double day, int32_t& year, int32_t& month, 183 int32_t& dom, int32_t& dow, int32_t& doy); 184 185 /** 186 * Convert a 1970-epoch day number to proleptic Gregorian year, 187 * month, day-of-month, and day-of-week. 188 * @param day 1970-epoch day (integral value) 189 * @param year output parameter to receive year 190 * @param month output parameter to receive month (0-based, 0==Jan) 191 * @param dom output parameter to receive day-of-month (1-based) 192 * @param dow output parameter to receive day-of-week (1-based, 1==Sun) 193 */ 194 static inline void dayToFields(double day, int32_t& year, int32_t& month, 195 int32_t& dom, int32_t& dow); 196 197 /** 198 * Convert a 1970-epoch milliseconds to proleptic Gregorian year, 199 * month, day-of-month, and day-of-week, day of year and millis-in-day. 200 * @param time 1970-epoch milliseconds 201 * @param year output parameter to receive year 202 * @param month output parameter to receive month (0-based, 0==Jan) 203 * @param dom output parameter to receive day-of-month (1-based) 204 * @param dow output parameter to receive day-of-week (1-based, 1==Sun) 205 * @param doy output parameter to receive day-of-year (1-based) 206 * @param mid output parameter to recieve millis-in-day 207 */ 208 static void timeToFields(UDate time, int32_t& year, int32_t& month, 209 int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid); 210 211 /** 212 * Return the day of week on the 1970-epoch day 213 * @param day the 1970-epoch day (integral value) 214 * @return the day of week 215 */ 216 static int32_t dayOfWeek(double day); 217 218 /** 219 * Returns the ordinal number for the specified day of week within the month. 220 * The valid return value is 1, 2, 3, 4 or -1. 221 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. 222 * @param month 0-based month, with 0==Jan 223 * @param dom 1-based day of month 224 * @return The ordinal number for the specified day of week within the month 225 */ 226 static int32_t dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom); 227 228 /** 229 * Converts Julian day to time as milliseconds. 230 * @param julian the given Julian day number. 231 * @return time as milliseconds. 232 * @internal 233 */ 234 static inline double julianDayToMillis(int32_t julian); 235 236 /** 237 * Converts time as milliseconds to Julian day. 238 * @param millis the given milliseconds. 239 * @return the Julian day number. 240 * @internal 241 */ 242 static inline int32_t millisToJulianDay(double millis); 243 244 /** 245 * Calculates the Gregorian day shift value for an extended year. 246 * @param eyear Extended year 247 * @returns number of days to ADD to Julian in order to convert from J->G 248 */ 249 static inline int32_t gregorianShift(int32_t eyear); 250 251 private: 252 static const int16_t DAYS_BEFORE[24]; 253 static const int8_t MONTH_LENGTH[24]; 254 }; 255 256 inline double ClockMath::floorDivide(double numerator, double denominator) { 257 return uprv_floor(numerator / denominator); 258 } 259 260 inline UBool Grego::isLeapYear(int32_t year) { 261 // year&0x3 == year%4 262 return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0)); 263 } 264 265 inline int8_t 266 Grego::monthLength(int32_t year, int32_t month) { 267 return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)]; 268 } 269 270 inline int8_t 271 Grego::previousMonthLength(int y, int m) { 272 return (m > 0) ? monthLength(y, m-1) : 31; 273 } 274 275 inline void Grego::dayToFields(double day, int32_t& year, int32_t& month, 276 int32_t& dom, int32_t& dow) { 277 int32_t doy_unused; 278 dayToFields(day,year,month,dom,dow,doy_unused); 279 } 280 281 inline double Grego::julianDayToMillis(int32_t julian) 282 { 283 return (julian - kEpochStartAsJulianDay) * kOneDay; 284 } 285 286 inline int32_t Grego::millisToJulianDay(double millis) { 287 return (int32_t) (kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay)); 288 } 289 290 inline int32_t Grego::gregorianShift(int32_t eyear) { 291 int32_t y = eyear-1; 292 int32_t gregShift = ClockMath::floorDivide(y, 400) - ClockMath::floorDivide(y, 100) + 2; 293 return gregShift; 294 } 295 296 U_NAMESPACE_END 297 298 #endif // !UCONFIG_NO_FORMATTING 299 #endif // GREGOIMP_H 300 301 //eof 302