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