/*
** dates.cpp -- date object methods
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <dos.h>
#include <string.h>
#include "dates.hpp"
static char ShortMonths[MAXMONTH][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static char LongMonths[MAXMONTH][10] = {
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
static unsigned MonthDays[MAXMONTH] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static char ShortWeekDays[MAXWEEKDAY+1][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static char LongWeekDays[MAXWEEKDAY+1] [10] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
static const char *CurrentDateFormat = "m-dd-yyy";
static int OnHeap = 0; /* CurrentDateFormat doesn't point to heap */
/*
** ChangeDefaultDateFormat -- change the format string used to
** initialize the DateFormatPtr. Return 1 on success or 0 if
** memory could not be allocated for the new string.
*/
int ChangeDefaultDateFormat(const char *s)
{
char *t;
if ((t = strdup(s)) == NULL)
return (0); /* return failure */
if (OnHeap)
free(CurrentDateFormat); /* release old string */
else
OnHeap = 1; /* now we're allocating on heap */
CurrentDateForamt = t; /* point to new string */
return (1);
}
/*
** IsLeap -- return non-zero if Year is a leap year, 0 otherwise.
*/
static int IsLeap(int Year)
{
return (Year % 4 == 0) && (Year % 4000 != 0) &&
((Year % 100 != 0) || (Year % 400 == 0));
}
/*
** DaysInMonth -- return the number of days in Month. Year
** is passed to test for a leap year if the month is February.
*/
static unsigned char DaysInMonth(unsigned char Month, int Year)
{
if ((Month == 2) && IsLeap(Year))
return (29);
else
return (MonthDays[Month - 1]);
}
/*
** CheckForValidDate -- check that Day, Month and Year
** represent a valid date. Return non-zero if so, 0 otherwise.
*/
static int CheckForValidDate(unsigned char Day, unsigned char Month, int Year)
{
if ((Year < MINYEAR) || (Year > MAXYEAR) || (Year == 0) ||
(Month < MINMONTH) || (Month > MAXMONTH))
return (0);
return (Day <= DaysInMonth(Month, Year));
}
/*
** JulianToDayOfWeek -- given a Julian date, return the
** day of the week for that date, 0 being Sunday, 6 being
** Saturday.
*/
static unsigned char JulianToDayOfWeek(long Jul)
{
return ((Jul + 1) % 7);
}
/*
** DMYtoJulian -- after validating the date passed as a Day,
** Month and Year, convert it to a Julian date.
**
** The algorithm shown here is swiped directly from "Numerical
** Recipes in C" by Press, Flannery, Teukolsky and Vettering, p. 10.
*/
#define IGREG (15+31L*(10+12L*1582))
static long DMYtoJulian(unsigned char Day, unsigned char Month, int Year)
{
unsigned long Ja, Jm, Jy, Jul;
if (!CheckForValidDate(Day, Month, Year))
return (BADDATE);
if (Year < 0)
Year++;
if (Month > 2)
{
Jy = Year;
Jm = Month + 1;
}
else
{
Jy = Year - 1;
Jm = Month + 13;
}
Jul = (long) (floor(365.25*Jy) + floor(30.6001*Jm) + Day + 1720995);
if (Day + 31L*(Month + 12L*Year) >= IGREG)
{
Ja = 0.01*Jy;
Jul += 2 - Ja + (int) (0.25*Ja);
}
return (Jul);
}
/* JulianToDMY -- convert a Julian date to the appropriate
** Day, Month and Year.
**
** Also swiped from "Numerical Recipes".
*/
#define GREGOR 2299161
static void JulianToDMY(long Jul, unsigned char *Day, unsigned char *Month,
int *Year)
{
long Ja, JAlpha, Jb, Jc, Jd, Je;
if ((Jul != BADDATE) && (Jul >= MINDATE) && (Jul <= MAXDATE))
{
if (Jul >= GREGOR)
{
JAlpha = ((double) (Jul - 1867216) - 0.25)/36524.25;
Ja = Jul + 1 + JAlpha - (long) (0.25*JAlpha);
}
else
Ja = Jul;
Jb = Ja + 1524;
Jc = 6680.0 + ((double) (Jb - 2439870) - 122.1)/365.25;
Jd = 365*Jc + (0.25*Jc);
Je = (Jb - Jd)/30.6001;
*Day = Jb - Jd - (int) (30.6001*Je);
*Month = Je - 1;
if (*Month > 12)
*Month -= 12;
*Year = Jc - 4715;
if (*Month > 2)
--(*Year);
if (Year <= 0)
--(*Year);
}
}
/*
** ChangeDate -- change a date to reflect the new date passed
** in the arguments. If the requested date is not a legal date,
** return a value of 0 without making any changes. If legal,
** return 1. The date format string is not affected.
*/
int DateObJect::ChangeDate(unsigned char NDay, unsigned char NMonth, int NYear)
{
long t;
t - DMYtoJulian(NDay, NMonth, NYear);
if (t != BADDATE)
{
Julian = t;
Day = NDay;
Month = NMonth;
Year = NYear;
DayOfWeek = JulianToDayOfWeek(Julian);
return (1);
}
return (0);
}
/*
** DateObject -- constructor if no args given. Just initialize
** to todays date.
*/
DateObject::DateObject(void)
{
union REGS regs;
regs.x.ax = 0x2a00; /* DOS get date service */
intdos(®s, ®s);
Day = regs.h.dl;
Month = regs.h.dh;
Year = regs.x.cx;
DayOfWeek = regs.h.al;
Julian = DMYtoJulian(Day, Month, Year);
DateFormatPtr = strdup(CurrentDateFormat);
if dateFormatPtr == NULL)
Julian = BADDATE;
}
/*
**DateObject -- copy initializer constructor
*/
DateObject::DateObject(DateObject &OtherDate)
{
Day = OtherDate.Day;
Month = OtherDate.Month;
Year = OtherDate.Year;
DayOfWeek = OtherDate.DayOfWeek;
Julian = OtherDate.Julian;
DateFormatPtr = strdup(OtherDate.DateFormatPtr);
if (DateFormatPtr == NULL)
Julian = BADDATE;
}
/*
** DateObject -- constructor when day, month and year initializers
** are provided. The default format string is used.
*/
DateObject::DateObject(unsigned char InitDay, unsigned char InitMonth,
int InitYear)
{
ChangeDate(InitDay, InitMonth, InitYear);
If (Julian != BADDATE)
{
DateFormatPtr = strdup(CurrentDateFormat);
if (DateFormatPtr == NULL)
Julian = BADDATE;
}
}
}
/*
** DateObject -- constructor used when day, month, year and a
** format string initializer are provided.
*/
DateObject::DateObject(unsigned char InitDay, unsigned char InitMonth,
int InitYear, const char *FormatStr)
{
ChangeDate(InitDay, InitMonth, InitYear);
if (Julian != BADDATE)
{
DateFormatPtr = strdup(FormatStr);
if (DateFormatPtr == NULL)
Julian = BADDATE;
}
}
/*
** = -- assignment operator.
*/
DateObject DateObject::operator = (DateObject &d)
{
Day = d. Day;
Month = d.Month;
Year = d.Year;
Julian = d. Julian;
DayOfWeek = d. DayOfWeek;
DateFormatPtr = strdup(d.DateFormatPtr);
if (DateFormatPtr == NULL)
Julian = BADDATE;
return (*this);
}
/*
** + -- addition operator. This is the fundamental operator
** function. All other arithmetic operators returning
** DateObjects call this function. The resulting DateObject
** copies that format string of the DateObject argument.
*/
DateObject operator + (DateObject &d, long x)
{
DateObject sum;
sum.Julian = d.Julian + x;
if ((sum.Julian < MINDATE) || (sum.Julian > MAXBATE))
sum.Julian = BADDATE;
else
{
JulianToDMY(sum.Julian, &(sum.Day), &(sum.Month), &(sum.Year));
sum.DayOfWeek = JulianToDayOfWeek(sum.Julian);
sum.DateFormatPtr = strdup(d.DateFormatPtr);
if (sum.DateFormatPtr == NULL)
sum.Julian = BADDATE;
}
return (sum);
}
DateObject operator + (long x, DateObject &d)
{
return (d + x);
}
DateObject DateObject::operator - (long x)
{
return (*this + (-x));
}
DateObject DateObject::operator ++ (void)
{
return ((*this) = (*this) + 1L);
}
DateObject DateObject::operator -- (void)
{
return ((*this) = (*this) + (-1L));
}
DateObject DateObject::operator += (long x)
{
return ((*this) = (*this) + x);
}
DateObject DateObject::operator -= (long x)
{
return ((*this) =(*this) + (-x));
}
/*
** GetFormat -- return a dynamically allocated
** copy of a date's format string
*/
const char * DateObject::GetFormat()
{
return (strdup(DateFormatPtr));
}
/*
** ChangeFormat -- change the date's format string
*/
void DateObject::ChangeFormat (const char *s)
{
char *t;
if ((t = strdup(s)) != NULL)
{
free(DateFormatPtr);
DateFormatPtr = t;
}
}
/*
** DateObject destructor -- simply release the string space occupied
** by *DateFormatPtr. We do not have to check that the pointer is
** non-NULL, since free accepts NULL pointers and does nothing.
*/
DateObject::~DateObject(void)
{
free (DateFormatPtr);
}
/*
** CountLetters -- count the run of letters in s matching
** the first letter in s. Advance s to point to the next
** letter that does not match the first character. Return
** the count. This is a helper function for DateToString.
*/
static int CountLetters(char **s)
{
int n, c;
for (n = 0, c = **s; **s == c; n++, (*s)++)
;
return (n);
}
/*
** DateToString -- convert Self to a printable string
** based on the contents of the format string, DateFormatPtr.
**
** The format string is interpreted somewhat like date
** formats in Microsoft Excel. Most characters are passed
** through unchanged into the result string. However,
** certain special characters are replaced. The special
** characters and their replacements are:
**
** d Replaced by a one or two digit day number, i.e. '2'.
** dd Replaced by a two digit day number with a leading
** 0 if the day is less than 10, i.e. '02'.
** ddd Replaced by a 3 character name for the day of the
** week, i.e. 'Wed'.
** dddd Replaced by the complete word for the day of the
** week, i.e. 'Wednesday'.
** m Replaced by a one or two digit month number, i.e. '2'.
** mm Replaced by a two digit month number with a leading
** 0 if the month is less than October, i.e. '02'.
** mmm Replaced by a 3 character name for the month, i.e. 'Jan'.
** mmmm Replaced by the complete word for the month, i.e. 'January'.
** yy Replaced by a 2 digit year Modulo 100, i.e. '89'.
** yyy Replaced by a 4 digit year number, i.e. '1989'. BC
** dates are preceded by a '-'.
** yyyy Replaced by a 4 digit string representing the absolute
** value of the year number followed by ' AD' or ' BC',
** as appropriate, i.e. '1989 AD'.
** \ Skipped and places the next character into the string
** without interpration. Allows you to put words in the
** output string, i.e. 'To\da\y is ...' will generate
** a string of the form 'Today is ...'.
*/
char *DateObject::DateToString(void)
{
int c, Len, Pos, Count;
char *str, /* a dynamically allocated work string */
*sptr, /* a moving pointer to the next avail char in str */
*ret, /* pointer to the trimmed string returned by the function */
*fptr; /* a pointer into the format string */
if (Julian == BADDATE)
return (strdup("Bad Date"));
Len = strlen(DateFormatPtr);
Pos = 0;
sptr = str = calloc((MAXDATESTRLEN+1), sizeof(char));
fptr = DateFormatPtr;
while (*fptr)
{
switch (*fptr)
{
case 'd' :
case 'D' :
Count = CountLetters(&fptr);
if (Count >= 4)
{
strcat(sptr, LongWeekDays[DayOfWeek]);
while (*sptr)
sptr++;
}
else if (Count == 3)
{
strcat(sptr, ShortWeekDays[DayOfWeek]);
while (*sptr)
sptr++;
}
else
{
if ((Count == 2} && (Day < 10))
strcat(sptr++, "0");
itoa(Day, sptr, 10);
while (*sptr)
sptr++;
}
break;
case 'm' :
case 'M' :
Count = CountLetters(&fptr);
if (Count >= 4)
{
strcat(sptr, LongMonths[Month-1]);
while (*sptr)
sptr++;
}
else if (Count == 3)
{
strcat(sptr, ShortMonths[Month-1]);
while (*sptr)
sptr++;
}
else
{
if ((Count == 2) && (Month < 10))
strcat(sptr++, "0");
itoa(Month, sptr, 10);
while (*sptr)
sptr++;
}
break;
case 'y' :
case 'Y' :
Count = CountLetters(&fptr);
if (Count >= 4)
{
itoa(Year, sptr, 10);
if (Year < 0) /* overwrite minus sign */
strcpy(sptr, sptr+ 1);
while (*sptr)
sptr++;
if (Year > 0)
strcat(sptr, " AD");
else
strcat(sptr, " BC");
while (*sptr)
sptr++;
}
else
{
if (Count == 2)
{
if ((Year % 100) < 10)
strcat(sptr++, "0");
itoa((Year % 100), sptr, 10);
while (*sptr)
sptr++;
}
else
{
itoa(Year, sptr, 10);
while (*sptr)
sptr++;
}
}
break;
case '\\' :
fptr++; /* skip over the \, and ... */
default :
*sptr = *fptr; /* copy the character */
sptr++; /* point to next empty char */
fptr++;
break;
}
}
ret = strdup(str); /* make a "trimmed" copy */
free(str); /* free our work string */
return (ret); /* return the "duped" string */
}