/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: */ |