Bump version to 6.0-36
[LibreOffice.git] / i18npool / source / calendar / calendar_hijri.cxx
blobaab9088d930cbad549b186b632f7037606966c29
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <stdlib.h>
22 #include <math.h>
24 #include <android/compatibility.hxx>
26 #include <calendar_hijri.hxx>
28 using namespace ::com::sun::star::uno;
29 using namespace ::com::sun::star::lang;
30 using namespace ::com::sun::star::i18n;
32 #define GREGORIAN_CROSSOVER 2299161
34 namespace i18npool {
36 // not used
37 //static UErrorCode status; // status is shared in all calls to Calendar, it has to be reset for each call.
39 // radians per degree (pi/180)
40 const double Calendar_hijri::RadPerDeg = 0.01745329251994329577;
42 // Synodic Period (mean time between 2 successive new moon: 29d, 12 hr, 44min, 3sec
43 const double Calendar_hijri::SynPeriod = 29.53058868;
44 const double Calendar_hijri::SynMonth = 365.25/29.53058868; // Solar days in a year/SynPeriod
46 // Julian day on Jan 1, 1900
47 const double Calendar_hijri::jd1900 = 2415020.75933;
49 // Reference point: March 26, 2001 == 1422 Hijri == 1252 Synodial month from 1900
50 const sal_Int32 Calendar_hijri::SynRef = 1252;
51 const sal_Int32 Calendar_hijri::GregRef = 1422;
53 // Local time specific to Saudi Arabia
54 const double Calendar_hijri::SA_TimeZone = 3.0;
56 const double Calendar_hijri::EveningPeriod = 6.0;
58 const sal_Int32 Calendar_hijri::LeapYear[] = {
59 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29
62 Calendar_hijri::Calendar_hijri()
64 cCalendar = "com.sun.star.i18n.Calendar_hijri";
67 #define FIELDS ((1 << CalendarFieldIndex::ERA) | (1 << CalendarFieldIndex::YEAR) | (1 << CalendarFieldIndex::MONTH) | (1 << CalendarFieldIndex::DAY_OF_MONTH))
69 // map field value from hijri calendar to gregorian calendar
70 void Calendar_hijri::mapToGregorian()
72 if (fieldSet & FIELDS) {
73 sal_Int32 day = (sal_Int32)fieldSetValue[CalendarFieldIndex::DAY_OF_MONTH];
74 sal_Int32 month = (sal_Int32)fieldSetValue[CalendarFieldIndex::MONTH] + 1;
75 sal_Int32 year = (sal_Int32)fieldSetValue[CalendarFieldIndex::YEAR];
76 if (fieldSetValue[CalendarFieldIndex::ERA] == 0)
77 year *= -1;
79 ToGregorian(&day, &month, &year);
81 fieldSetValue[CalendarFieldIndex::ERA] = year <= 0 ? 0 : 1;
82 fieldSetValue[CalendarFieldIndex::MONTH] = sal::static_int_cast<sal_Int16>(month - 1);
83 fieldSetValue[CalendarFieldIndex::DAY_OF_MONTH] = (sal_Int16) day;
84 fieldSetValue[CalendarFieldIndex::YEAR] = (sal_Int16) abs(year);
85 fieldSet |= FIELDS;
89 // map field value from gregorian calendar to hijri calendar
90 void Calendar_hijri::mapFromGregorian()
92 sal_Int32 month, day, year;
94 day = (sal_Int32)fieldValue[CalendarFieldIndex::DAY_OF_MONTH];
95 month = (sal_Int32)fieldValue[CalendarFieldIndex::MONTH] + 1;
96 year = (sal_Int32)fieldValue[CalendarFieldIndex::YEAR];
97 if (fieldValue[CalendarFieldIndex::ERA] == 0)
98 year *= -1;
100 // Get Hijri date
101 getHijri(&day, &month, &year);
103 fieldValue[CalendarFieldIndex::DAY_OF_MONTH] = (sal_Int16)day;
104 fieldValue[CalendarFieldIndex::MONTH] = sal::static_int_cast<sal_Int16>(month - 1);
105 fieldValue[CalendarFieldIndex::YEAR] = (sal_Int16) abs(year);
106 fieldValue[CalendarFieldIndex::ERA] = (sal_Int16) year < 1 ? 0 : 1;
110 // This function returns the Julian date/time of the Nth new moon since
111 // January 1900. The synodic month is passed as parameter.
113 // Adapted from "Astronomical Formulae for Calculators" by
114 // Jean Meeus, Third Edition, Willmann-Bell, 1985.
116 double
117 Calendar_hijri::NewMoon(sal_Int32 n)
119 double jd, t, t2, t3, k, ma, sa, tf, xtra;
120 k = n;
121 t = k/1236.85; // Time in Julian centuries from 1900 January 0.5
122 t2 = t * t;
123 t3 = t2 * t;
125 // Mean time of phase
126 jd = jd1900
127 + SynPeriod * k
128 - 0.0001178 * t2
129 - 0.000000155 * t3
130 + 0.00033 * sin(RadPerDeg * (166.56 + 132.87 * t - 0.009173 * t2));
132 // Sun's mean anomaly in radian
133 sa = RadPerDeg * (359.2242
134 + 29.10535608 * k
135 - 0.0000333 * t2
136 - 0.00000347 * t3);
138 // Moon's mean anomaly
139 ma = RadPerDeg * (306.0253
140 + 385.81691806 * k
141 + 0.0107306 * t2
142 + 0.00001236 * t3);
144 // Moon's argument of latitude
145 tf = RadPerDeg * 2.0 * (21.2964
146 + 390.67050646 * k
147 - 0.0016528 * t2
148 - 0.00000239 * t3);
150 // should reduce to interval between 0 to 1.0 before calculating further
151 // Corrections for New Moon
152 xtra = (0.1734 - 0.000393 * t) * sin(sa)
153 + 0.0021 * sin(sa * 2)
154 - 0.4068 * sin(ma)
155 + 0.0161 * sin(2 * ma)
156 - 0.0004 * sin(3 * ma)
157 + 0.0104 * sin(tf)
158 - 0.0051 * sin(sa + ma)
159 - 0.0074 * sin(sa - ma)
160 + 0.0004 * sin(tf + sa)
161 - 0.0004 * sin(tf - sa)
162 - 0.0006 * sin(tf + ma)
163 + 0.0010 * sin(tf - ma)
164 + 0.0005 * sin(sa + 2 * ma);
166 // convert from Ephemeris Time (ET) to (approximate) Universal Time (UT)
167 jd += xtra - (0.41 + 1.2053 * t + 0.4992 * t2)/1440;
169 return jd;
172 // Get Hijri Date
173 void
174 Calendar_hijri::getHijri(sal_Int32 *day, sal_Int32 *month, sal_Int32 *year)
176 double prevday;
177 // double dayfraction;
178 sal_Int32 syndiff;
179 sal_Int32 newsyn;
180 double newjd;
181 double julday;
182 sal_Int32 synmonth;
184 // Get Julian Day from Gregorian
185 julday = getJulianDay(*day, *month, *year);
187 // obtain approx. of how many Synodic months since the beginning of the year 1900
188 synmonth = (sal_Int32)(0.5 + (julday - jd1900)/SynPeriod);
190 newsyn = synmonth;
191 prevday = (sal_Int32)julday - 0.5;
193 do {
194 newjd = NewMoon(newsyn);
196 // Decrement syntonic months
197 newsyn--;
198 } while (newjd > prevday);
199 newsyn++;
201 // difference from reference point
202 syndiff = newsyn - SynRef;
204 // Round up the day
205 *day = (sal_Int32)(((sal_Int32)julday) - newjd + 0.5);
206 *month = (syndiff % 12) + 1;
208 // currently not supported
209 //dayOfYear = (sal_Int32)(month * SynPeriod + day);
210 *year = GregRef + (sal_Int32)(syndiff / 12);
212 // If month negative, consider it previous year
213 if (syndiff != 0 && *month <= 0) {
214 *month += 12;
215 (*year)--;
218 // If Before Hijri subtract 1
219 if (*year <= 0) (*year)--;
222 void
223 Calendar_hijri::ToGregorian(sal_Int32 *day, sal_Int32 *month, sal_Int32 *year)
225 sal_Int32 nmonth;
226 // double dayfraction;
227 double jday;
228 // sal_Int32 dayint;
230 if ( *year < 0 ) (*year)++;
232 // Number of month from reference point
233 nmonth = *month + *year * 12 - (GregRef * 12 + 1);
235 // Add Synodic Reference point
236 nmonth += SynRef;
238 // Get Julian days add time too
239 jday = NewMoon(nmonth) + *day;
241 // Round-up
242 jday = (double)((sal_Int32)(jday + 0.5));
244 // Use algorithm from "Numerical Recipes in C"
245 getGregorianDay((sal_Int32)jday, day, month, year);
247 // Julian -> Gregorian only works for non-negative year
248 if ( *year <= 0 ) {
249 *day = -1;
250 *month = -1;
251 *year = -1;
255 /* this algorithm is taken from "Numerical Recipes in C", 2nd ed, pp 14-15. */
256 /* this algorithm only valid for non-negative gregorian year */
257 void
258 Calendar_hijri::getGregorianDay(sal_Int32 lJulianDay, sal_Int32 *pnDay, sal_Int32 *pnMonth, sal_Int32 *pnYear)
260 /* working variables */
261 long lFactorA, lFactorB, lFactorC, lFactorD, lFactorE;
263 /* test whether to adjust for the Gregorian calendar crossover */
264 if (lJulianDay >= GREGORIAN_CROSSOVER) {
265 /* calculate a small adjustment */
266 long lAdjust = (long) (((float) (lJulianDay - 1867216) - 0.25) / 36524.25);
268 lFactorA = lJulianDay + 1 + lAdjust - ((long) (0.25 * lAdjust));
270 } else {
271 /* no adjustment needed */
272 lFactorA = lJulianDay;
275 lFactorB = lFactorA + 1524;
276 lFactorC = (long) (6680.0 + ((float) (lFactorB - 2439870) - 122.1) / 365.25);
277 lFactorD = (long) (365 * lFactorC + (0.25 * lFactorC));
278 lFactorE = (long) ((lFactorB - lFactorD) / 30.6001);
280 /* now, pull out the day number */
281 *pnDay = lFactorB - lFactorD - (long) (30.6001 * lFactorE);
283 /* ...and the month, adjusting it if necessary */
284 *pnMonth = lFactorE - 1;
285 if (*pnMonth > 12)
286 (*pnMonth) -= 12;
288 /* ...and similarly for the year */
289 *pnYear = lFactorC - 4715;
290 if (*pnMonth > 2)
291 (*pnYear)--;
293 // Negative year adjustments
294 if (*pnYear <= 0)
295 (*pnYear)--;
298 double
299 Calendar_hijri::getJulianDay(sal_Int32 day, sal_Int32 month, sal_Int32 year)
301 double jy, jm;
303 if( year == 0 ) {
304 return -1.0;
307 if( year == 1582 && month == 10 && day > 4 && day < 15 ) {
308 return -1.0;
311 if( month > 2 ) {
312 jy = year;
313 jm = month + 1;
314 } else {
315 jy = year - 1;
316 jm = month + 13;
319 sal_Int32 intgr = (sal_Int32)((sal_Int32)(365.25 * jy) + (sal_Int32)(30.6001 * jm) + day + 1720995 );
321 //check for switch to Gregorian calendar
322 double const gregcal = 15 + 31 * ( 10 + 12 * 1582 );
324 if( day + 31 * (month + 12 * year) >= gregcal ) {
325 double ja;
326 ja = (sal_Int32)(0.01 * jy);
327 intgr += (sal_Int32)(2 - ja + (sal_Int32)(0.25 * ja));
330 return (double) intgr;
335 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */