/*! \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; }