/*
The CalCalcs routines, a set of C-language routines to perform
calendar calculations.
Version 1.0, released 7 January 2010
Copyright (C) 2010 David W. Pierce, dpierce@ucsd.edu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include "calcalcs.h"
static int c_isleap_gregorian ( int year, int *leap );
static int c_isleap_gregorian_y0( int year, int *leap );
static int c_isleap_julian ( int year, int *leap );
static int c_isleap_never ( int year, int *leap );
static int c_date2jday_julian ( int year, int month, int day, int *jday );
static int c_date2jday_gregorian ( int year, int month, int day, int *jday );
static int c_date2jday_gregorian_y0( int year, int month, int day, int *jday );
static int c_date2jday_noleap ( int year, int month, int day, int *jday );
static int c_date2jday_360_day ( int year, int month, int day, int *jday );
static int c_jday2date_julian ( int jday, int *year, int *month, int *day );
static int c_jday2date_gregorian ( int jday, int *year, int *month, int *day );
static int c_jday2date_gregorian_y0( int jday, int *year, int *month, int *day );
static int c_jday2date_noleap ( int jday, int *year, int *month, int *day );
static int c_jday2date_360_day ( int jday, int *year, int *month, int *day );
static int c_dpm_julian ( int year, int month, int *dpm );
static int c_dpm_gregorian ( int year, int month, int *dpm );
static int c_dpm_gregorian_y0( int year, int month, int *dpm );
static int c_dpm_noleap ( int year, int month, int *dpm );
static int c_dpm_360_day ( int year, int month, int *dpm );
#define CCS_ERROR_MESSAGE_LEN 8192
static char error_message[CCS_ERROR_MESSAGE_LEN];
/* Following are number of Days Per Month (dpm). They are called 'idx1' to
* emphasize that they are intended to be index by a month starting at 1
* rather than at 0.
* na, jan feb mar apr may jun jul aug sep oct nov dec */
static int dpm_idx1[] = {-99, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static int dpm_leap_idx1[] = {-99, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/* Same as above, but SUM of previous months. Indexing starts at 1 for January */
static int spm_idx1[] = {-99, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
/* static int spm_leap_idx1[] = {-99, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; note: spm is only used for calendars with no leap years */
static int date_ge( int year, int month, int day, int y2, int m2, int d2 );
static int date_le( int year, int month, int day, int y2, int m2, int d2 );
static int date_lt( int year, int month, int day, int y2, int m2, int d2 );
static int date_gt( int year, int month, int day, int y2, int m2, int d2 );
static int set_xition_extra_info( calcalcs_cal *cal );
static void ccs_dump_xition_dates( void );
static void ccs_gxd_add_country( char *code, char *longname, int year, int month, int day );
static void ccs_init_country_database( void );
/* Some arbitrary number that is unlikely to be encounterd in a string of random digits */
#define CCS_VALID_SIG 89132412
/* These implement the database that associates a two-letter country code, such as "UK",
* with the transition date that the country switched from the Julian to Gregorian calendar
*/
#define CCS_MAX_N_COUNTRY_CODES 5000
static int ccs_n_country_codes = 0;
static ccs_country_code *ccs_xition_dates[ CCS_MAX_N_COUNTRY_CODES ];
static int have_initted_country_codes = 0;
/**********************************************************************************************
* Initialize a calendar. The passed argument is the name of the calendar, and may be
* one of the following character strings:
* "standard"
* "proleptic_Julian"
* "proleptic_Gregorian"
* "noleap" (aka "365_day" and "no_leap")
* "360_day"
*
* As a special hack, a calendar can be named "standard_XX" where XX is a two-letter
* date code recognized by ccs_get_xition_date, in which case a standard calendar with
* the specified transition date will be used.
*
* Returns a pointer to the new calendar, or NULL on error.
*/
calcalcs_cal *ccs_init_calendar( const char *calname )
{
calcalcs_cal *retval;
int use_specified_xition_date, spec_year_x, spec_month_x, spec_day_x;
error_message[0] = '\0';
if( strncasecmp( calname, "standard", 8 ) == 0 ) {
if( ! have_initted_country_codes )
ccs_init_country_database();
/* See if this is a name of the form "Standard_XX" */
use_specified_xition_date = 0;
if( (strlen(calname) >= 11) && (calname[8] == '_')) {
if( ccs_get_xition_date( calname+9, &spec_year_x, &spec_month_x, &spec_day_x ) != 0 ) {
fprintf( stderr, "Error, unknown calendar passed to ccs_init_calendar: \"%s\". Returning NULL\n",
calname );
return(NULL);
}
use_specified_xition_date = 1;
}
retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
if( retval == NULL ) {
fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar. Returning NULL\n" );
return( NULL );
}
retval->sig = CCS_VALID_SIG;
retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
strcpy( retval->name, calname );
retval->mixed = 1;
retval->early_cal = ccs_init_calendar( "proleptic_julian" );
retval->late_cal = ccs_init_calendar( "proleptic_gregorian" );
/* Following are FIRST DAY the "later" calendar should be used */
if( use_specified_xition_date == 1 ) {
retval->year_x = spec_year_x;
retval->month_x = spec_month_x;
retval->day_x = spec_day_x;
}
else
{
retval->year_x = 1582;
retval->month_x = 10;
retval->day_x = 15;
}
/* Set the last date the earlier cal was used, and the transition day's Julian date */
if( set_xition_extra_info( retval ) != 0 ) {
fprintf( stderr, "calcalcs_init_cal: Error trying to initialize calendar \"%s\": %s. Returning NULL\n",
calname, error_message );
return(NULL);
}
}
else if( (strcasecmp( calname, "gregorian" ) == 0) ||
(strcasecmp( calname, "proleptic_gregorian" ) == 0)) {
/* This is a "regular" Gregorian calendar, which does not include "year 0".
* See also calendar gregorian_y0, which does include a year 0, below
*/
retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
if( retval == NULL ) {
fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
return( NULL );
}
retval->sig = CCS_VALID_SIG;
retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
strcpy( retval->name, calname );
retval->ndays_reg = 365;
retval->ndays_leap = 366;
retval->mixed = 0;
retval->c_isleap = &c_isleap_gregorian;
retval->c_date2jday = &c_date2jday_gregorian;
retval->c_jday2date = &c_jday2date_gregorian;
retval->c_dpm = &c_dpm_gregorian;
}
else if( (strcasecmp( calname, "gregorian_y0" ) == 0) ||
(strcasecmp( calname, "proleptic_gregorian_y0" ) == 0)) {
/* This is a Gregorian calendar that includes "year 0".
*/
retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
if( retval == NULL ) {
fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
return( NULL );
}
retval->sig = CCS_VALID_SIG;
retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
strcpy( retval->name, calname );
retval->ndays_reg = 365;
retval->ndays_leap = 366;
retval->mixed = 0;
retval->c_isleap = &c_isleap_gregorian_y0;
retval->c_date2jday = &c_date2jday_gregorian_y0;
retval->c_jday2date = &c_jday2date_gregorian_y0;
retval->c_dpm = &c_dpm_gregorian_y0;
}
else if( (strcasecmp( calname, "julian" ) == 0 ) ||
(strcasecmp( calname, "proleptic_julian" ) == 0 )) {
retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
if( retval == NULL ) {
fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
return( NULL );
}
retval->sig = CCS_VALID_SIG;
retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
strcpy( retval->name, calname );
retval->ndays_reg = 365;
retval->ndays_leap = 366;
retval->mixed = 0;
retval->c_isleap = &c_isleap_julian;
retval->c_date2jday = &c_date2jday_julian;
retval->c_jday2date = &c_jday2date_julian;
retval->c_dpm = &c_dpm_julian;
}
else if( (strcasecmp(calname,"noleap")==0) ||
(strcasecmp(calname,"no_leap")==0) ||
(strcasecmp(calname,"365_day")==0)) {
retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
if( retval == NULL ) {
fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
return( NULL );
}
retval->sig = CCS_VALID_SIG;
retval->name = (char *)malloc( sizeof(char) * (strlen("noleap")+1) );
strcpy( retval->name, "noleap" );
retval->ndays_reg = 365;
retval->ndays_leap = 365;
retval->mixed = 0;
retval->c_isleap = &c_isleap_never;
retval->c_date2jday = &c_date2jday_noleap;
retval->c_jday2date = &c_jday2date_noleap;
retval->c_dpm = &c_dpm_noleap;
}
else if( strcasecmp(calname,"360_day")==0) {
retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
if( retval == NULL ) {
fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
return( NULL );
}
retval->sig = CCS_VALID_SIG;
retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
strcpy( retval->name, calname );
retval->ndays_reg = 360;
retval->ndays_leap = 360;
retval->mixed = 0;
retval->c_isleap = &c_isleap_never;
retval->c_date2jday = &c_date2jday_360_day;
retval->c_jday2date = &c_jday2date_360_day;
retval->c_dpm = &c_dpm_360_day;
}
else
return( NULL );
return( retval );
}
/**********************************************************************************************
*
* Determine if the passed year is a leap year in the specified calendar.
* The passed parameter leap is set to '1' if the year is a leap year, and '0' if it is not.
*
* Returns 0 on success, and a negative value on error.
* Errors include the passed year being invalid (before 4713 B.C.) or not existing
* in the specified calendar (i.e., there is no year 0 in either the Gregorian or
* Julian calendars).
*
*/
int ccs_isleap( calcalcs_cal *calendar, int year, int *leap )
{
int ierr;
calcalcs_cal *c2use;
if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
if( year < -4714 ) {
sprintf( error_message, "ccs_isleap: year %d is out of range for the %s calendar; dates must not be before 4713 B.C.", year, calendar->name );
return( CALCALCS_ERR_OUT_OF_RANGE );
}
if( calendar->mixed ) {
if( year >= calendar->year_x ) /* Q: did any countries transition during a year that had different leap status before and after??? Let's hope not! */
c2use = calendar->late_cal;
else
c2use = calendar->early_cal;
}
else
c2use = calendar;
ierr = c2use->c_isleap( year, leap );
return( ierr );
}
/**********************************************************************************************
* ccs_dpm: returns the number of days per month for the passed year/month/calendar combo
*
* Returns 0 on success, and a negative number on error (for example, an illegal month number)
*/
int ccs_dpm( calcalcs_cal *calendar, int year, int month, int *dpm )
{
int ndays_reg, ierr;
int overlap_px_month, overlap_x_month;
calcalcs_cal *c2use;
if( calendar->mixed ) {
/* A calendar transition potentially affects two months -- the month containing the
* last day of the old calendar, and the month containing the first day of the
* new calendar. If we are in either of those months, things get much harder.
* (Note that these can easily be the same month)
*/
overlap_px_month = ((year == calendar->year_px) && (month == calendar->month_px));
overlap_x_month = ((year == calendar->year_x ) && (month == calendar->month_x ));
if( overlap_px_month || overlap_x_month ) {
if( overlap_px_month && (!overlap_x_month)) {
/* Last day of the month must have been last day the early calendar was used */
*dpm = calendar->day_px;
return(0);
}
else if( overlap_x_month && (!overlap_px_month)) {
if( (ierr = ccs_dpm( calendar->late_cal, year, month, &ndays_reg )) != 0 )
return( ierr );
*dpm = ndays_reg - calendar->day_x + 1;
return(0);
}
else /* overlap_px_month && overlap_x_month */
{
if( (ierr = ccs_dpm( calendar->late_cal, year, month, &ndays_reg )) != 0 )
return( ierr );
*dpm = calendar->day_px + (ndays_reg - calendar->day_x + 1);
return(0);
}
}
else if( date_ge( year, month, 1, calendar->year_x, calendar->month_x, calendar->day_x ))
c2use = calendar->late_cal;
else
c2use = calendar->early_cal;
}
else
c2use = calendar;
return( c2use->c_dpm( year, month, dpm ));
}
/**********************************************************************************************
* ccs_jday2date: give a Julian day number, return the corresponding date in the
* selected calendar
*
* Returns 0 on success, <0 on error and fills string error_message
*/
int ccs_jday2date( calcalcs_cal *calendar, int jday, int *year, int *month, int *day )
{
calcalcs_cal *c2use;
if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
if( calendar->mixed ) {
if( jday >= calendar->jday_x )
c2use = calendar->late_cal;
else
c2use = calendar->early_cal;
}
else
c2use = calendar;
return( c2use->c_jday2date( jday, year, month, day ));
}
/**********************************************************************************************
* ccs_date2jday: given a date, return the (true) Julian day number
*
* Note that "Julian day number" is not the day number of the year, but rather the
* day number starting on Jan 1st 4713 BC (in the proleptic Julian calendar) and
* counting consecutively.
*
* Returns 0 on success, <0 on error and fills string error_message
*/
int ccs_date2jday( calcalcs_cal *calendar, int year, int month, int day, int *jday )
{
int dpm, ierr;
calcalcs_cal *c2use;
if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
if( calendar->mixed ) {
if( date_ge( year, month, day, calendar->year_x, calendar->month_x, calendar->day_x ))
c2use = calendar->late_cal;
else if( date_le( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ))
c2use = calendar->early_cal;
else
{
sprintf( error_message, "ccs_date2jday: date %04d-%02d-%02d is not a valid date in the %s calendar; it falls between the last date the %s calendar was used (%04d-%02d-%02d) and the first date the %s calendar was used (%04d-%02d-%02d)",
year, month, day, calendar->name,
calendar->early_cal->name,
calendar->year_px, calendar->month_px, calendar->day_px,
calendar->late_cal->name,
calendar->year_x, calendar->month_x, calendar->day_x );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
}
else
c2use = calendar;
if( (ierr = ccs_dpm( c2use, year, month, &dpm )) != 0 )
return( ierr );
if( (month < 1) || (month > 12) || (day < 1) || (day > dpm)) {
sprintf( error_message, "date2jday passed an date that is invalid in the %s calendar: %04d-%02d-%02d",
c2use->name, year, month, day );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
return( c2use->c_date2jday( year, month, day, jday ));
}
/********************************************************************************************
*
* ccs_date2doy: given a Y/M/D date, calculates the day number of the year, starting at 1 for
* January 1st.
*
* Returns 0 on success, and a negative value on error (for example, an illegal date [one
* that does not exist in the specified calendar])
*/
int ccs_date2doy( calcalcs_cal *calendar, int year, int month, int day, int *doy )
{
int ierr, jd0, jd1, doy_px, jd_args, xition_date_first_day_of_year,
ndays_elapsed;
calcalcs_cal *c2use;
if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
if( calendar->mixed ) {
/* If we fall in the twilight zone after the old calendar was stopped but before
* the new calendar was used, it's an error
*/
if( date_gt( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ) &&
date_lt( year, month, day, calendar->year_x, calendar->month_x, calendar->day_x )) {
sprintf( error_message, "ccs_date2doy: date %04d-%02d-%02d is not a valid date in the %s calendar; it falls between the last date the %s calendar was used (%04d-%02d-%02d) and the first date the %s calendar was used (%04d-%02d-%02d)",
year, month, day, calendar->name,
calendar->early_cal->name,
calendar->year_px, calendar->month_px, calendar->day_px,
calendar->late_cal->name,
calendar->year_x, calendar->month_x, calendar->day_x );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
xition_date_first_day_of_year = ((year == calendar->year_x) && (calendar->month_x == 1) && (calendar->day_x == 1));
if( (year > calendar->year_x) || xition_date_first_day_of_year )
c2use = calendar->late_cal;
else if( date_le( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ))
c2use = calendar->early_cal;
else
{
/* Complicated if we are asking for the day of the year during
* the transition year and after the transition date. I'm choosing
* to define the day numbering during a transition year as
* consecutive, which means that the doy for dates
* after the transition date equals the doy of the last
* day of the earlier calendar plus the number of days
* that have elapsed since the transition day.
*/
/* Get the doy of the day BEFORE the transition day
* in the earlier calendar
*/
if( (ierr = ccs_date2doy( calendar->early_cal, calendar->year_px, calendar->month_px, calendar->day_px, &doy_px )) != 0 )
return( ierr );
/* Get number of days that have elapsed between the transition day
* and the requested date
*/
if( (ierr = ccs_date2jday( calendar->late_cal, year, month, day, &jd_args )) != 0 )
return( ierr );
ndays_elapsed = jd_args - calendar->jday_x + 1; /* if this IS the transition day, ndays_elapsed==1 */
/* Finally, the day of the year is the day number of the day BEFORE the
* transition day plus the number of elapsed days since the
* transition day.
*/
*doy = doy_px + ndays_elapsed;
return(0);
}
}
else
c2use = calendar;
/* Get Julian day number of Jan 1st of the specified year */
if( (ierr = c2use->c_date2jday( year, 1, 1, &jd0 )) != 0 )
return( ierr );
/* Get Julian day number of the specified date */
if( (ierr = c2use->c_date2jday( year, month, day, &jd1 )) != 0 )
return( ierr );
*doy = jd1 - jd0 + 1; /* Add 1 because numbering starts at 1 */
return(0);
}
/********************************************************************************************
*
* ccs_doy2date: given a year and a day number in that year (with counting starting at 1 for
* Jan 1st), this returns the month and day of the month that the doy refers to.
*
* Returns 0 on success, and a negative value on error (for example, a day of the year
* that is less than 1 or greater than 366).
*/
int ccs_doy2date( calcalcs_cal *calendar, int year, int doy, int *month, int *day )
{
int ierr, leap, jd0, jd1, tyear, doy_px, jd_want,
xition_date_first_day_of_year, ndays_max;
calcalcs_cal *c2use;
if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
if( calendar->mixed ) {
xition_date_first_day_of_year = ((year == calendar->year_x) && (calendar->month_x == 1) && (calendar->day_x == 1));
if( year < calendar->year_x )
c2use = calendar->early_cal;
else if( (year > calendar->year_x) || xition_date_first_day_of_year )
c2use = calendar->late_cal;
else
{
/* Get the doy of the day BEFORE the transition day
* in the earlier calendar
*/
if( (ierr = ccs_date2doy( calendar->early_cal, calendar->year_px, calendar->month_px, calendar->day_px, &doy_px )) != 0 )
return( ierr );
/* If our requested doy is before the transition doy, we
* can just easily calculate it with the early calendar
*/
if( doy <= doy_px )
return( ccs_doy2date( calendar->early_cal, year, doy, month, day ));
/* Finally calculate the Julian day we want, and convert it to a date */
jd_want = calendar->jday_x + (doy - doy_px - 1);
if( (ierr = ccs_jday2date( calendar->late_cal, jd_want, &tyear, month, day)) != 0 )
return(ierr);
/* If the year we got from that Julian day is different from the original
* year specified, it means we have gone off the end of the transition year,
* probably because that year has less days than regular years. In that
* event, return an error.
*/
if( tyear != year ) {
sprintf( error_message, "year %d in the %s calendar (with transition date %04d-%02d-%02d) has less than %d days, but that was the day-of-year number requested in a call to ccs_doy2date\n",
year, calendar->name, calendar->year_x, calendar->month_x, calendar->day_x, doy );
return( CALCALCS_ERR_INVALID_DAY_OF_YEAR );
}
return(0);
}
}
else
c2use = calendar;
/* Check to make sure we are not asking for a doy that does not exist,
* esp. as regards to the number of days in leap vs. non-leap years
*/
if( (ierr = c2use->c_isleap( year, &leap )) != 0 )
return( ierr );
if( leap == 1 )
ndays_max = c2use->ndays_leap;
else
ndays_max = c2use->ndays_reg;
if( (doy < 1) || (doy > ndays_max)) {
sprintf( error_message, "routine ccs_doy2date was passed a day-of-year=%d, but for year %d in the %s calendar, the value must be between 1 and %d",
doy, year, c2use->name, ndays_max );
return( CALCALCS_ERR_INVALID_DAY_OF_YEAR );
}
/* Get Julian day number of Jan 1st of the specified year */
if( (ierr = c2use->c_date2jday( year, 1, 1, &jd0 )) != 0 )
return( ierr );
/* Calculate new Julian day */
jd1 = jd0 + doy - 1;
/* Get date for new Julian day */
if( (ierr = c2use->c_jday2date( jd1, &tyear, month, day )) != 0 )
return( ierr );
return(0);
}
/********************************************************************************************
* ccs_dayssince: Given a Y/M/D date in a specified calendar, and the number of days since
* that date, this returns the new Y/M/D date in a (possibly different) calendar.
*
* Note that specifying "zero" days since, and giving different calendars as the original
* and new calendars, essentially converts dates between calendars.
*
* Returns 0 on success, and a negative value on error.
*/
int ccs_dayssince( calcalcs_cal *calendar_orig, int year_orig, int month_orig, int day_orig,
int ndays_since, calcalcs_cal *calendar_new, int *year_new, int *month_new, int *day_new )
{
int ierr, jd0, jd1;
calcalcs_cal *c2use_orig, *c2use_new;
if( calendar_orig == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
if( calendar_orig->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
if( calendar_new == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
if( calendar_new->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
/* Figure out which calendar of the ORIGINAL calendar to use if it's a mixed calendar
*/
if( calendar_orig->mixed ) {
if( date_ge( year_orig, month_orig, day_orig,
calendar_orig->year_x, calendar_orig->month_x, calendar_orig->day_x ))
c2use_orig = calendar_orig->late_cal;
else if( date_le( year_orig, month_orig, day_orig,
calendar_orig->year_px, calendar_orig->month_px, calendar_orig->day_px ))
c2use_orig = calendar_orig->early_cal;
else
{
sprintf( error_message, "ccs_dayssince: date %04d-%02d-%02d is not a valid date in the %s calendar; it falls between the last date the %s calendar was used (%04d-%02d-%02d) and the first date the %s calendar was used (%04d-%02d-%02d)",
year_orig, month_orig, day_orig, calendar_orig->name,
calendar_orig->early_cal->name,
calendar_orig->year_px, calendar_orig->month_px, calendar_orig->day_px,
calendar_orig->late_cal->name,
calendar_orig->year_x, calendar_orig->month_x, calendar_orig->day_x );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
}
else
c2use_orig = calendar_orig;
/* Get Julian day in the original calendar and date combo */
if( (ierr = c2use_orig->c_date2jday( year_orig, month_orig, day_orig, &jd0 )) != 0 )
return(ierr);
/* Get new Julian day */
jd1 = jd0 + ndays_since;
if( calendar_new->mixed ) {
/* Figure out which calendar of the NEW calendar to use if it's a mixed calendar.
*/
if( jd1 >= calendar_new->jday_x )
c2use_new = calendar_new->late_cal;
else
c2use_new = calendar_new->early_cal;
}
else
c2use_new = calendar_new;
/* Convert the new Julian day to a date in the new calendar */
if( (ierr = c2use_new->c_jday2date( jd1, year_new, month_new, day_new )) != 0 )
return( ierr );
return(0);
}
/********************************************************************************************/
static void ccs_gxd_add_country( char *code, char *longname, int year, int month, int day )
{
if( ccs_n_country_codes >= CCS_MAX_N_COUNTRY_CODES ) {
fprintf( stderr, "Error, the calcalcs library is attempting to store more country codes than is possible; max is %d\n",
CCS_MAX_N_COUNTRY_CODES );
fprintf( stderr, "To fix, recompile with a larger number for CCS_MAX_N_COUNTRY_CODES\n" );
exit( -1 );
}
ccs_xition_dates[ccs_n_country_codes] = (ccs_country_code *)malloc( sizeof( ccs_country_code ));
if( ccs_xition_dates[ccs_n_country_codes] == NULL ) {
fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code %s\n",
code );
exit(-1);
}
ccs_xition_dates[ccs_n_country_codes]->code = (char *)malloc( sizeof(char) * (strlen(code)+1) );
if( ccs_xition_dates[ccs_n_country_codes]->code == NULL ) {
fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code named %s\n",
code );
exit(-1);
}
strcpy( ccs_xition_dates[ccs_n_country_codes]->code, code );
ccs_xition_dates[ccs_n_country_codes]->longname = (char *)malloc( sizeof(char) * (strlen(longname)+1) );
if( ccs_xition_dates[ccs_n_country_codes]->longname == NULL ) {
fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code long name %s\n",
longname );
exit(-1);
}
strcpy( ccs_xition_dates[ccs_n_country_codes]->longname, longname );
ccs_xition_dates[ccs_n_country_codes]->year = year;
ccs_xition_dates[ccs_n_country_codes]->month = month;
ccs_xition_dates[ccs_n_country_codes]->day = day;
ccs_n_country_codes++;
}
/********************************************************************************************/
static void ccs_init_country_database()
{
ccs_gxd_add_country( "AK", "Alaska", 1867, 10, 18 );
ccs_gxd_add_country( "AL", "Albania", 1912, 12, 1 );
ccs_gxd_add_country( "AT", "Austria", 1583, 10, 16 );
ccs_gxd_add_country( "BE", "Belgium", 1582, 12, 25 );
ccs_gxd_add_country( "BG", "Bulgaria", 1916, 4, 1 );
ccs_gxd_add_country( "CN", "China", 1929, 1, 1 );
ccs_gxd_add_country( "CZ", "Czechoslovakia", 1584, 1, 17 );
ccs_gxd_add_country( "DK", "Denmark", 1700, 3, 1 );
ccs_gxd_add_country( "NO", "Norway", 1700, 3, 1 );
ccs_gxd_add_country( "EG", "Egypt", 1875, 1, 1 );
ccs_gxd_add_country( "EE", "Estonia", 1918, 1, 1 );
ccs_gxd_add_country( "FI", "Finland", 1753, 3, 1 );
ccs_gxd_add_country( "FR", "France", 1582, 12, 20 );
ccs_gxd_add_country( "DE", "Germany", 1583, 11, 22 );
ccs_gxd_add_country( "UK", "United Kingdom", 1752, 9, 14 );
ccs_gxd_add_country( "GR", "Greece", 1924, 3, 23 );
ccs_gxd_add_country( "HU", "Hungary", 1587, 11, 1 );
ccs_gxd_add_country( "IT", "Italy", 1582, 10, 15 );
ccs_gxd_add_country( "JP", "Japan", 1918, 1, 1 );
ccs_gxd_add_country( "LV", "Latvia", 1915, 1, 1 );
ccs_gxd_add_country( "LT", "Lithuania", 1915, 1, 1 );
ccs_gxd_add_country( "LU", "Luxemburg", 1582, 12, 15 );
ccs_gxd_add_country( "NL", "Netherlands", 1582, 10, 15 );
ccs_gxd_add_country( "PL", "Poland", 1582, 10, 15 );
ccs_gxd_add_country( "PT", "Portugal", 1582, 10, 15 );
ccs_gxd_add_country( "RO", "Romania", 1919, 4, 14 );
ccs_gxd_add_country( "ES", "Spain", 1582, 10, 15 );
ccs_gxd_add_country( "SE", "Sweden", 1753, 3, 1 );
ccs_gxd_add_country( "CH", "Switzerland", 1584, 1, 22 );
ccs_gxd_add_country( "TR", "Turkey", 1927, 1, 1 );
ccs_gxd_add_country( "YU", "Yugoslavia", 1919, 1, 1 );
ccs_gxd_add_country( "US", "United States", 1752, 9, 14 );
ccs_gxd_add_country( "SU", "Soviet Union", 1918, 2, 1 );
ccs_gxd_add_country( "RU", "Russia", 1918, 2, 1 );
have_initted_country_codes = 1;
}
/********************************************************************************************/
int ccs_get_xition_date( const char *country_code, int *year, int *month, int *day )
{
int i;
if( ! have_initted_country_codes )
ccs_init_country_database();
if( strcmp( country_code, "??" ) == 0 ) {
ccs_dump_xition_dates();
*year = 0;
*month = 0;
*day = 0;
return(0);
}
/* Find the passed country code in our list */
for( i=0; icode ) == 0 ) {
*year = ccs_xition_dates[i]->year;
*month = ccs_xition_dates[i]->month;
*day = ccs_xition_dates[i]->day;
return(0);
}
}
/* Maybe they passed a longname? */
for( i=0; ilongname ) == 0 ) {
*year = ccs_xition_dates[i]->year;
*month = ccs_xition_dates[i]->month;
*day = ccs_xition_dates[i]->day;
return(0);
}
}
sprintf( error_message, "ccs_get_xition_date: unknown calendar country/region code: \"%s\". Known codes: ", country_code );
for( i=0; icode) + strlen(ccs_xition_dates[i]->longname) + 10) < CCS_ERROR_MESSAGE_LEN ) {
strcat( error_message, ccs_xition_dates[i]->code );
strcat( error_message, " (" );
strcat( error_message, ccs_xition_dates[i]->longname );
strcat( error_message, ") " );
}
}
return(CALCALCS_ERR_UNKNOWN_COUNTRY_CODE);
}
/********************************************************************************************/
static void ccs_dump_xition_dates( void )
{
int i;
printf( "Calcalcs library known country codes:\n" );
for( i=0; icode,
ccs_xition_dates[i]->year,
ccs_xition_dates[i]->month,
ccs_xition_dates[i]->day,
ccs_xition_dates[i]->longname );
if( i%3 == 2 )
printf( "\n" );
}
}
/********************************************************************************************/
int ccs_set_xition_date( calcalcs_cal *calendar, int year, int month, int day )
{
int ierr, dpm;
if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
if( calendar->mixed == 0 )
return( CALCALCS_ERR_NOT_A_MIXED_CALENDAR );
/* Check to make sure the specified date is a valid date in the
* LATE calendar (since the transition date is the first date
* that the late calendar is used)
*/
if( (ierr = ccs_dpm( calendar->late_cal, year, month, &dpm )) != 0 )
return( ierr );
if( (month < 1) || (month > 12) || (day < 1) || (day > dpm)) {
fprintf( stderr, "Error in routine set_cal_xition_date: trying to set the calendar Julian/Gregorian transition date to an illegal date: %04d-%02d-%02d\n", year, month, day );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
calendar->year_x = year;
calendar->month_x = month;
calendar->day_x = day;
if( (ierr = set_xition_extra_info( calendar )) != 0 )
return( ierr );
return(0);
}
/********************************************************************************************/
char *ccs_err_str( int errno )
{
if( errno == 0 )
sprintf( error_message, "no error from calcalcs routines version %f", CALCALCS_VERSION_NUMBER );
else if( errno == CALCALCS_ERR_NULL_CALENDAR )
sprintf( error_message, "a NULL calendar was passed to the calcalcs routine" );
else if( errno == CALCALCS_ERR_INVALID_CALENDAR )
sprintf( error_message, "an invalid, malformed, previously-freed, or uninitialized calendar was passed to the calcalcs routine" );
return( error_message );
}
/*==================================================================================================
* Returns 0 on success, <0 on error.
*/
int c_isleap_julian( int year, int *leap )
{
int tyear;
if( year == 0 ) {
sprintf( error_message, "the Julian calendar has no year 0" );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
/* Because there is no year 0 in the Julian calendar, years -1, -5, -9, etc
* are leap years.
*/
if( year < 0 )
tyear = year + 1;
else
tyear = year;
*leap = ((tyear % 4) == 0);
return(0);
}
/*==================================================================================================
* Returns 0 on success, <0 on error.
*/
int c_isleap_gregorian( int year, int *leap )
{
int tyear;
if( year == 0 ) {
sprintf( error_message, "the Gregorian calendar has no year 0. Use the \"Gregorian_y0\" calendar if you want to include year 0." );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
/* Because there is no year 0 in the gregorian calendar, years -1, -5, -9, etc
* are leap years.
*/
if( year < 0 )
tyear = year + 1;
else
tyear = year;
*leap = (((tyear % 4) == 0) && ((tyear % 100) != 0)) || ((tyear % 400) == 0);
return(0);
}
/*==================================================================================================
* Returns 0 on success, <0 on error.
*/
int c_isleap_gregorian_y0( int year, int *leap )
{
*leap = (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0);
return(0);
}
/*==================================================================================================
* Given a Y/M/D in the Gregorian calendar, this computes the (true) Julian day number of the
* specified date. Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian
* calendar. The algorithm is based on the "counting" algorithm in the C++ code I obtained from
* Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C.
* In that file, the work is declared to be in the public domain. I modified it by
* extending it to negative years (years BC) in addition to positive years, and to use
* actual Julian Days as the counter. Otherwise, the spirit of the algorithm is similar.
*
* Returns 0 on success, <0 on error.
*/
int c_date2jday_gregorian( int year, int month, int day, int *jday )
{
int m, leap, *dpm2use, err;
if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
sprintf( error_message, "date %04d-%02d-%02d does not exist in the Gregorian calendar",
year, month, day );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
if( year == 0 ) {
sprintf( error_message, "year 0 does not exist in the Gregorian calendar. Use the \"Gregorian_y0\" calendar if you want to include year 0" );
return( CALCALCS_ERR_NO_YEAR_ZERO );
}
/* Limit ourselves to positive Julian Days */
if( year < -4714 ) {
sprintf( error_message, "year %d is out of range of the Gregorian calendar routines; must have year >= -4714", year );
return( CALCALCS_ERR_OUT_OF_RANGE );
}
/* Following is necessary because Gregorian calendar skips year 0, so the
* offst for negative years is different than offset for positive years
*/
if( year < 0 )
year += 4801;
else
year += 4800;
if( (err = c_isleap_gregorian( year, &leap )) != 0 )
return( err );
if( leap )
dpm2use = dpm_leap_idx1;
else
dpm2use = dpm_idx1;
*jday = day;
for( m=month-1; m>0; m-- )
*jday += dpm2use[m];
*jday += 365*(year-1) + (year-1)/4 - (year-1)/100 + (year-1)/400;
/* Ajust to "true" Julian days. This constant is how many days difference there is
* between the Julian Day origin date of 4713 BC and our offset date of 4800 BC
*/
*jday -= 31739;
return(0);
}
/*==================================================================================================
* Given a Y/M/D in the Gregorian calendar, this computes the (true) Julian day number of the
* specified date. Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian
* calendar. The algorithm is based on the "counting" algorithm in the C++ code I obtained from
* Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C.
* In that file, the work is declared to be in the public domain. I modified it by
* extending it to negative years (years BC) in addition to positive years, and to use
* actual Julian Days as the counter. Otherwise, the spirit of the algorithm is similar.
*
* Returns 0 on success, <0 on error.
*/
int c_date2jday_gregorian_y0( int year, int month, int day, int *jday )
{
int m, leap, *dpm2use, err;
if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
sprintf( error_message, "date %04d-%02d-%02d does not exist in the Gregorian calendar",
year, month, day );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
/* Limit ourselves to positive Julian Days */
if( year < -4714 ) {
sprintf( error_message, "year %d is out of range of the Gregorian calendar routines; must have year >= -4714", year );
return( CALCALCS_ERR_OUT_OF_RANGE );
}
year += 4800;
if( (err = c_isleap_gregorian_y0( year, &leap )) != 0 )
return( err );
if( leap )
dpm2use = dpm_leap_idx1;
else
dpm2use = dpm_idx1;
*jday = day;
for( m=month-1; m>0; m-- )
*jday += dpm2use[m];
*jday += 365*(year-1) + (year-1)/4 - (year-1)/100 + (year-1)/400;
/* Ajust to "true" Julian days. This constant is how many days difference there is
* between the Julian Day origin date of 4713 BC and our offset date of 4800 BC
*/
*jday -= 31739;
return(0);
}
/*==========================================================================================
* Given a (true) Julian Day, this converts to a date in the Gregorian calendar.
* Technically, in the proleptic Gregorian calendar, since this works for dates
* back to 4713 BC. Again based on the same public domain code from Edward Reingold's
* web site as the date2jday routine, extended by me to apply to negative years (years BC).
*
* Returns 0 on success, <0 on error.
*/
int c_jday2date_gregorian( int jday, int *year, int *month, int *day )
{
int tjday, leap, *dpm2use, ierr, yp1;
/* Make first estimate for year. We subtract 4714 because Julian Day number
* 0 occurs in year 4714 BC in the Gregorian calendar (recall that it occurs
* in year 4713 BC in the JULIAN calendar
*/
*year = jday/366 - 4714;
/* Advance years until we find the right one */
yp1 = *year + 1;
if( yp1 == 0 ) yp1 = 1; /* no year 0 in the Gregorian calendar */
if( (ierr = c_date2jday_gregorian( yp1, 1, 1, &tjday )) != 0 )
return( ierr );
while( jday >= tjday ) {
(*year)++;
if( *year == 0 )
*year = 1; /* no year 0 in the Gregorian calendar */
yp1 = *year + 1;
if( yp1 == 0 ) yp1 = 1; /* no year 0 in the Gregorian calendar */
if( (ierr = c_date2jday_gregorian( yp1, 1, 1, &tjday )) != 0 )
return( ierr );
}
if( (ierr = c_isleap_gregorian( *year, &leap )) != 0 )
return( ierr );
if( leap )
dpm2use = dpm_leap_idx1;
else
dpm2use = dpm_idx1;
*month = 1;
if( (ierr = c_date2jday_gregorian( *year, *month, dpm2use[*month], &tjday)) != 0)
return( ierr );
while( jday > tjday ) {
(*month)++;
if( (ierr = c_date2jday_gregorian( *year, *month, dpm2use[*month], &tjday) != 0))
return( ierr );
}
if( (ierr = c_date2jday_gregorian( *year, *month, 1, &tjday)) != 0 )
return( ierr );
*day = jday - tjday + 1;
return(0);
}
/*==========================================================================================
* Given a (true) Julian Day, this converts to a date in the Gregorian calendar.
* Technically, in the proleptic Gregorian calendar, since this works for dates
* back to 4713 BC. Again based on the same public domain code from Edward Reingold's
* web site as the date2jday routine, extended by me to apply to negative years (years BC).
*
* Returns 0 on success, <0 on error.
*/
int c_jday2date_gregorian_y0( int jday, int *year, int *month, int *day )
{
int tjday, leap, *dpm2use, ierr, yp1;
/* Make first estimate for year. We subtract 4714 because Julian Day number
* 0 occurs in year 4714 BC in the Gregorian calendar (recall that it occurs
* in year 4713 BC in the JULIAN calendar
*/
*year = jday/366 - 4715;
/* Advance years until we find the right one */
yp1 = *year + 1;
if( (ierr = c_date2jday_gregorian_y0( yp1, 1, 1, &tjday )) != 0 )
return( ierr );
while( jday >= tjday ) {
(*year)++;
yp1 = *year + 1;
if( (ierr = c_date2jday_gregorian_y0( yp1, 1, 1, &tjday )) != 0 )
return( ierr );
}
if( (ierr = c_isleap_gregorian_y0( *year, &leap )) != 0 )
return( ierr );
if( leap )
dpm2use = dpm_leap_idx1;
else
dpm2use = dpm_idx1;
*month = 1;
if( (ierr = c_date2jday_gregorian_y0( *year, *month, dpm2use[*month], &tjday)) != 0)
return( ierr );
while( jday > tjday ) {
(*month)++;
if( (ierr = c_date2jday_gregorian_y0( *year, *month, dpm2use[*month], &tjday) != 0))
return( ierr );
}
if( (ierr = c_date2jday_gregorian_y0( *year, *month, 1, &tjday)) != 0 )
return( ierr );
*day = jday - tjday + 1;
return(0);
}
/*==================================================================================================
* Given a Y/M/D in the Julian calendar, this computes the (true) Julian day number of the
* specified date. Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian
* calendar. The algorithm is based on the "counting" algorithm in the C++ code I obtained from
* Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C.
* In that file, the work is declared to be in the public domain. I modified it by
* extending it to negative years (years BC) in addition to positive years, and to use
* actual Julian Days as the counter. Otherwise, the spirit of the algorithm is similar.
*
* Returns 0 on success, <0 on error.
*/
int c_date2jday_julian( int year, int month, int day, int *jday )
{
int m, leap, *dpm2use, err;
if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
sprintf( error_message, "date %04d-%02d-%02d does not exist in the Julian calendar",
year, month, day );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
if( year == 0 ) {
sprintf( error_message, "year 0 does not exist in the Julian calendar" );
return( CALCALCS_ERR_NO_YEAR_ZERO );
}
/* Limit ourselves to positive Julian Days */
if( year < -4713 ) {
sprintf( error_message, "year %d is out of range of the Julian calendar routines; must have year >= -4713", year );
return( CALCALCS_ERR_OUT_OF_RANGE );
}
/* Following is necessary because Julian calendar skips year 0, so the
* offst for negative years is different than offset for positive years
*/
if( year < 0 )
year += 4801;
else
year += 4800;
if( (err = c_isleap_julian( year, &leap )) != 0 )
return( err );
if( leap )
dpm2use = dpm_leap_idx1;
else
dpm2use = dpm_idx1;
*jday = day;
for( m=month-1; m>0; m-- )
*jday += dpm2use[m];
*jday += 365*(year-1) + (year-1)/4;
/* Ajust to "true" Julian days. This constant is how many days difference there is
* between the Julian Day origin date of 4713 BC and our offset date of 4800 BC
*/
*jday -= 31777;
return(0);
}
/*==========================================================================================
* Given a (true) Julian Day, this converts to a date in the Julian calendar.
* Technically, in the proleptic Julian calendar, since this works for dates
* back to 4713 BC. Again based on the same public domain code from Edward Reingold's
* web site as the date2jday routine, extended by me to apply to negative years (years BC).
*
* Returns 0 on success, <0 on error.
*/
int c_jday2date_julian( int jday, int *year, int *month, int *day )
{
int tjday, leap, *dpm2use, ierr, yp1;
/* Make first estimate for year. We subtract 4713 because Julian Day number
* 0 occurs in year 4713 BC in the Julian calendar
*/
*year = jday/366 - 4713;
/* Advance years until we find the right one */
yp1 = *year + 1;
if( yp1 == 0 ) yp1 = 1; /* no year 0 in the Julian calendar */
if( (ierr = c_date2jday_julian( yp1, 1, 1, &tjday )) != 0 )
return( ierr );
while( jday >= tjday ) {
(*year)++;
if( *year == 0 )
*year = 1; /* no year 0 in the Julian calendar */
yp1 = *year + 1;
if( yp1 == 0 ) yp1 = 1; /* no year 0 in the Julian calendar */
if( (ierr = c_date2jday_julian( yp1, 1, 1, &tjday )) != 0 )
return( ierr );
}
if( (ierr = c_isleap_julian( *year, &leap )) != 0 )
return( ierr );
if( leap )
dpm2use = dpm_leap_idx1;
else
dpm2use = dpm_idx1;
*month = 1;
if( (ierr = c_date2jday_julian( *year, *month, dpm2use[*month], &tjday)) != 0)
return( ierr );
while( jday > tjday ) {
(*month)++;
if( (ierr = c_date2jday_julian( *year, *month, dpm2use[*month], &tjday) != 0))
return( ierr );
}
if( (ierr = c_date2jday_julian( *year, *month, 1, &tjday)) != 0 )
return( ierr );
*day = jday - tjday + 1;
return(0);
}
/*==================================================================================================
* Free the storage associated with a calendar
*/
void ccs_free_calendar( calcalcs_cal *cc )
{
if( cc == NULL )
return;
if( cc->mixed == 1 ) {
ccs_free_calendar( cc->early_cal );
ccs_free_calendar( cc->late_cal );
}
if( cc->sig != CCS_VALID_SIG ) {
fprintf( stderr, "Warning: invalid calendar passed to routine ccs_free_calendar!\n" );
return;
}
cc->sig = 0;
if( cc->name != NULL )
free( cc->name );
free( cc );
}
/**********************************************************************************************/
static int date_gt( int year, int month, int day, int y2, int m2, int d2 )
{
return( ! date_le( year, month, day, y2, m2, d2 ));
}
/**********************************************************************************************/
static int date_lt( int year, int month, int day, int y2, int m2, int d2 )
{
return( ! date_ge( year, month, day, y2, m2, d2 ));
}
/**********************************************************************************************/
static int date_le( int year, int month, int day, int y2, int m2, int d2 ) {
if( year > y2 )
return(0);
if( year < y2 )
return(1);
/* If get here, must be same year */
if( month > m2 )
return(0);
if( month < m2)
return(1);
/* If get here, must be same month */
if( day > d2 )
return(0);
return(1);
}
/**********************************************************************************************/
static int date_ge( int year, int month, int day, int y2, int m2, int d2 ) {
if( year < y2 )
return(0);
if( year > y2 )
return(1);
/* If get here, must be same year */
if( month < m2 )
return(0);
if( month > m2)
return(1);
/* If get here, must be same month */
if( day < d2 )
return(0);
return(1);
}
/**********************************************************************************************/
int c_isleap_never( int year, int *leap )
{
*leap = 0;
return( 0 );
}
/**********************************************************************************************/
int c_date2jday_360_day( int year, int month, int day, int *jday )
{
int spm;
if( day > 30) {
sprintf( error_message, "date %04d-%02d-%02d does not exist in the 360_day calendar",
year, month, day );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
spm = (month-1)*30; /* sum of days in the previous months */
*jday = year*360 + spm + (day-1);
return( 0 );
}
/**********************************************************************************************/
int c_date2jday_noleap( int year, int month, int day, int *jday )
{
if( (month == 2) && (day == 29)) {
sprintf( error_message, "date %04d-%02d-%02d does not exist in the noleap calendar",
year, month, day );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
*jday = year*365 + spm_idx1[month] + (day-1);
return( 0 );
}
/**********************************************************************************************/
int c_jday2date_360_day( int jday, int *year, int *month, int *day )
{
int nextra, yr_offset, doy;
yr_offset = 0;
if( jday < 0 ) {
yr_offset = (-jday)/360+1;
jday += 360*yr_offset;
}
*year = jday/360;
nextra = jday - *year*360;
doy = nextra + 1; /* Julday numbering starts at 0, doy starts at 1 */
*month = nextra/30 + 1;
*day = doy - (*month-1)*30;
*year -= yr_offset;
return(0);
}
/**********************************************************************************************/
int c_jday2date_noleap( int jday, int *year, int *month, int *day )
{
int nextra, yr_offset, doy;
yr_offset = 0;
if( jday < 0 ) {
yr_offset = (-jday)/365+1;
jday += 365*yr_offset;
}
*year = jday/365;
nextra = jday - *year*365;
doy = nextra + 1; /* Julday numbering starts at 0, doy starts at 1 */
*month = 1;
while( doy > spm_idx1[*month + 1] )
*month += 1;
*day = doy - spm_idx1[*month];
*year -= yr_offset;
return(0);
}
/**********************************************************************************************/
int c_dpm_gregorian( int year, int month, int *dpm )
{
int ierr, leap;
if( (month<1) || (month>12)) {
sprintf( error_message, "month %d does not exist in the Gregorian calendar", month );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
if( (ierr = c_isleap_gregorian( year, &leap )) != 0 )
return( ierr );
if( leap )
*dpm = dpm_leap_idx1[month];
else
*dpm = dpm_idx1[month];
return(0);
}
/**********************************************************************************************/
int c_dpm_gregorian_y0( int year, int month, int *dpm )
{
int ierr, leap;
if( (month<1) || (month>12)) {
sprintf( error_message, "month %d does not exist in the Gregorian calendar", month );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
if( (ierr = c_isleap_gregorian_y0( year, &leap )) != 0 )
return( ierr );
if( leap )
*dpm = dpm_leap_idx1[month];
else
*dpm = dpm_idx1[month];
return(0);
}
/**********************************************************************************************/
int c_dpm_julian( int year, int month, int *dpm )
{
int ierr, leap;
if( (month<1) || (month>12)) {
sprintf( error_message, "month %d does not exist in the Julian calendar", month );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
if( (ierr = c_isleap_julian( year, &leap )) != 0 )
return( ierr );
if( leap )
*dpm = dpm_leap_idx1[month];
else
*dpm = dpm_idx1[month];
return(0);
}
/**********************************************************************************************/
int c_dpm_360_day( int year, int month, int *dpm )
{
if( (month<1) || (month>12)) {
sprintf( error_message, "month %d does not exist in the 360_day calendar", month );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
*dpm = 30;
return(0);
}
/**********************************************************************************************/
int c_dpm_noleap( int year, int month, int *dpm )
{
if( (month<1) || (month>12)) {
sprintf( error_message, "month %d does not exist in the noleap calendar", month );
return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
}
*dpm = dpm_idx1[month];
return(0);
}
/*******************************************************************************************/
static int set_xition_extra_info( calcalcs_cal *cal )
{
int ierr;
/* This is the Julian Day of the transition date */
ierr = ccs_date2jday( cal->late_cal, cal->year_x, cal->month_x, cal->day_x, &(cal->jday_x) );
if( ierr != 0 ) {
sprintf( error_message, "Failed to turn the mixed calendar transition day %04d-%02d-%02d in the %s calendar into a Julian day!\n",
cal->year_x, cal->month_x, cal->day_x, cal->name );
return(ierr);
}
/* This is the date of the day BEFORE the transition day,
* i.e., the last day that the early calendar was used
*/
ierr = ccs_jday2date( cal->early_cal, (cal->jday_x-1), &(cal->year_px), &(cal->month_px), &(cal->day_px));
if( ierr != 0 ) {
sprintf( error_message, "Failed to turn the day before the mixed calendar transition day into a date! %s\n",
ccs_err_str(ierr) );
return(ierr);
}
return(0);
}