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