1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <i18nutil/calendar.hxx>
25 #include <calendar_hijri.hxx>
26 #include <tools/long.hxx>
27 #include <basegfx/numeric/ftools.hxx>
29 using namespace ::com::sun::star::i18n
;
33 // Synodic Period (mean time between 2 successive new moon: 29d, 12 hr, 44min, 3sec
34 constexpr double SynPeriod
= 29.53058868;
36 // Julian day on Jan 1, 1900
37 constexpr double jd1900
= 2415020.75933;
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;
43 Calendar_hijri::Calendar_hijri()
45 cCalendar
= u
"com.sun.star.i18n.Calendar_hijri"_ustr
;
48 #define FIELDS ((1 << CalendarFieldIndex::ERA) | (1 << CalendarFieldIndex::YEAR) | (1 << CalendarFieldIndex::MONTH) | (1 << CalendarFieldIndex::DAY_OF_MONTH))
50 // map field value from hijri calendar to gregorian calendar
51 void Calendar_hijri::mapToGregorian()
53 if (!(fieldSet
& FIELDS
))
56 sal_Int32 day
= static_cast<sal_Int32
>(fieldSetValue
[CalendarFieldIndex::DAY_OF_MONTH
]);
57 sal_Int32 month
= static_cast<sal_Int32
>(fieldSetValue
[CalendarFieldIndex::MONTH
]) + 1;
58 sal_Int32 year
= static_cast<sal_Int32
>(fieldSetValue
[CalendarFieldIndex::YEAR
]);
59 if (fieldSetValue
[CalendarFieldIndex::ERA
] == 0)
62 ToGregorian(&day
, &month
, &year
);
64 fieldSetValue
[CalendarFieldIndex::ERA
] = year
<= 0 ? 0 : 1;
65 fieldSetValue
[CalendarFieldIndex::MONTH
] = sal::static_int_cast
<sal_Int16
>(month
- 1);
66 fieldSetValue
[CalendarFieldIndex::DAY_OF_MONTH
] = static_cast<sal_Int16
>(day
);
67 fieldSetValue
[CalendarFieldIndex::YEAR
] = static_cast<sal_Int16
>(abs(year
));
71 // map field value from gregorian calendar to hijri calendar
72 void Calendar_hijri::mapFromGregorian()
74 sal_Int32 month
, day
, year
;
76 day
= static_cast<sal_Int32
>(fieldValue
[CalendarFieldIndex::DAY_OF_MONTH
]);
77 month
= static_cast<sal_Int32
>(fieldValue
[CalendarFieldIndex::MONTH
]) + 1;
78 year
= static_cast<sal_Int32
>(fieldValue
[CalendarFieldIndex::YEAR
]);
79 if (fieldValue
[CalendarFieldIndex::ERA
] == 0)
83 getHijri(&day
, &month
, &year
);
85 fieldValue
[CalendarFieldIndex::DAY_OF_MONTH
] = static_cast<sal_Int16
>(day
);
86 fieldValue
[CalendarFieldIndex::MONTH
] = sal::static_int_cast
<sal_Int16
>(month
- 1);
87 fieldValue
[CalendarFieldIndex::YEAR
] = static_cast<sal_Int16
>(abs(year
));
88 fieldValue
[CalendarFieldIndex::ERA
] = static_cast<sal_Int16
>(year
) < 1 ? 0 : 1;
92 // This function returns the Julian date/time of the Nth new moon since
93 // January 1900. The synodic month is passed as parameter.
95 // Adapted from "Astronomical Formulae for Calculators" by
96 // Jean Meeus, Third Edition, Willmann-Bell, 1985.
99 Calendar_hijri::NewMoon(sal_Int32 n
)
101 double jd
, t
, t2
, t3
, k
, ma
, sa
, tf
, xtra
;
103 t
= k
/1236.85; // Time in Julian centuries from 1900 January 0.5
107 // Mean time of phase
112 + 0.00033 * sin(basegfx::deg2rad(166.56 + 132.87 * t
- 0.009173 * t2
));
114 // Sun's mean anomaly in radian
115 sa
= basegfx::deg2rad(359.2242
120 // Moon's mean anomaly
121 ma
= basegfx::deg2rad(306.0253
126 // Moon's argument of latitude
127 tf
= 2.0 * basegfx::deg2rad(21.2964
132 // should reduce to interval between 0 to 1.0 before calculating further
133 // Corrections for New Moon
134 xtra
= (0.1734 - 0.000393 * t
) * sin(sa
)
135 + 0.0021 * sin(sa
* 2)
137 + 0.0161 * sin(2 * ma
)
138 - 0.0004 * sin(3 * ma
)
140 - 0.0051 * sin(sa
+ ma
)
141 - 0.0074 * sin(sa
- ma
)
142 + 0.0004 * sin(tf
+ sa
)
143 - 0.0004 * sin(tf
- sa
)
144 - 0.0006 * sin(tf
+ ma
)
145 + 0.0010 * sin(tf
- ma
)
146 + 0.0005 * sin(sa
+ 2 * ma
);
148 // convert from Ephemeris Time (ET) to (approximate) Universal Time (UT)
149 jd
+= xtra
- (0.41 + 1.2053 * t
+ 0.4992 * t2
)/1440;
156 Calendar_hijri::getHijri(sal_Int32
*day
, sal_Int32
*month
, sal_Int32
*year
)
164 // Get Julian Day from Gregorian
165 sal_Int32
const julday
= getJulianDay(*day
, *month
, *year
);
167 // obtain approx. of how many Synodic months since the beginning of the year 1900
168 synmonth
= static_cast<sal_Int32
>(0.5 + (julday
- jd1900
)/SynPeriod
);
171 prevday
= julday
- 0.5;
174 newjd
= NewMoon(newsyn
);
176 // Decrement syntonic months
178 } while (newjd
> prevday
);
181 // difference from reference point
182 syndiff
= newsyn
- SynRef
;
185 *day
= static_cast<sal_Int32
>(julday
- newjd
+ 0.5);
186 *month
= (syndiff
% 12) + 1;
188 // currently not supported
189 //dayOfYear = (sal_Int32)(month * SynPeriod + day);
190 *year
= GregRef
+ static_cast<sal_Int32
>(syndiff
/ 12);
192 // If month negative, consider it previous year
193 if (syndiff
!= 0 && *month
<= 0) {
198 // If Before Hijri subtract 1
199 if (*year
<= 0) (*year
)--;
203 Calendar_hijri::ToGregorian(sal_Int32
*day
, sal_Int32
*month
, sal_Int32
*year
)
208 if ( *year
< 0 ) (*year
)++;
210 // Number of month from reference point
211 nmonth
= *month
+ *year
* 12 - (GregRef
* 12 + 1);
213 // Add Synodic Reference point
216 // Get Julian days add time too
217 jday
= NewMoon(nmonth
) + *day
;
220 jday
= std::trunc(jday
+ 0.5);
222 // Use algorithm from "Numerical Recipes in C"
223 getGregorianDay(static_cast<sal_Int32
>(jday
), day
, month
, year
);
225 // Julian -> Gregorian only works for non-negative year
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 */
236 Calendar_hijri::getGregorianDay(sal_Int32 lJulianDay
, sal_Int32
*pnDay
, sal_Int32
*pnMonth
, sal_Int32
*pnYear
)
238 /* working variables */
239 tools::Long lFactorA
, lFactorB
, lFactorC
, lFactorD
, lFactorE
;
241 constexpr sal_Int32 GREGORIAN_CROSSOVER
= 2299161;
243 /* test whether to adjust for the Gregorian calendar crossover */
244 if (lJulianDay
>= GREGORIAN_CROSSOVER
) {
245 /* calculate a small adjustment */
246 tools::Long lAdjust
= static_cast<tools::Long
>((static_cast<float>(lJulianDay
- 1867216) - 0.25) / 36524.25);
248 lFactorA
= lJulianDay
+ 1 + lAdjust
- static_cast<tools::Long
>(0.25 * lAdjust
);
251 /* no adjustment needed */
252 lFactorA
= lJulianDay
;
255 lFactorB
= lFactorA
+ 1524;
256 lFactorC
= static_cast<tools::Long
>(6680.0 + (static_cast<float>(lFactorB
- 2439870) - 122.1) / 365.25);
257 lFactorD
= static_cast<tools::Long
>(365 * lFactorC
+ (0.25 * lFactorC
));
258 lFactorE
= static_cast<tools::Long
>((lFactorB
- lFactorD
) / i18nutil::monthDaysWithoutJanFeb
);
260 /* now, pull out the day number */
261 *pnDay
= lFactorB
- lFactorD
- static_cast<tools::Long
>(i18nutil::monthDaysWithoutJanFeb
* lFactorE
);
263 /* ...and the month, adjusting it if necessary */
264 *pnMonth
= lFactorE
- 1;
268 /* ...and similarly for the year */
269 *pnYear
= lFactorC
- 4715;
273 // Negative year adjustments
279 Calendar_hijri::getJulianDay(sal_Int32 day
, sal_Int32 month
, sal_Int32 year
)
287 if( year
== 1582 && month
== 10 && day
> 4 && day
< 15 ) {
299 sal_Int32 intgr
= static_cast<sal_Int32
>(static_cast<sal_Int32
>(365.25 * jy
) + static_cast<sal_Int32
>(i18nutil::monthDaysWithoutJanFeb
* jm
) + day
+ 1720995 );
301 //check for switch to Gregorian calendar
302 double const gregcal
= 15 + 31 * ( 10 + 12 * 1582 );
304 if( day
+ 31 * (month
+ 12 * year
) >= gregcal
) {
306 ja
= std::trunc(0.01 * jy
);
307 intgr
+= static_cast<sal_Int32
>(2 - ja
+ std::trunc(0.25 * ja
));
315 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */