Coverage Report

Created: 2025-06-24 06:43

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