Coverage Report

Created: 2023-02-22 06:51

/src/icu/source/i18n/gregoimp.cpp
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
#include "gregoimp.h"
15
16
#if !UCONFIG_NO_FORMATTING
17
18
#include "unicode/ucal.h"
19
#include "uresimp.h"
20
#include "cstring.h"
21
#include "uassert.h"
22
23
U_NAMESPACE_BEGIN
24
25
0
int32_t ClockMath::floorDivide(int32_t numerator, int32_t denominator) {
26
0
    return (numerator >= 0) ?
27
0
        numerator / denominator : ((numerator + 1) / denominator) - 1;
28
0
}
29
30
0
int64_t ClockMath::floorDivide(int64_t numerator, int64_t denominator) {
31
0
    return (numerator >= 0) ?
32
0
        numerator / denominator : ((numerator + 1) / denominator) - 1;
33
0
}
34
35
int32_t ClockMath::floorDivide(double numerator, int32_t denominator,
36
0
                          int32_t& remainder) {
37
0
    double quotient;
38
0
    quotient = uprv_floor(numerator / denominator);
39
0
    remainder = (int32_t) (numerator - (quotient * denominator));
40
0
    return (int32_t) quotient;
41
0
}
42
43
double ClockMath::floorDivide(double dividend, double divisor,
44
0
                         double& remainder) {
45
    // Only designed to work for positive divisors
46
0
    U_ASSERT(divisor > 0);
47
0
    double quotient = floorDivide(dividend, divisor);
48
0
    remainder = dividend - (quotient * divisor);
49
    // N.B. For certain large dividends, on certain platforms, there
50
    // is a bug such that the quotient is off by one.  If you doubt
51
    // this to be true, set a breakpoint below and run cintltst.
52
0
    if (remainder < 0 || remainder >= divisor) {
53
        // E.g. 6.7317038241449352e+022 / 86400000.0 is wrong on my
54
        // machine (too high by one).  4.1792057231752762e+024 /
55
        // 86400000.0 is wrong the other way (too low).
56
0
        double q = quotient;
57
0
        quotient += (remainder < 0) ? -1 : +1;
58
0
        if (q == quotient) {
59
            // For quotients > ~2^53, we won't be able to add or
60
            // subtract one, since the LSB of the mantissa will be >
61
            // 2^0; that is, the exponent (base 2) will be larger than
62
            // the length, in bits, of the mantissa.  In that case, we
63
            // can't give a correct answer, so we set the remainder to
64
            // zero.  This has the desired effect of making extreme
65
            // values give back an approximate answer rather than
66
            // crashing.  For example, UDate values above a ~10^25
67
            // might all have a time of midnight.
68
0
            remainder = 0;
69
0
        } else {
70
0
            remainder = dividend - (quotient * divisor);
71
0
        }
72
0
    }
73
0
    U_ASSERT(0 <= remainder && remainder < divisor);
74
0
    return quotient;
75
0
}
76
77
const int32_t JULIAN_1_CE    = 1721426; // January 1, 1 CE Gregorian
78
const int32_t JULIAN_1970_CE = 2440588; // January 1, 1970 CE Gregorian
79
80
const int16_t Grego::DAYS_BEFORE[24] =
81
    {0,31,59,90,120,151,181,212,243,273,304,334,
82
     0,31,60,91,121,152,182,213,244,274,305,335};
83
84
const int8_t Grego::MONTH_LENGTH[24] =
85
    {31,28,31,30,31,30,31,31,30,31,30,31,
86
     31,29,31,30,31,30,31,31,30,31,30,31};
87
88
0
double Grego::fieldsToDay(int32_t year, int32_t month, int32_t dom) {
89
90
0
    int32_t y = year - 1;
91
92
0
    double julian = 365 * y + ClockMath::floorDivide(y, 4) + (JULIAN_1_CE - 3) + // Julian cal
93
0
        ClockMath::floorDivide(y, 400) - ClockMath::floorDivide(y, 100) + 2 + // => Gregorian cal
94
0
        DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; // => month/dom
95
96
0
    return julian - JULIAN_1970_CE; // JD => epoch day
97
0
}
98
99
void Grego::dayToFields(double day, int32_t& year, int32_t& month,
100
0
                        int32_t& dom, int32_t& dow, int32_t& doy) {
101
102
    // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
103
0
    day += JULIAN_1970_CE - JULIAN_1_CE;
104
105
    // Convert from the day number to the multiple radix
106
    // representation.  We use 400-year, 100-year, and 4-year cycles.
107
    // For example, the 4-year cycle has 4 years + 1 leap day; giving
108
    // 1461 == 365*4 + 1 days.
109
0
    int32_t n400 = ClockMath::floorDivide(day, 146097, doy); // 400-year cycle length
110
0
    int32_t n100 = ClockMath::floorDivide(doy, 36524, doy); // 100-year cycle length
111
0
    int32_t n4   = ClockMath::floorDivide(doy, 1461, doy); // 4-year cycle length
112
0
    int32_t n1   = ClockMath::floorDivide(doy, 365, doy);
113
0
    year = 400*n400 + 100*n100 + 4*n4 + n1;
114
0
    if (n100 == 4 || n1 == 4) {
115
0
        doy = 365; // Dec 31 at end of 4- or 400-year cycle
116
0
    } else {
117
0
        ++year;
118
0
    }
119
    
120
0
    UBool isLeap = isLeapYear(year);
121
    
122
    // Gregorian day zero is a Monday.
123
0
    dow = (int32_t) uprv_fmod(day + 1, 7);
124
0
    dow += (dow < 0) ? (UCAL_SUNDAY + 7) : UCAL_SUNDAY;
125
126
    // Common Julian/Gregorian calculation
127
0
    int32_t correction = 0;
128
0
    int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
129
0
    if (doy >= march1) {
130
0
        correction = isLeap ? 1 : 2;
131
0
    }
132
0
    month = (12 * (doy + correction) + 6) / 367; // zero-based month
133
0
    dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1; // one-based DOM
134
0
    doy++; // one-based doy
135
0
}
136
137
void Grego::timeToFields(UDate time, int32_t& year, int32_t& month,
138
0
                        int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid) {
139
0
    double millisInDay;
140
0
    double day = ClockMath::floorDivide((double)time, (double)U_MILLIS_PER_DAY, millisInDay);
141
0
    mid = (int32_t)millisInDay;
142
0
    dayToFields(day, year, month, dom, dow, doy);
143
0
}
144
145
0
int32_t Grego::dayOfWeek(double day) {
146
0
    int32_t dow;
147
0
    ClockMath::floorDivide(day + UCAL_THURSDAY, 7, dow);
148
0
    return (dow == 0) ? UCAL_SATURDAY : dow;
149
0
}
150
151
0
int32_t Grego::dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom) {
152
0
    int32_t weekInMonth = (dom + 6)/7;
153
0
    if (weekInMonth == 4) {
154
0
        if (dom + 7 > monthLength(year, month)) {
155
0
            weekInMonth = -1;
156
0
        }
157
0
    } else if (weekInMonth == 5) {
158
0
        weekInMonth = -1;
159
0
    }
160
0
    return weekInMonth;
161
0
}
162
163
U_NAMESPACE_END
164
165
#endif
166
//eof