1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: calendar_hijri.cxx,v $
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"
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
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)
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
);
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)
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.
129 Calendar_hijri::NewMoon(sal_Int32 n
)
131 double jd
, t
, t2
, t3
, k
, ma
, sa
, tf
, xtra
;
133 t
= k
/1236.85; // Time in Julian centuries from 1900 January 0.5
137 // Mean time of phase
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
150 // Moon's mean anomaly
151 ma
= RadPerDeg
* (306.0253
156 // Moon's argument of latitude
157 tf
= RadPerDeg
* 2.0 * (21.2964
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)
167 + 0.0161 * sin(2 * ma
)
168 - 0.0004 * sin(3 * ma
)
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;
186 Calendar_hijri::getHijri(sal_Int32
*day
, sal_Int32
*month
, sal_Int32
*year
)
189 // double dayfraction;
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
);
203 prevday
= (sal_Int32
)julday
- 0.5;
206 newjd
= NewMoon(newsyn
);
208 // Decrement syndonic months
210 } while (newjd
> prevday
);
213 // difference from reference point
214 syndiff
= newsyn
- SynRef
;
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) {
230 // If Before Hijri subtract 1
231 if (*year
<= 0) (*year
)--;
235 Calendar_hijri::ToGregorian(sal_Int32
*day
, sal_Int32
*month
, sal_Int32
*year
)
238 // double dayfraction;
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
250 // Get Julian days add time too
251 jday
= NewMoon(nmonth
) + *day
;
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
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 */
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
;
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
));
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;
301 /* ...and similarly for the year */
302 *pnYear
= lFactorC
- 4715;
306 // Negative year adjustments
312 Calendar_hijri::getJulianDay(sal_Int32 day
, sal_Int32 month
, sal_Int32 year
)
320 if( year
== 1582 && month
== 10 && day
> 4 && day
< 15 ) {
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
) {
339 ja
= (sal_Int32
)(0.01 * jy
);
340 intgr
+= (sal_Int32
)(2 - ja
+ (sal_Int32
)(0.25 * ja
));
343 return (double) intgr
;