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