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