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