blob: f8570214696132e5759f23451307311330ea8328 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_i18npool.hxx"
#include <stdlib.h>
#include <math.h>
#include "calendar_hijri.hxx"
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::i18n;
using namespace ::rtl;
#define ERROR RuntimeException()
#define GREGORIAN_CROSSOVER 2299161
// not used
//static UErrorCode status; // status is shared in all calls to Calendar, it has to be reset for each call.
// radians per degree (pi/180)
const double Calendar_hijri::RadPerDeg = 0.01745329251994329577;
// Synodic Period (mean time between 2 successive new moon: 29d, 12 hr, 44min, 3sec
const double Calendar_hijri::SynPeriod = 29.53058868;
const double Calendar_hijri::SynMonth = 365.25/29.53058868; // Solar days in a year/SynPeriod
// Julian day on Jan 1, 1900
const double Calendar_hijri::jd1900 = 2415020.75933;
// Reference point: March 26, 2001 == 1422 Hijri == 1252 Synodial month from 1900
const sal_Int32 Calendar_hijri::SynRef = 1252;
const sal_Int32 Calendar_hijri::GregRef = 1422;
// Local time specific to Saudi Arabia
const double Calendar_hijri::SA_TimeZone = 3.0;
const double Calendar_hijri::EveningPeriod = 6.0;
const sal_Int32 Calendar_hijri::LeapYear[] = {
2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29
};
Calendar_hijri::Calendar_hijri()
{
cCalendar = "com.sun.star.i18n.Calendar_hijri";
}
#define FIELDS ((1 << CalendarFieldIndex::ERA) | (1 << CalendarFieldIndex::YEAR) | (1 << CalendarFieldIndex::MONTH) | (1 << CalendarFieldIndex::DAY_OF_MONTH))
// map field value from hijri calendar to gregorian calendar
void Calendar_hijri::mapToGregorian() throw(RuntimeException)
{
if (fieldSet & FIELDS) {
sal_Int32 day = (sal_Int32)fieldSetValue[CalendarFieldIndex::DAY_OF_MONTH];
sal_Int32 month = (sal_Int32)fieldSetValue[CalendarFieldIndex::MONTH] + 1;
sal_Int32 year = (sal_Int32)fieldSetValue[CalendarFieldIndex::YEAR];
if (fieldSetValue[CalendarFieldIndex::ERA] == 0)
year *= -1;
ToGregorian(&day, &month, &year);
fieldSetValue[CalendarFieldIndex::ERA] = year <= 0 ? 0 : 1;
fieldSetValue[CalendarFieldIndex::MONTH] = sal::static_int_cast<sal_Int16>(month - 1);
fieldSetValue[CalendarFieldIndex::DAY_OF_MONTH] = (sal_Int16) day;
fieldSetValue[CalendarFieldIndex::YEAR] = (sal_Int16) abs(year);
fieldSet |= FIELDS;
}
}
// map field value from gregorian calendar to hijri calendar
void Calendar_hijri::mapFromGregorian() throw(RuntimeException)
{
sal_Int32 month, day, year;
day = (sal_Int32)fieldValue[CalendarFieldIndex::DAY_OF_MONTH];
month = (sal_Int32)fieldValue[CalendarFieldIndex::MONTH] + 1;
year = (sal_Int32)fieldValue[CalendarFieldIndex::YEAR];
if (fieldValue[CalendarFieldIndex::ERA] == 0)
year *= -1;
// Get Hijri date
getHijri(&day, &month, &year);
fieldValue[CalendarFieldIndex::DAY_OF_MONTH] = (sal_Int16)day;
fieldValue[CalendarFieldIndex::MONTH] = sal::static_int_cast<sal_Int16>(month - 1);
fieldValue[CalendarFieldIndex::YEAR] = (sal_Int16) abs(year);
fieldValue[CalendarFieldIndex::ERA] = (sal_Int16) year < 1 ? 0 : 1;
}
//
// This function returns the Julian date/time of the Nth new moon since
// January 1900. The synodic month is passed as parameter.
//
// Adapted from "Astronomical Formulae for Calculators" by
// Jean Meeus, Third Edition, Willmann-Bell, 1985.
//
double
Calendar_hijri::NewMoon(sal_Int32 n)
{
double jd, t, t2, t3, k, ma, sa, tf, xtra;
k = n;
t = k/1236.85; // Time in Julian centuries from 1900 January 0.5
t2 = t * t;
t3 = t2 * t;
// Mean time of phase
jd = jd1900
+ SynPeriod * k
- 0.0001178 * t2
- 0.000000155 * t3
+ 0.00033 * sin(RadPerDeg * (166.56 + 132.87 * t - 0.009173 * t2));
// Sun's mean anomaly in radian
sa = RadPerDeg * (359.2242
+ 29.10535608 * k
- 0.0000333 * t2
- 0.00000347 * t3);
// Moon's mean anomaly
ma = RadPerDeg * (306.0253
+ 385.81691806 * k
+ 0.0107306 * t2
+ 0.00001236 * t3);
// Moon's argument of latitude
tf = RadPerDeg * 2.0 * (21.2964
+ 390.67050646 * k
- 0.0016528 * t2
- 0.00000239 * t3);
// should reduce to interval between 0 to 1.0 before calculating further
// Corrections for New Moon
xtra = (0.1734 - 0.000393 * t) * sin(sa)
+ 0.0021 * sin(sa * 2)
- 0.4068 * sin(ma)
+ 0.0161 * sin(2 * ma)
- 0.0004 * sin(3 * ma)
+ 0.0104 * sin(tf)
- 0.0051 * sin(sa + ma)
- 0.0074 * sin(sa - ma)
+ 0.0004 * sin(tf + sa)
- 0.0004 * sin(tf - sa)
- 0.0006 * sin(tf + ma)
+ 0.0010 * sin(tf - ma)
+ 0.0005 * sin(sa + 2 * ma);
// convert from Ephemeris Time (ET) to (approximate) Universal Time (UT)
jd += xtra - (0.41 + 1.2053 * t + 0.4992 * t2)/1440;
return (jd);
}
// Get Hijri Date
void
Calendar_hijri::getHijri(sal_Int32 *day, sal_Int32 *month, sal_Int32 *year)
{
double prevday;
// double dayfraction;
sal_Int32 syndiff;
sal_Int32 newsyn;
double newjd;
double julday;
sal_Int32 synmonth;
// Get Julian Day from Gregorian
julday = getJulianDay(*day, *month, *year);
// obtain approx. of how many Synodic months since the beginning of the year 1900
synmonth = (sal_Int32)(0.5 + (julday - jd1900)/SynPeriod);
newsyn = synmonth;
prevday = (sal_Int32)julday - 0.5;
do {
newjd = NewMoon(newsyn);
// Decrement syndonic months
newsyn--;
} while (newjd > prevday);
newsyn++;
// difference from reference point
syndiff = newsyn - SynRef;
// Round up the day
*day = (sal_Int32)(((sal_Int32)julday) - newjd + 0.5);
*month = (syndiff % 12) + 1;
// currently not supported
//dayOfYear = (sal_Int32)(month * SynPeriod + day);
*year = GregRef + (sal_Int32)(syndiff / 12);
// If month negative, consider it previous year
if (syndiff != 0 && *month <= 0) {
*month += 12;
(*year)--;
}
// If Before Hijri subtract 1
if (*year <= 0) (*year)--;
}
void
Calendar_hijri::ToGregorian(sal_Int32 *day, sal_Int32 *month, sal_Int32 *year)
{
sal_Int32 nmonth;
// double dayfraction;
double jday;
// sal_Int32 dayint;
if ( *year < 0 ) (*year)++;
// Number of month from reference point
nmonth = *month + *year * 12 - (GregRef * 12 + 1);
// Add Synodic Reference point
nmonth += SynRef;
// Get Julian days add time too
jday = NewMoon(nmonth) + *day;
// Round-up
jday = (double)((sal_Int32)(jday + 0.5));
// Use algorithm from "Numerical Recipes in C"
getGregorianDay((sal_Int32)jday, day, month, year);
// Julian -> Gregorian only works for non-negative year
if ( *year <= 0 ) {
*day = -1;
*month = -1;
*year = -1;
}
}
/* this algorithm is taken from "Numerical Recipes in C", 2nd ed, pp 14-15. */
/* this algorithm only valid for non-negative gregorian year */
void
Calendar_hijri::getGregorianDay(sal_Int32 lJulianDay, sal_Int32 *pnDay, sal_Int32 *pnMonth, sal_Int32 *pnYear)
{
/* working variables */
long lFactorA, lFactorB, lFactorC, lFactorD, lFactorE;
long lAdjust;
/* test whether to adjust for the Gregorian calendar crossover */
if (lJulianDay >= GREGORIAN_CROSSOVER) {
/* calculate a small adjustment */
lAdjust = (long) (((float) (lJulianDay - 1867216) - 0.25) / 36524.25);
lFactorA = lJulianDay + 1 + lAdjust - ((long) (0.25 * lAdjust));
} else {
/* no adjustment needed */
lFactorA = lJulianDay;
}
lFactorB = lFactorA + 1524;
lFactorC = (long) (6680.0 + ((float) (lFactorB - 2439870) - 122.1) / 365.25);
lFactorD = (long) (365 * lFactorC + (0.25 * lFactorC));
lFactorE = (long) ((lFactorB - lFactorD) / 30.6001);
/* now, pull out the day number */
*pnDay = lFactorB - lFactorD - (long) (30.6001 * lFactorE);
/* ...and the month, adjusting it if necessary */
*pnMonth = lFactorE - 1;
if (*pnMonth > 12)
(*pnMonth) -= 12;
/* ...and similarly for the year */
*pnYear = lFactorC - 4715;
if (*pnMonth > 2)
(*pnYear)--;
// Negative year adjustments
if (*pnYear <= 0)
(*pnYear)--;
}
double
Calendar_hijri::getJulianDay(sal_Int32 day, sal_Int32 month, sal_Int32 year)
{
double jy, jm;
if( year == 0 ) {
return -1.0;
}
if( year == 1582 && month == 10 && day > 4 && day < 15 ) {
return -1.0;
}
if( month > 2 ) {
jy = year;
jm = month + 1;
} else {
jy = year - 1;
jm = month + 13;
}
sal_Int32 intgr = (sal_Int32)((sal_Int32)(365.25 * jy) + (sal_Int32)(30.6001 * jm) + day + 1720995 );
//check for switch to Gregorian calendar
double gregcal = 15 + 31 * ( 10 + 12 * 1582 );
if( day + 31 * (month + 12 * year) >= gregcal ) {
double ja;
ja = (sal_Int32)(0.01 * jy);
intgr += (sal_Int32)(2 - ja + (sal_Int32)(0.25 * ja));
}
return (double) intgr;
}