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