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
23U_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 */
30class 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 */
137class 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
256inline double ClockMath::floorDivide(double numerator, double denominator) {
257    return uprv_floor(numerator / denominator);
258}
259
260inline UBool Grego::isLeapYear(int32_t year) {
261    // year&0x3 == year%4
262    return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0));
263}
264
265inline int8_t
266Grego::monthLength(int32_t year, int32_t month) {
267    return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)];
268}
269
270inline int8_t
271Grego::previousMonthLength(int y, int m) {
272  return (m > 0) ? monthLength(y, m-1) : 31;
273}
274
275inline 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
281inline double Grego::julianDayToMillis(int32_t julian)
282{
283  return (julian - kEpochStartAsJulianDay) * kOneDay;
284}
285
286inline int32_t Grego::millisToJulianDay(double millis) {
287  return (int32_t) (kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay));
288}
289
290inline 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
296U_NAMESPACE_END
297
298#endif // !UCONFIG_NO_FORMATTING
299#endif // GREGOIMP_H
300
301//eof
302