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