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, int32_t& month,
214 int32_t& dom, int32_t& dow, int32_t& doy, UErrorCode& status);
215
216 /**
217 * Convert a 1970-epoch day number to proleptic Gregorian year,
218 * month, day-of-month, and day-of-week.
219 * @param day 1970-epoch day
220 * @param year output parameter to receive year
221 * @param month output parameter to receive month (0-based, 0==Jan)
222 * @param dom output parameter to receive day-of-month (1-based)
223 * @param dow output parameter to receive day-of-week (1-based, 1==Sun)
224 * @param status error code.
225 */
226 static inline void dayToFields(int32_t day, int32_t& year, int32_t& month,
227 int32_t& dom, int32_t& dow, UErrorCode& status);
228
229 /**
230 * Convert a 1970-epoch milliseconds to proleptic Gregorian year,
231 * month, day-of-month, and day-of-week, day of year and millis-in-day.
232 * @param time 1970-epoch milliseconds
233 * @param year output parameter to receive year
234 * @param month output parameter to receive month (0-based, 0==Jan)
235 * @param dom output parameter to receive day-of-month (1-based)
236 * @param dow output parameter to receive day-of-week (1-based, 1==Sun)
237 * @param doy output parameter to receive day-of-year (1-based)
238 * @param mid output parameter to receive millis-in-day
239 * @param status error code.
240 */
241 static void timeToFields(UDate time, int32_t& year, int32_t& month,
242 int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid, UErrorCode& status);
243
244 /**
245 * Return the day of week on the 1970-epoch day
246 * @param day the 1970-epoch day
247 * @return the day of week
248 */
249 static int32_t dayOfWeek(int32_t day);
250
251 /**
252 * Returns the ordinal number for the specified day of week within the month.
253 * The valid return value is 1, 2, 3, 4 or -1.
254 * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.
255 * @param month 0-based month, with 0==Jan
256 * @param dom 1-based day of month
257 * @return The ordinal number for the specified day of week within the month
258 */
259 static int32_t dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom);
260
261 /**
262 * Converts Julian day to time as milliseconds.
263 * @param julian the given Julian day number.
264 * @return time as milliseconds.
265 * @internal
266 */
267 static inline double julianDayToMillis(int32_t julian);
268
269 /**
270 * Converts time as milliseconds to Julian day.
271 * @param millis the given milliseconds.
272 * @return the Julian day number.
273 * @internal
274 */
275 static inline int32_t millisToJulianDay(double millis);
276
277 /**
278 * Calculates the Gregorian day shift value for an extended year.
279 * @param eyear Extended year
280 * @returns number of days to ADD to Julian in order to convert from J->G
281 */
282 static inline int32_t gregorianShift(int32_t eyear);
283
284 private:
285 static const int16_t DAYS_BEFORE[24];
286 static const int8_t MONTH_LENGTH[24];
287 };
288
floorDivide(double numerator,double denominator)289 inline double ClockMath::floorDivide(double numerator, double denominator) {
290 return uprv_floor(numerator / denominator);
291 }
292
isLeapYear(int32_t year)293 inline UBool Grego::isLeapYear(int32_t year) {
294 // year&0x3 == year%4
295 return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0));
296 }
297
298 inline int8_t
monthLength(int32_t year,int32_t month)299 Grego::monthLength(int32_t year, int32_t month) {
300 return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)];
301 }
302
303 inline int8_t
previousMonthLength(int y,int m)304 Grego::previousMonthLength(int y, int m) {
305 return (m > 0) ? monthLength(y, m-1) : 31;
306 }
307
dayToFields(int32_t day,int32_t & year,int32_t & month,int32_t & dom,int32_t & dow,UErrorCode & status)308 inline void Grego::dayToFields(int32_t day, int32_t& year, int32_t& month,
309 int32_t& dom, int32_t& dow, UErrorCode& status) {
310 int32_t doy_unused;
311 dayToFields(day,year,month,dom,dow,doy_unused, status);
312 }
313
julianDayToMillis(int32_t julian)314 inline double Grego::julianDayToMillis(int32_t julian)
315 {
316 return (static_cast<double>(julian) - kEpochStartAsJulianDay) * kOneDay;
317 }
318
millisToJulianDay(double millis)319 inline int32_t Grego::millisToJulianDay(double millis) {
320 return static_cast<int32_t>(kEpochStartAsJulianDay + ClockMath::floorDivide(millis, kOneDay));
321 }
322
gregorianShift(int32_t eyear)323 inline int32_t Grego::gregorianShift(int32_t eyear) {
324 int64_t y = static_cast<int64_t>(eyear) - 1;
325 int64_t gregShift = ClockMath::floorDivideInt64(y, 400LL) - ClockMath::floorDivideInt64(y, 100LL) + 2;
326 return static_cast<int32_t>(gregShift);
327 }
328
329 #define IMPL_SYSTEM_DEFAULT_CENTURY(T, U) \
330 /** \
331 * The system maintains a static default century start date and Year. They \
332 * are initialized the first time they are used. Once the system default \
333 * century date and year are set, they do not change \
334 */ \
335 namespace { \
336 static UDate gSystemDefaultCenturyStart = DBL_MIN; \
337 static int32_t gSystemDefaultCenturyStartYear = -1; \
338 static icu::UInitOnce gSystemDefaultCenturyInit {}; \
339 static void U_CALLCONV \
340 initializeSystemDefaultCentury() { \
341 UErrorCode status = U_ZERO_ERROR; \
342 T calendar(U, status); \
343 /* initialize systemDefaultCentury and systemDefaultCenturyYear based */ \
344 /* on the current time. They'll be set to 80 years before */ \
345 /* the current time. */ \
346 if (U_FAILURE(status)) { \
347 return; \
348 } \
349 calendar.setTime(Calendar::getNow(), status); \
350 calendar.add(UCAL_YEAR, -80, status); \
351 gSystemDefaultCenturyStart = calendar.getTime(status); \
352 gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); \
353 /* We have no recourse upon failure unless we want to propagate the */ \
354 /* failure out. */ \
355 } \
356 } /* namespace */ \
357 UDate T::defaultCenturyStart() const { \
358 /* lazy-evaluate systemDefaultCenturyStart */ \
359 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); \
360 return gSystemDefaultCenturyStart; \
361 } \
362 int32_t T::defaultCenturyStartYear() const { \
363 /* lazy-evaluate systemDefaultCenturyStart */ \
364 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); \
365 return gSystemDefaultCenturyStartYear; \
366 } \
367 UBool T::haveDefaultCentury() const { return true; }
368
369 U_NAMESPACE_END
370
371 #endif // !UCONFIG_NO_FORMATTING
372 #endif // GREGOIMP_H
373
374 //eof
375