Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/i18npool/source/calendar/calendar_hijri.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sal/config.h>
21
#include <i18nutil/calendar.hxx>
22
#include <cmath>
23
#include <stdlib.h>
24
25
#include <calendar_hijri.hxx>
26
#include <tools/long.hxx>
27
#include <basegfx/numeric/ftools.hxx>
28
29
using namespace ::com::sun::star::i18n;
30
31
namespace i18npool {
32
33
// Synodic Period (mean time between 2 successive new moon: 29d, 12 hr, 44min, 3sec
34
constexpr double SynPeriod = 29.53058868;
35
36
// Julian day on Jan 1, 1900
37
constexpr double jd1900 = 2415020.75933;
38
39
// Reference point: March 26, 2001 == 1422 Hijri == 1252 Synodial month from 1900
40
constexpr sal_Int32 SynRef = 1252;
41
constexpr sal_Int32 GregRef = 1422;
42
43
Calendar_hijri::Calendar_hijri()
44
196
{
45
196
    cCalendar = u"com.sun.star.i18n.Calendar_hijri"_ustr;
46
196
}
47
48
0
#define FIELDS  ((1 << CalendarFieldIndex::ERA) | (1 << CalendarFieldIndex::YEAR) | (1 << CalendarFieldIndex::MONTH) | (1 << CalendarFieldIndex::DAY_OF_MONTH))
49
50
// map field value from hijri calendar to gregorian calendar
51
void Calendar_hijri::mapToGregorian()
52
0
{
53
0
    if (!(fieldSet & FIELDS))
54
0
        return;
55
56
0
    sal_Int32 day = static_cast<sal_Int32>(fieldSetValue[CalendarFieldIndex::DAY_OF_MONTH]);
57
0
    sal_Int32 month = static_cast<sal_Int32>(fieldSetValue[CalendarFieldIndex::MONTH]) + 1;
58
0
    sal_Int32 year = static_cast<sal_Int32>(fieldSetValue[CalendarFieldIndex::YEAR]);
59
0
    if (fieldSetValue[CalendarFieldIndex::ERA] == 0)
60
0
        year *= -1;
61
62
0
    ToGregorian(&day, &month, &year);
63
64
0
    fieldSetValue[CalendarFieldIndex::ERA] = year <= 0 ? 0 : 1;
65
0
    fieldSetValue[CalendarFieldIndex::MONTH] = sal::static_int_cast<sal_Int16>(month - 1);
66
0
    fieldSetValue[CalendarFieldIndex::DAY_OF_MONTH] = static_cast<sal_Int16>(day);
67
0
    fieldSetValue[CalendarFieldIndex::YEAR] = static_cast<sal_Int16>(abs(year));
68
0
    fieldSet |= FIELDS;
69
0
}
70
71
// map field value from gregorian calendar to hijri calendar
72
void Calendar_hijri::mapFromGregorian()
73
1.13k
{
74
1.13k
    sal_Int32 month, day, year;
75
76
1.13k
    day = static_cast<sal_Int32>(fieldValue[CalendarFieldIndex::DAY_OF_MONTH]);
77
1.13k
    month = static_cast<sal_Int32>(fieldValue[CalendarFieldIndex::MONTH]) + 1;
78
1.13k
    year = static_cast<sal_Int32>(fieldValue[CalendarFieldIndex::YEAR]);
79
1.13k
    if (fieldValue[CalendarFieldIndex::ERA] == 0)
80
0
        year *= -1;
81
82
    // Get Hijri date
83
1.13k
    getHijri(&day, &month, &year);
84
85
1.13k
    fieldValue[CalendarFieldIndex::DAY_OF_MONTH] = static_cast<sal_Int16>(day);
86
1.13k
    fieldValue[CalendarFieldIndex::MONTH] = sal::static_int_cast<sal_Int16>(month - 1);
87
1.13k
    fieldValue[CalendarFieldIndex::YEAR] = static_cast<sal_Int16>(abs(year));
88
1.13k
    fieldValue[CalendarFieldIndex::ERA] = static_cast<sal_Int16>(year) < 1 ? 0 : 1;
89
1.13k
}
90
91
92
// This function returns the Julian date/time of the Nth new moon since
93
// January 1900.  The synodic month is passed as parameter.
94
95
// Adapted from "Astronomical  Formulae for Calculators" by
96
// Jean Meeus, Third Edition, Willmann-Bell, 1985.
97
98
double
99
Calendar_hijri::NewMoon(sal_Int32 n)
100
1.13k
{
101
1.13k
    double jd, t, t2, t3, k, ma, sa, tf, xtra;
102
1.13k
    k = n;
103
1.13k
    t = k/1236.85;  // Time in Julian centuries from 1900 January 0.5
104
1.13k
    t2 = t * t;
105
1.13k
    t3 = t2 * t;
106
107
    // Mean time of phase
108
1.13k
    jd =  jd1900
109
1.13k
        + SynPeriod * k
110
1.13k
        - 0.0001178 * t2
111
1.13k
        - 0.000000155 * t3
112
1.13k
        + 0.00033 * sin(basegfx::deg2rad(166.56 + 132.87 * t - 0.009173 * t2));
113
114
    // Sun's mean anomaly in radian
115
1.13k
    sa =  basegfx::deg2rad(359.2242
116
1.13k
                + 29.10535608 * k
117
1.13k
                - 0.0000333 * t2
118
1.13k
                - 0.00000347 * t3);
119
120
    // Moon's mean anomaly
121
1.13k
    ma =  basegfx::deg2rad(306.0253
122
1.13k
                + 385.81691806 * k
123
1.13k
                + 0.0107306 * t2
124
1.13k
                + 0.00001236 * t3);
125
126
    // Moon's argument of latitude
127
1.13k
    tf = 2.0 * basegfx::deg2rad(21.2964
128
1.13k
                + 390.67050646 * k
129
1.13k
                - 0.0016528 * t2
130
1.13k
                - 0.00000239 * t3);
131
132
    // should reduce to interval between 0 to 1.0 before calculating further
133
    // Corrections for New Moon
134
1.13k
    xtra = (0.1734 - 0.000393 * t) * sin(sa)
135
1.13k
        + 0.0021 * sin(sa * 2)
136
1.13k
        - 0.4068 * sin(ma)
137
1.13k
        + 0.0161 * sin(2 * ma)
138
1.13k
        - 0.0004 * sin(3 * ma)
139
1.13k
        + 0.0104 * sin(tf)
140
1.13k
        - 0.0051 * sin(sa + ma)
141
1.13k
        - 0.0074 * sin(sa - ma)
142
1.13k
        + 0.0004 * sin(tf + sa)
143
1.13k
        - 0.0004 * sin(tf - sa)
144
1.13k
        - 0.0006 * sin(tf + ma)
145
1.13k
        + 0.0010 * sin(tf - ma)
146
1.13k
        + 0.0005 * sin(sa + 2 * ma);
147
148
    // convert from Ephemeris Time (ET) to (approximate) Universal Time (UT)
149
1.13k
    jd += xtra - (0.41 + 1.2053 * t + 0.4992 * t2)/1440;
150
151
1.13k
    return jd;
152
1.13k
}
153
154
// Get Hijri Date
155
void
156
Calendar_hijri::getHijri(sal_Int32 *day, sal_Int32 *month, sal_Int32 *year)
157
1.13k
{
158
1.13k
    double prevday;
159
1.13k
    sal_Int32 syndiff;
160
1.13k
    sal_Int32 newsyn;
161
1.13k
    double newjd;
162
1.13k
    sal_Int32 synmonth;
163
164
    // Get Julian Day from Gregorian
165
1.13k
    sal_Int32 const julday = getJulianDay(*day, *month, *year);
166
167
    // obtain approx. of how many Synodic months since the beginning of the year 1900
168
1.13k
    synmonth = static_cast<sal_Int32>(0.5 + (julday - jd1900)/SynPeriod);
169
170
1.13k
    newsyn = synmonth;
171
1.13k
    prevday = julday - 0.5;
172
173
1.13k
    do {
174
1.13k
        newjd = NewMoon(newsyn);
175
176
        // Decrement syntonic months
177
1.13k
        newsyn--;
178
1.13k
    } while (newjd > prevday);
179
1.13k
    newsyn++;
180
181
    // difference from reference point
182
1.13k
    syndiff = newsyn - SynRef;
183
184
    // Round up the day
185
1.13k
    *day = static_cast<sal_Int32>(julday - newjd + 0.5);
186
1.13k
    *month =  (syndiff % 12) + 1;
187
188
    // currently not supported
189
    //dayOfYear = (sal_Int32)(month * SynPeriod + day);
190
1.13k
    *year = GregRef + static_cast<sal_Int32>(syndiff / 12);
191
192
    // If month negative, consider it previous year
193
1.13k
    if (syndiff != 0 && *month <= 0) {
194
0
        *month += 12;
195
0
        (*year)--;
196
0
    }
197
198
    // If Before Hijri subtract 1
199
1.13k
    if (*year <= 0) (*year)--;
200
1.13k
}
201
202
void
203
Calendar_hijri::ToGregorian(sal_Int32 *day, sal_Int32 *month, sal_Int32 *year)
204
0
{
205
0
    sal_Int32 nmonth;
206
0
    double jday;
207
208
0
    if ( *year < 0 ) (*year)++;
209
210
    // Number of month from reference point
211
0
    nmonth = *month + *year * 12 - (GregRef * 12 + 1);
212
213
    // Add Synodic Reference point
214
0
    nmonth += SynRef;
215
216
    // Get Julian days add time too
217
0
    jday = NewMoon(nmonth) + *day;
218
219
    // Round-up
220
0
    jday = std::trunc(jday + 0.5);
221
222
    // Use algorithm from "Numerical Recipes in C"
223
0
    getGregorianDay(static_cast<sal_Int32>(jday), day, month, year);
224
225
    // Julian -> Gregorian only works for non-negative year
226
0
    if ( *year <= 0 ) {
227
0
    *day = -1;
228
0
    *month = -1;
229
0
    *year = -1;
230
0
    }
231
0
}
232
233
/* this algorithm is taken from "Numerical Recipes in C", 2nd ed, pp 14-15. */
234
/* this algorithm only valid for non-negative gregorian year                */
235
void
236
Calendar_hijri::getGregorianDay(sal_Int32 lJulianDay, sal_Int32 *pnDay, sal_Int32 *pnMonth, sal_Int32 *pnYear)
237
0
{
238
    /* working variables */
239
0
    tools::Long lFactorA, lFactorB, lFactorC, lFactorD, lFactorE;
240
241
0
    constexpr sal_Int32 GREGORIAN_CROSSOVER = 2299161;
242
243
    /* test whether to adjust for the Gregorian calendar crossover */
244
0
    if (lJulianDay >= GREGORIAN_CROSSOVER) {
245
    /* calculate a small adjustment */
246
0
    tools::Long lAdjust = static_cast<tools::Long>((static_cast<float>(lJulianDay - 1867216) - 0.25) / 36524.25);
247
248
0
    lFactorA = lJulianDay + 1 + lAdjust - static_cast<tools::Long>(0.25 * lAdjust);
249
250
0
    } else {
251
    /* no adjustment needed */
252
0
    lFactorA = lJulianDay;
253
0
    }
254
255
0
    lFactorB = lFactorA + 1524;
256
0
    lFactorC = static_cast<tools::Long>(6680.0 + (static_cast<float>(lFactorB - 2439870) - 122.1) / 365.25);
257
0
    lFactorD = static_cast<tools::Long>(365 * lFactorC + (0.25 * lFactorC));
258
0
    lFactorE = static_cast<tools::Long>((lFactorB - lFactorD) / i18nutil::monthDaysWithoutJanFeb);
259
260
    /* now, pull out the day number */
261
0
    *pnDay = lFactorB - lFactorD - static_cast<tools::Long>(i18nutil::monthDaysWithoutJanFeb * lFactorE);
262
263
    /* ...and the month, adjusting it if necessary */
264
0
    *pnMonth = lFactorE - 1;
265
0
    if (*pnMonth > 12)
266
0
        (*pnMonth) -= 12;
267
268
    /* ...and similarly for the year */
269
0
    *pnYear = lFactorC - 4715;
270
0
    if (*pnMonth > 2)
271
0
        (*pnYear)--;
272
273
    // Negative year adjustments
274
0
    if (*pnYear <= 0)
275
0
        (*pnYear)--;
276
0
}
277
278
sal_Int32
279
Calendar_hijri::getJulianDay(sal_Int32 day, sal_Int32 month, sal_Int32 year)
280
1.13k
{
281
1.13k
    double jy, jm;
282
283
1.13k
    if( year == 0 ) {
284
0
    return -1;
285
0
    }
286
287
1.13k
    if( year == 1582 && month == 10 && day > 4 && day < 15 ) {
288
0
    return -1;
289
0
    }
290
291
1.13k
    if( month > 2 ) {
292
1.13k
    jy = year;
293
1.13k
    jm = month + 1;
294
1.13k
    } else {
295
0
    jy = year - 1;
296
0
    jm = month + 13;
297
0
    }
298
299
1.13k
    sal_Int32 intgr = static_cast<sal_Int32>(static_cast<sal_Int32>(365.25 * jy) + static_cast<sal_Int32>(i18nutil::monthDaysWithoutJanFeb * jm) + day + 1720995 );
300
301
    //check for switch to Gregorian calendar
302
1.13k
    double const gregcal = 15 + 31 * ( 10 + 12 * 1582 );
303
304
1.13k
    if( day + 31 * (month + 12 * year) >= gregcal ) {
305
1.13k
        double ja;
306
1.13k
        ja = std::trunc(0.01 * jy);
307
1.13k
        intgr += static_cast<sal_Int32>(2 - ja + std::trunc(0.25 * ja));
308
1.13k
    }
309
310
1.13k
    return intgr;
311
1.13k
}
312
313
}
314
315
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */