/* TimeUtilities.c
* *
* Emilio 2/2/2001
* Added GetDaysInMonth function. Added local array nDaysPerMonthLocal.
*
* Functions used to make calendar format conversions, particularly to convert
* among Julian Day, Gregorian Calendar, Solar Day, and Day of the Week.
* Leap Years and other subtleties are taken into account.
* Code taken from TJ Saunders' code, originally adapted in large part from
* "Numerical Recipes in C", 2nd ed, pp. 11-12.
******************************************************************************/
#include "emu.h"
/* array used locally, so it doesn't conflict or modify the global array *
* nDaysPerMonth from unitconv.h */
static int nDaysPerMonthLocal[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/* ------------------------------------------------------------------------------ */
void GregorianToJulianDay(int nMonth, int nDay, int nYear, long *plJulianDay)
{
/* this algorithm is based on that found in "Numerical Recipes in C", 2nd ed, */
/* pp. 11-12. */
/* local (as in local to this function) working variables */
int nLocalMonth;
int nLocalYear;
/* working variable */
int nAdjust;
/* capture the input values */
nLocalMonth = nMonth;
nLocalYear = nYear;
if (nYear == 0) {
/* display an error */
Warning("GregorianToJulianDay()", "no year 0 AD");
return;
}
if (nYear < 0)
/* add one to an AD year for the "Scalinger Year" */
nLocalYear++;
if (nMonth > 2) {
/* increment the month if is March or later */
nLocalMonth = nMonth + 1;
} else {
/* decrement the year into the previous Julian cycle */
nLocalYear--;
/* place January and February into the previous Julian cycle */
nLocalMonth = nMonth + 13;
}
*plJulianDay = (long) (floor(365.25 * nLocalYear) + floor(30.6001 * nLocalMonth) +
nDay + 1720995);
/* test whether to adjust the day for the Gregorian calendar or not */
if ((nDay + 31L * (nMonth + 12L * nYear)) >= GREGORIAN_START) {
/* a small adjustment factor */
nAdjust = (int) (0.01 * nLocalYear);
*plJulianDay += (2 - nAdjust + (int) (0.25 * nAdjust));
}
/* done */
return;
}
/* ------------------------------------------------------------------------------ */
void GregorianToSolarDay(int nMonth, int nDay, int nYear, int *pnSolarDay)
{
/* a loop variable */
int nMonthIndex;
/* working variables */
int nDays = 0;
/* first, adjust the number of days in February, if necessary */
nDaysPerMonthLocal[1] = (IsLeapYear(nYear)) ? 29 : 28;
/* loop through the number of months, summing up the number of days so far */
/* use the input month minus one as the loop limit to allow for the number of days */
/* for the next month */
for (nMonthIndex = 0; nMonthIndex < (nMonth - 1); nMonthIndex++)
nDays += nDaysPerMonthLocal[nMonthIndex];
/* add the day of the month */
nDays += nDay;
/* and that's your Solar day, 1-based */
*pnSolarDay = nDays;
/* done */
return;
}
/* ------------------------------------------------------------------------------ */
void JulianToGregorianDay(long lJulianDay, int *pnMonth, int *pnDay, int *pnYear) {
/* this algorithm is taken from "Numerical Recipes in C", 2nd ed, pp 14-15. */
/* 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;
/* what follows is a lot of numerical hocus-pocus that I can't follow...I'm just */
/* copying it right out of "Numerical Recipes" */
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)--;
if (*pnYear <= 0)
(*pnYear)--;
/* done */
return;
}
/* ------------------------------------------------------------------------------ */
void JulianToSolarDay(long lJulianDay, int *pnSolarDay) {
/* convert the input Julian day to a Gregorian day, then call GregorianToSolarDay() */
/* variables for the Gregorian day values */
int nMonth, nDay, nYear;
/* convert to Gregorian dates */
JulianToGregorianDay(lJulianDay, &nMonth, &nDay, &nYear);
/* now get the Solar day */
GregorianToSolarDay(nMonth, nDay, nYear, pnSolarDay);
/* done */
return;
}
/* ------------------------------------------------------------------------------ */
int GetDayOfWeekIndex(long lJulianDay) {
/* returns the day of the week given the input Julian day. The output ranges */
/* from 0 to 7, with 0 being Monday and 6 being Sunday */
return (int) (lJulianDay % 7L);
}
/* ------------------------------------------------------------------------------ */
void GetDayOfWeekName(long lJulianDay, char *pszDayOfWeek) {
/* return a string with the name of the day of the week for the given Julian */
/* Day */
switch (GetDayOfWeekIndex(lJulianDay)) {
case 0: /* Monday */
sprintf(pszDayOfWeek, "Monday");
break;
case 1: /* Tuesday */
sprintf(pszDayOfWeek, "Tuesday");
break;
case 2: /* Wednesday */
sprintf(pszDayOfWeek, "Wednesday");
break;
case 3: /* Thursday */
sprintf(pszDayOfWeek, "Thursday");
break;
case 4: /* Friday */
sprintf(pszDayOfWeek, "Friday");
break;
case 5: /* Saturday */
sprintf(pszDayOfWeek, "Saturday");
break;
case 6: /* Sunday */
sprintf(pszDayOfWeek, "Sunday");
break;
default:
sprintf(pszDayOfWeek, "error -- unknown day of the week");
}
/* done */
return;
}
/* ------------------------------------------------------------------------------ */
/* return the number of days of the specified months (base 1), accounting for *
* Leap years */
short GetDaysInMonth(short month, int nYear)
{
/* account for leap years */
nDaysPerMonthLocal[1] = (IsLeapYear(nYear)) ? 29 : 28;
return((short)nDaysPerMonthLocal[month-1]);
}
/* ------------------------------------------------------------------------------ */
BOOL IsLeapYear(int nYear) {
/* the result */
BOOL bIsLeapYear;
if ((nYear % 4 == 0) &&
(nYear % 100 != 0 || nYear % 400 == 0))
bIsLeapYear = TRUE;
else
bIsLeapYear = FALSE;
/* return the result */
return bIsLeapYear;
}
/* ------------------------------------------------------------------------------ */
void SolarToGregorianDay(int nSolarDay, int nYear, int *pnMonth, int *pnDay) {
/* convert the given Solar day (1-based) and year to a month and day */
/* a loop variable */
int nMonthIndex;
/* working variables */
int nDaysRemaining = nSolarDay;
int nDay = 0;
/* determine if it's a leap year */
nDaysPerMonthLocal[1] = (IsLeapYear(nYear)) ? 29 : 28;
/* loop through the months, subtracting the number of days per month from the */
/* the given Solar day */
for (nMonthIndex = 0; nMonthIndex < YEAR2MONTH; nMonthIndex++) {
nDaysRemaining -= nDaysPerMonthLocal[nMonthIndex];
/* stop subtracting */
if (nDaysRemaining <= 0)
break;
}
/* the month index pointer (plus a small adjustment) should now be the month */
*pnMonth = nMonthIndex + 1;
/* add the number of days for the month back into the remaining days variable */
/* to get the day of the month */
*pnDay = nDaysRemaining + nDaysPerMonthLocal[nMonthIndex];
/* done */
return;
}
/* ------------------------------------------------------------------------------ */
void SolarToJulianDay(int nSolarDay, int nYear, long *plJulianDay) {
/* convert the given Solar day and year to a Gregorian day, then call */
/* GregorianToJulianDay() */
/* working variables */
int nMonth, nDay;
SolarToGregorianDay(nSolarDay, nYear, &nMonth, &nDay);
GregorianToJulianDay(nMonth, nDay, nYear, plJulianDay);
/* done */
return;
}