tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / i18npool / source / calendar / calendar_hijri.cxx
blob0e9396d4a2ce9fcd1a648f361735e8a25e5a0d08
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <cmath>
23 #include <stdlib.h>
25 #include <calendar_hijri.hxx>
26 #include <tools/long.hxx>
27 #include <basegfx/numeric/ftools.hxx>
29 using namespace ::com::sun::star::i18n;
31 namespace i18npool {
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))
54 return;
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)
60 year *= -1;
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));
68 fieldSet |= FIELDS;
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)
80 year *= -1;
82 // Get Hijri date
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.
98 double
99 Calendar_hijri::NewMoon(sal_Int32 n)
101 double jd, t, t2, t3, k, ma, sa, tf, xtra;
102 k = n;
103 t = k/1236.85; // Time in Julian centuries from 1900 January 0.5
104 t2 = t * t;
105 t3 = t2 * t;
107 // Mean time of phase
108 jd = jd1900
109 + SynPeriod * k
110 - 0.0001178 * t2
111 - 0.000000155 * t3
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
116 + 29.10535608 * k
117 - 0.0000333 * t2
118 - 0.00000347 * t3);
120 // Moon's mean anomaly
121 ma = basegfx::deg2rad(306.0253
122 + 385.81691806 * k
123 + 0.0107306 * t2
124 + 0.00001236 * t3);
126 // Moon's argument of latitude
127 tf = 2.0 * basegfx::deg2rad(21.2964
128 + 390.67050646 * k
129 - 0.0016528 * t2
130 - 0.00000239 * t3);
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)
136 - 0.4068 * sin(ma)
137 + 0.0161 * sin(2 * ma)
138 - 0.0004 * sin(3 * ma)
139 + 0.0104 * sin(tf)
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;
151 return jd;
154 // Get Hijri Date
155 void
156 Calendar_hijri::getHijri(sal_Int32 *day, sal_Int32 *month, sal_Int32 *year)
158 double prevday;
159 sal_Int32 syndiff;
160 sal_Int32 newsyn;
161 double newjd;
162 sal_Int32 synmonth;
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);
170 newsyn = synmonth;
171 prevday = julday - 0.5;
173 do {
174 newjd = NewMoon(newsyn);
176 // Decrement syntonic months
177 newsyn--;
178 } while (newjd > prevday);
179 newsyn++;
181 // difference from reference point
182 syndiff = newsyn - SynRef;
184 // Round up the day
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) {
194 *month += 12;
195 (*year)--;
198 // If Before Hijri subtract 1
199 if (*year <= 0) (*year)--;
202 void
203 Calendar_hijri::ToGregorian(sal_Int32 *day, sal_Int32 *month, sal_Int32 *year)
205 sal_Int32 nmonth;
206 double jday;
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
214 nmonth += SynRef;
216 // Get Julian days add time too
217 jday = NewMoon(nmonth) + *day;
219 // Round-up
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
226 if ( *year <= 0 ) {
227 *day = -1;
228 *month = -1;
229 *year = -1;
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)
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);
250 } else {
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;
265 if (*pnMonth > 12)
266 (*pnMonth) -= 12;
268 /* ...and similarly for the year */
269 *pnYear = lFactorC - 4715;
270 if (*pnMonth > 2)
271 (*pnYear)--;
273 // Negative year adjustments
274 if (*pnYear <= 0)
275 (*pnYear)--;
278 sal_Int32
279 Calendar_hijri::getJulianDay(sal_Int32 day, sal_Int32 month, sal_Int32 year)
281 double jy, jm;
283 if( year == 0 ) {
284 return -1;
287 if( year == 1582 && month == 10 && day > 4 && day < 15 ) {
288 return -1;
291 if( month > 2 ) {
292 jy = year;
293 jm = month + 1;
294 } else {
295 jy = year - 1;
296 jm = month + 13;
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 ) {
305 double ja;
306 ja = std::trunc(0.01 * jy);
307 intgr += static_cast<sal_Int32>(2 - ja + std::trunc(0.25 * ja));
310 return intgr;
315 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */