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