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