/*! \file dates.c
Date Calculation Routines.
A collection of date calculation routines that are Year 2000
compliant for MMDDYYYY or MMDDYY string dates.
\author Jon A. Lambert
\version 1.96
\date 06/27/2002
\remarks
This code copyright (C) 2000-2002 by Jon A. Lambert<BR>
<antilochos@ix.netcom.com> - All rights reserved.<BR>
No part of this code may be used without my written consent.<BR>
This code released to the PUBLIC DOMAIN on 4/2/2006
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
static int CurrentCentury ();
static bool ValidDayMonthRange (int month, int day);
long DaysSince1800(const char * date);
int DaysInMonth(int month, int year);
bool IsLeapYear(int year);
int Month(const char * date);
int Day(const char * date);
int Year(const char * date);
void MakeDateString(char * date, struct tm * dt);
bool ValidateDate(char* dt);
/*!
DaysSince1800(const char *): long
Returns the # of days from 1/1/1800 to date.
\param - date in MMDDYYYY or MMDDYY format.
\return - long integer value of days since 1800.
\remarks - Calculates the number of days from January 1st,
1800 to the passed date.
Used for date arithmetic.
*/
long DaysSince1800 (const char *date)
{
int curr_year = 1800, curr_month = 1, curr_day = 1;
long total_days = 0;
if ((Year (date) >= 1800) && (Month (date) > 0)
&& (Day (date) > 0)) {
while (curr_year < Year (date))
total_days += IsLeapYear (curr_year++) ? 366 : 365;
while (curr_month < Month (date))
total_days += DaysInMonth (curr_month++, curr_year);
while (curr_day++ < Day (date))
++total_days;
}
return total_days;
}
/*!
DaysInMonth(int, int): int
Returns the # of days in the month.
\param - Integer month and integer year.
\return - Integer value of # of days in the month.
\remarks - Will return the # of days in the passed month.
*/
int DaysInMonth (int month, int year)
{
// thirty days hath september, april, june and november
if (month == 9 || month == 4 || month == 6 || month == 11)
return 30;
if (month == 2)
if (IsLeapYear (year))
return 29;
else
return 28;
else
return 31;
}
/*!
IsLeapYear(year: positive integer): boolean
Check whether a given year is a leap year.
\param - year Year to check.
\return - true when `year' is a leap year, false if it is not.
\remarks - In the Gregorian calendar a year is a leap year when it
is divisible by 4 and is not a turn of the century
exept when the turn of the century is in a year being
a multiple of 400. The Gregorian calendar was
introduced by pope Gregorius XIII in 1582.
Rule 1: If the year is less than 1582 it IS NOT a leap year.
Rule 2: If the year is divisible by 400, it IS a leap year.
Rule 3: If the year is divisible by 100, it IS NOT a leap year.
Rule 4: If the year is divisible by 4, it IS a leap year.
*/
bool IsLeapYear (int year)
{
return ((year > 1581) && ((year % 400 == 0) || ((year % 100)
&& (year % 4 == 0))));
}
/*!
Month(const char *): int
Returns the month from date.
\param - Date in MMDDYYYY or MMDDYY format.
\return - integer value of month or 0
\remarks - Integer value of month will be returned.
*/
int Month (const char *date)
{
char month_str[3];
strncpy (month_str, date, 2);
month_str[2] = '\0';
return (atoi (month_str));
}
/*!
Day(const char *): int
Returns the day from date.
\param - Date in MMDDYYYY or MMDDYY format.
\return - integer value of day or 0
\remarks - integer value of day will be returned.
*/
int Day (const char *date)
{
char day_str[3];
strncpy (day_str, date + 2, 2);
day_str[2] = '\0';
return (atoi (day_str));
}
/*!
Year(const char *): int
Returns the year from date.
\param - Date in MMDDYYYY or MMDDYY format.
\return - integer value of year or zero
\remarks - Converts 2 digit years to 4 digits by assuming
current dates century.
*/
int Year (const char *date)
{
char year_str[5];
int year_val;
strncpy (year_str, date + 4, 4);
year_str[4] = '\0';
year_val = atoi (year_str);
// Check if only two digits entered
if (year_val < 100)
year_val += CurrentCentury (); // add century information
return (year_val);
}
/*!
CurrentCentury(): int
Retrieves the current century
\return - integer.
\remarks - The current system time is retrieved and the century is
returned from the current year.
Ex: if the year is 1998 then 1900 is returned
if the year is 2019 then 2000 is returned
*/
static int CurrentCentury ()
{
time_t curr_time;
struct tm *local_time;
time (&curr_time);
local_time = localtime (&curr_time);
// Integer math to truncate century to hundreds
return (((local_time->tm_year + 1900) / 100) * 100);
}
/*!
MakeDateString(char * date, struct tm * dt): void
Sets the month in the destination date field.
\param - pointer to string to destination date must have allocated
a mininum of 9 characters.
pointer to a tm structure.
\return - void.
\remarks - Date field will be set.
*/
void MakeDateString (char *date, struct tm *dt)
{
char temp_str[MAX_INPUT_LENGTH];
sprintf (temp_str, "%02d%02d%02d",
dt->tm_mon + 1, dt->tm_mday, dt->tm_year - 100);
temp_str[6] = '\0';
strcpy (date, temp_str);
}
/*!
ValidDayMonthRange(int month, int day): boolean
Check whether a day falls within legal date range for month
\param - Integer month and integer year.
\return - true when day, false day is out of range.
\remarks - Checks table of months to see if day is within the
legal range.
*/
static bool ValidDayMonthRange (int month, int day)
{
int day_table[12] = { 31, 29, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31
};
if ((month < 1) || (month > 12))
return FALSE;
if ((day > day_table[month - 1]) || (day < 1))
return FALSE;
else
return TRUE;
}
/*!
ValidateDate(char*): boolean
Check whether a date is valid
\param - date in MMDDYYYY or MMDDYY format.
\return - true when date is valid, false when date invalid.
\remarks - In the Gregorian calendar a year is a leap year when it
is divisible by 4 and is not a turn of the century
except when the turn of the century is in a year being
a multiple of 400. The Gregorian calendar was
introduced by pope Gregorius XIII in 1582.
*/
bool ValidateDate (char *dt)
{
int month, day, year;
month = Month (dt);
if ((month < 1) || (month > 12))
return FALSE;
year = Year (dt);
if ((year < 100) || (year > 9999))
return FALSE;
day = Day (dt);
// Check that day is within month's allowed range
if (!ValidDayMonthRange (month, day))
return FALSE;
// Check for valid leapyear conditions
if ((month == 2) && (day == 29)) {
if (!(IsLeapYear (year)))
return FALSE;
}
return TRUE;
}