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