tcalcalcs.c - pism - [fork] customized build of PISM, the parallel ice sheet model (tillflux branch)
 (HTM) git clone git://src.adamsgaard.dk/pism
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       tcalcalcs.c (55991B)
       ---
            1 /*
            2     The CalCalcs routines, a set of C-language routines to perform
            3     calendar calculations.
            4 
            5     Version 1.0, released 7 January 2010
            6 
            7     Copyright (C) 2010 David W. Pierce, dpierce@ucsd.edu
            8 
            9     This program is free software: you can redistribute it and/or modify
           10     it under the terms of the GNU General Public License as published by
           11     the Free Software Foundation, either version 3 of the License, or
           12     (at your option) any later version.
           13 
           14     This program is distributed in the hope that it will be useful,
           15     but WITHOUT ANY WARRANTY; without even the implied warranty of
           16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
           17     GNU General Public License for more details.
           18 
           19     You should have received a copy of the GNU General Public License
           20     along with this program.  If not, see <http://www.gnu.org/licenses/>.
           21 */
           22 
           23 #include <stdio.h>
           24 #include <stdlib.h>
           25 #include <unistd.h>
           26 #include <string.h>
           27 #include <ctype.h>
           28 
           29 #include "calcalcs.h"
           30 
           31 static int c_isleap_gregorian        ( int year, int *leap );
           32 static int c_isleap_gregorian_y0( int year, int *leap );
           33 static int c_isleap_julian           ( int year, int *leap );
           34 static int c_isleap_never            ( int year, int *leap );
           35 
           36 static int c_date2jday_julian      ( int year, int month, int day, int *jday );
           37 static int c_date2jday_gregorian   ( int year, int month, int day, int *jday );
           38 static int c_date2jday_gregorian_y0( int year, int month, int day, int *jday );
           39 static int c_date2jday_noleap      ( int year, int month, int day, int *jday );
           40 static int c_date2jday_360_day     ( int year, int month, int day, int *jday );
           41 
           42 static int c_jday2date_julian      ( int jday, int *year, int *month, int *day );
           43 static int c_jday2date_gregorian   ( int jday, int *year, int *month, int *day );
           44 static int c_jday2date_gregorian_y0( int jday, int *year, int *month, int *day );
           45 static int c_jday2date_noleap      ( int jday, int *year, int *month, int *day );
           46 static int c_jday2date_360_day     ( int jday, int *year, int *month, int *day );
           47 
           48 static int c_dpm_julian      ( int year, int month, int *dpm );
           49 static int c_dpm_gregorian   ( int year, int month, int *dpm );
           50 static int c_dpm_gregorian_y0( int year, int month, int *dpm );
           51 static int c_dpm_noleap      ( int year, int month, int *dpm );
           52 static int c_dpm_360_day     ( int year, int month, int *dpm );
           53 
           54 #define CCS_ERROR_MESSAGE_LEN        8192
           55 static char error_message[CCS_ERROR_MESSAGE_LEN];
           56 
           57 /* Following are number of Days Per Month (dpm).  They are called 'idx1' to
           58  * emphasize that they are intended to be index by a month starting at 1
           59  * rather than at 0.
           60  *                     na, jan feb mar apr may jun jul aug sep oct nov dec */
           61 static int dpm_idx1[]      = {-99, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
           62 static int dpm_leap_idx1[] = {-99, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
           63 
           64 /* Same as above, but SUM of previous months.  Indexing starts at 1 for January */
           65 static int spm_idx1[]      = {-99, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
           66 /* 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 */
           67 
           68 static int date_ge( int year, int month, int day, int y2, int m2, int d2 );
           69 static int date_le( int year, int month, int day, int y2, int m2, int d2 );
           70 static int date_lt( int year, int month, int day, int y2, int m2, int d2 );
           71 static int date_gt( int year, int month, int day, int y2, int m2, int d2 );
           72 static int set_xition_extra_info( calcalcs_cal *cal );
           73 static void ccs_dump_xition_dates( void );
           74 static void ccs_gxd_add_country( char *code, char *longname, int year, int month, int day );
           75 static void ccs_init_country_database( void );
           76 
           77 /* Some arbitrary number that is unlikely to be encounterd in a string of random digits */
           78 #define CCS_VALID_SIG        89132412
           79 
           80 /* These implement the database that associates a two-letter country code, such as "UK", 
           81  * with the transition date that the country switched from the Julian to Gregorian calendar
           82  */ 
           83 #define CCS_MAX_N_COUNTRY_CODES        5000
           84 static int ccs_n_country_codes = 0;
           85 static ccs_country_code *ccs_xition_dates[ CCS_MAX_N_COUNTRY_CODES ];
           86 static int have_initted_country_codes = 0;
           87 
           88 /**********************************************************************************************
           89  * Initialize a calendar.  The passed argument is the name of the calendar, and may be
           90  * one of the following character strings:
           91  *        "standard"
           92  *        "proleptic_Julian"
           93  *        "proleptic_Gregorian"
           94  *        "noleap" (aka "365_day" and "no_leap")
           95  *        "360_day"
           96  *
           97  * As a special hack, a calendar can be named "standard_XX" where XX is a two-letter
           98  * date code recognized by ccs_get_xition_date, in which case a standard calendar with
           99  * the specified transition date will be used.
          100  *
          101  * Returns a pointer to the new calendar, or NULL on error.
          102  */
          103 calcalcs_cal *ccs_init_calendar( const char *calname )
          104 {
          105         calcalcs_cal        *retval;
          106         int                use_specified_xition_date, spec_year_x, spec_month_x, spec_day_x;
          107 
          108         error_message[0] = '\0';
          109 
          110         if( strncasecmp( calname, "standard", 8 ) == 0 ) {
          111 
          112                 if( ! have_initted_country_codes )
          113                         ccs_init_country_database();
          114 
          115                 /* See if this is a name of the form "Standard_XX" */
          116                 use_specified_xition_date = 0;
          117                 if( (strlen(calname) >= 11) && (calname[8] == '_')) {
          118                         if( ccs_get_xition_date( calname+9, &spec_year_x, &spec_month_x, &spec_day_x ) != 0 ) {
          119                                 fprintf( stderr, "Error, unknown calendar passed to ccs_init_calendar: \"%s\". Returning NULL\n",
          120                                         calname );
          121                                 return(NULL);
          122                                 }
          123                         use_specified_xition_date = 1;
          124                         }
          125 
          126                 retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
          127                 if( retval == NULL ) {
          128                         fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar. Returning NULL\n" );
          129                         return( NULL );
          130                         }
          131                 retval->sig  = CCS_VALID_SIG;
          132                 retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
          133                 /* new code by CK */
          134                 if ( retval->name == NULL ) {
          135                   free(retval);
          136                   fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar. Returning NULL\n" );
          137                   return( NULL );
          138                 }
          139                 /* end of new code by CK */
          140                 strcpy( retval->name, calname );
          141 
          142                 retval->mixed = 1;
          143                 retval->early_cal = ccs_init_calendar( "proleptic_julian" );
          144                 retval->late_cal  = ccs_init_calendar( "proleptic_gregorian" );
          145 
          146                 /* Following are FIRST DAY the "later" calendar should be used */
          147                 if( use_specified_xition_date == 1 ) {
          148                         retval->year_x    = spec_year_x;
          149                         retval->month_x   = spec_month_x;
          150                         retval->day_x     = spec_day_x;
          151                         }
          152                 else
          153                         {
          154                         retval->year_x    = 1582;
          155                         retval->month_x   = 10;
          156                         retval->day_x     = 15;
          157                         }
          158 
          159                 /* Set the last date the earlier cal was used, and the transition day's Julian date */
          160                 if( set_xition_extra_info( retval ) != 0 ) {
          161                         fprintf( stderr, "calcalcs_init_cal: Error trying to initialize calendar \"%s\": %s. Returning NULL\n",
          162                                 calname, error_message );
          163                         /* new code by CK */
          164                         free(retval->name);
          165                         free(retval);
          166                         /* end of new code by CK */
          167                         return(NULL);
          168                         }
          169                 }
          170 
          171         else if( (strcasecmp( calname, "gregorian" ) == 0) || 
          172                  (strcasecmp( calname, "proleptic_gregorian" ) == 0)) {
          173 
          174                 /* This is a "regular" Gregorian calendar, which does not include "year 0".
          175                  * See also calendar gregorian_y0, which does include a year 0, below
          176                  */
          177                 retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
          178                 if( retval == NULL ) {
          179                         fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
          180                         return( NULL );
          181                         }
          182                 retval->sig  = CCS_VALID_SIG;
          183                 retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
          184                 /* new code by CK */
          185                 if ( retval->name == NULL ) {
          186                   free(retval);
          187                   fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar. Returning NULL\n" );
          188                   return( NULL );
          189                 }
          190                 /* end of new code by CK */
          191                 strcpy( retval->name, calname );
          192                 retval->ndays_reg  = 365;
          193                 retval->ndays_leap = 366;
          194 
          195                 retval->mixed = 0;
          196 
          197                 retval->c_isleap    = &c_isleap_gregorian;
          198                 retval->c_date2jday = &c_date2jday_gregorian;
          199                 retval->c_jday2date = &c_jday2date_gregorian;
          200                 retval->c_dpm       = &c_dpm_gregorian;
          201                 }
          202 
          203         else if( (strcasecmp( calname, "gregorian_y0" ) == 0) || 
          204                  (strcasecmp( calname, "proleptic_gregorian_y0" ) == 0)) {
          205 
          206                 /* This is a Gregorian calendar that includes "year 0".
          207                  */
          208                 retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
          209                 if( retval == NULL ) {
          210                         fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
          211                         return( NULL );
          212                         }
          213                 retval->sig  = CCS_VALID_SIG;
          214                 retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
          215                 /* new code by CK */
          216                 if ( retval->name == NULL ) {
          217                   free(retval);
          218                   fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar. Returning NULL\n" );
          219                   return( NULL );
          220                 }
          221                 /* end of new code by CK */
          222                 strcpy( retval->name, calname );
          223                 retval->ndays_reg  = 365;
          224                 retval->ndays_leap = 366;
          225 
          226                 retval->mixed = 0;
          227 
          228                 retval->c_isleap    = &c_isleap_gregorian_y0;
          229                 retval->c_date2jday = &c_date2jday_gregorian_y0;
          230                 retval->c_jday2date = &c_jday2date_gregorian_y0;
          231                 retval->c_dpm       = &c_dpm_gregorian_y0;
          232                 }
          233 
          234         else if( (strcasecmp( calname, "julian" ) == 0 ) ||
          235                     (strcasecmp( calname, "proleptic_julian" ) == 0 )) {
          236                 retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
          237                 if( retval == NULL ) {
          238                         fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
          239                         return( NULL );
          240                         }
          241                 retval->sig  = CCS_VALID_SIG;
          242                 retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
          243                 /* new code by CK */
          244                 if ( retval->name == NULL ) {
          245                   free(retval);
          246                   fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar. Returning NULL\n" );
          247                   return( NULL );
          248                 }
          249                 /* end of new code by CK */
          250                 strcpy( retval->name, calname );
          251                 retval->ndays_reg  = 365;
          252                 retval->ndays_leap = 366;
          253 
          254                 retval->mixed = 0;
          255 
          256                 retval->c_isleap    = &c_isleap_julian;
          257                 retval->c_date2jday = &c_date2jday_julian;
          258                 retval->c_jday2date = &c_jday2date_julian;
          259                 retval->c_dpm       = &c_dpm_julian;
          260                 }
          261 
          262         else if( (strcasecmp(calname,"noleap")==0) ||
          263                  (strcasecmp(calname,"no_leap")==0) ||
          264                  (strcasecmp(calname,"365_day")==0)) {
          265                 retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
          266                 if( retval == NULL ) {
          267                         fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
          268                         return( NULL );
          269                         }
          270                 retval->sig  = CCS_VALID_SIG;
          271                 retval->name = (char *)malloc( sizeof(char) * (strlen("noleap")+1) );
          272                 /* new code by CK */
          273                 if ( retval->name == NULL ) {
          274                   free(retval);
          275                   fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar. Returning NULL\n" );
          276                   return( NULL );
          277                 }
          278                 /* end of new code by CK */
          279                 strcpy( retval->name, "noleap" );
          280                 retval->ndays_reg  = 365;
          281                 retval->ndays_leap = 365;
          282 
          283                 retval->mixed  = 0;
          284 
          285                 retval->c_isleap    = &c_isleap_never;
          286                 retval->c_date2jday = &c_date2jday_noleap;
          287                 retval->c_jday2date = &c_jday2date_noleap;
          288                 retval->c_dpm       = &c_dpm_noleap;
          289                 }
          290 
          291         else if( strcasecmp(calname,"360_day")==0) {
          292                 retval = (calcalcs_cal *)malloc( sizeof(calcalcs_cal) );
          293                 if( retval == NULL ) {
          294                         fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar\n" );
          295                         return( NULL );
          296                         }
          297                 retval->sig  = CCS_VALID_SIG;
          298                 retval->name = (char *)malloc( sizeof(char) * (strlen(calname)+1) );
          299                 /* new code by CK */
          300                 if ( retval->name == NULL ) {
          301                   free(retval);
          302                   fprintf( stderr, "Error, cannot allocate space for the calcalcs calendar. Returning NULL\n" );
          303                   return( NULL );
          304                 }
          305                 /* end of new code by CK */
          306                 strcpy( retval->name, calname );
          307                 retval->ndays_reg  = 360;
          308                 retval->ndays_leap = 360;
          309 
          310                 retval->mixed  = 0;
          311 
          312                 retval->c_isleap    = &c_isleap_never;
          313                 retval->c_date2jday = &c_date2jday_360_day;
          314                 retval->c_jday2date = &c_jday2date_360_day;
          315                 retval->c_dpm       = &c_dpm_360_day;
          316                 }
          317 
          318         else
          319                 return( NULL );
          320 
          321         return( retval );
          322 }
          323 
          324 /**********************************************************************************************
          325  *
          326  * Determine if the passed year is a leap year in the specified calendar.
          327  * The passed parameter leap is set to '1' if the year is a leap year, and '0' if it is not.
          328  *
          329  * Returns 0 on success, and a negative value on error.
          330  * Errors include the passed year being invalid (before 4713 B.C.) or not existing
          331  * in the specified calendar (i.e., there is no year 0 in either the Gregorian or
          332  * Julian calendars).
          333  * 
          334  */
          335 int ccs_isleap( calcalcs_cal *calendar, int year, int *leap ) 
          336 {
          337         int        ierr;
          338         calcalcs_cal *c2use;
          339 
          340         if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
          341         if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
          342 
          343         if( year < -4714 ) {
          344                 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 );
          345                 return( CALCALCS_ERR_OUT_OF_RANGE );
          346                 }
          347 
          348         if( calendar->mixed ) {
          349                 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! */
          350                         c2use = calendar->late_cal;
          351                 else
          352                         c2use = calendar->early_cal;
          353                 }
          354         else
          355                 c2use = calendar;
          356 
          357         ierr = c2use->c_isleap( year, leap );
          358         return( ierr );
          359 }
          360 
          361 /**********************************************************************************************
          362  * ccs_dpm: returns the number of days per month for the passed year/month/calendar combo
          363  *
          364  * Returns 0 on success, and a negative number on error (for example, an illegal month number)
          365  */
          366 int ccs_dpm( calcalcs_cal *calendar, int year, int month, int *dpm )
          367 {
          368         int                ndays_reg, ierr;
          369         int                overlap_px_month, overlap_x_month;
          370         calcalcs_cal        *c2use;
          371 
          372         if( calendar->mixed ) {
          373                 /* A calendar transition potentially affects two months -- the month containing the
          374                  * last day of the old calendar, and the month containing the first day of the
          375                  * new calendar.  If we are in either of those months, things get much harder.
          376                  * (Note that these can easily be the same month)
          377                  */
          378                 overlap_px_month = ((year == calendar->year_px) && (month == calendar->month_px));
          379                 overlap_x_month  = ((year == calendar->year_x ) && (month == calendar->month_x ));
          380                 if( overlap_px_month || overlap_x_month ) {
          381                         if( overlap_px_month && (!overlap_x_month)) {
          382                                 /* Last day of the month must have been last day the early calendar was used */
          383                                 *dpm = calendar->day_px;
          384                                 return(0);
          385                                 }
          386                         else if( overlap_x_month && (!overlap_px_month)) {
          387                                 if( (ierr = ccs_dpm( calendar->late_cal, year, month, &ndays_reg )) != 0 )
          388                                         return( ierr );
          389                                 *dpm = ndays_reg - calendar->day_x + 1;
          390                                 return(0);
          391                                 }
          392 
          393                         else        /* overlap_px_month && overlap_x_month */
          394                                 {
          395                                 if( (ierr = ccs_dpm( calendar->late_cal, year, month, &ndays_reg )) != 0 )
          396                                         return( ierr );
          397                                 *dpm = calendar->day_px + (ndays_reg - calendar->day_x + 1);
          398                                 return(0);
          399                                 }
          400                         }
          401                 else if( date_ge( year, month, 1, calendar->year_x, calendar->month_x, calendar->day_x ))
          402                         c2use = calendar->late_cal;
          403                 else
          404                         c2use = calendar->early_cal;
          405                 }
          406         else
          407                 c2use = calendar;
          408 
          409         return( c2use->c_dpm( year, month, dpm ));
          410 }
          411 
          412 /**********************************************************************************************
          413  * ccs_jday2date: give a Julian day number, return the corresponding date in the 
          414  *                   selected calendar
          415  *
          416  * Returns 0 on success, <0 on error and fills string error_message
          417  */
          418 int ccs_jday2date( calcalcs_cal *calendar, int jday, int *year, int *month, int *day )
          419 {
          420         calcalcs_cal *c2use;
          421 
          422         if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
          423         if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
          424 
          425         if( calendar->mixed ) {
          426                 if( jday >= calendar->jday_x )
          427                         c2use = calendar->late_cal;
          428                 else
          429                         c2use = calendar->early_cal;
          430                 }
          431         else
          432                 c2use = calendar;
          433 
          434         return( c2use->c_jday2date( jday, year, month, day ));
          435 }
          436 
          437 /**********************************************************************************************
          438  * ccs_date2jday: given a date, return the (true) Julian day number
          439  *
          440  * Note that "Julian day number" is not the day number of the year, but rather the
          441  * day number starting on Jan 1st 4713 BC (in the proleptic Julian calendar) and 
          442  * counting consecutively.
          443  *
          444  * Returns 0 on success, <0 on error and fills string error_message
          445  */
          446 int ccs_date2jday( calcalcs_cal *calendar, int year, int month, int day, int *jday )
          447 {
          448         int                dpm, ierr;
          449         calcalcs_cal        *c2use;
          450 
          451         if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
          452         if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
          453 
          454         if( calendar->mixed ) {
          455                 if( date_ge( year, month, day, calendar->year_x, calendar->month_x, calendar->day_x ))
          456                         c2use = calendar->late_cal;
          457                 else if( date_le( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ))
          458                         c2use = calendar->early_cal;
          459                 else
          460                         {
          461                         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)", 
          462                                 year, month, day, calendar->name, 
          463                                 calendar->early_cal->name,
          464                                 calendar->year_px, calendar->month_px, calendar->day_px,
          465                                 calendar->late_cal->name,
          466                                 calendar->year_x, calendar->month_x, calendar->day_x );
          467                         return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
          468                         }
          469                 }
          470         else
          471                 c2use = calendar;
          472 
          473         if( (ierr = ccs_dpm( c2use, year, month, &dpm )) != 0 )
          474                 return( ierr );
          475 
          476         if( (month < 1) || (month > 12) || (day < 1) || (day > dpm)) {
          477                 sprintf( error_message, "date2jday passed an date that is invalid in the %s calendar: %04d-%02d-%02d", 
          478                         c2use->name, year, month, day );
          479                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
          480                 }
          481 
          482         return( c2use->c_date2jday( year, month, day, jday ));
          483 }
          484 
          485 /********************************************************************************************
          486  *
          487  * ccs_date2doy: given a Y/M/D date, calculates the day number of the year, starting at 1 for
          488  * January 1st.
          489  *
          490  * Returns 0 on success, and a negative value on error (for example, an illegal date [one
          491  * that does not exist in the specified calendar])
          492  */
          493 int ccs_date2doy( calcalcs_cal *calendar, int year, int month, int day, int *doy )
          494 {
          495         int        ierr, jd0, jd1, doy_px, jd_args, xition_date_first_day_of_year,
          496                 ndays_elapsed;
          497         calcalcs_cal        *c2use;
          498 
          499         if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
          500         if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
          501 
          502         if( calendar->mixed ) {
          503 
          504                 /* If we fall in the twilight zone after the old calendar was stopped but before
          505                  * the new calendar was used, it's an error 
          506                  */
          507                 if( date_gt( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ) &&
          508                     date_lt( year, month, day, calendar->year_x,  calendar->month_x,  calendar->day_x )) {
          509                         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)", 
          510                                 year, month, day, calendar->name, 
          511                                 calendar->early_cal->name,
          512                                 calendar->year_px, calendar->month_px, calendar->day_px,
          513                                 calendar->late_cal->name,
          514                                 calendar->year_x, calendar->month_x, calendar->day_x );
          515                         return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
          516                         }
          517 
          518                 xition_date_first_day_of_year = ((year == calendar->year_x) && (calendar->month_x == 1) && (calendar->day_x == 1));
          519                 if( (year > calendar->year_x) || xition_date_first_day_of_year )
          520                         c2use = calendar->late_cal;
          521                 else if( date_le( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ))
          522                         c2use = calendar->early_cal;
          523                 else
          524                         {
          525                         /* Complicated if we are asking for the day of the year during
          526                          * the transition year and after the transition date.  I'm choosing 
          527                          * to define the day numbering during a transition year as 
          528                          * consecutive, which means that the doy for dates
          529                          * after the transition date equals the doy of the last
          530                          * day of the earlier calendar plus the number of days
          531                          * that have elapsed since the transition day.
          532                          */
          533 
          534                         /* Get the doy of the day BEFORE the transition day 
          535                          * in the earlier calendar
          536                         */
          537                         if( (ierr = ccs_date2doy( calendar->early_cal, calendar->year_px, calendar->month_px, calendar->day_px, &doy_px )) != 0 )
          538                                 return( ierr );
          539 
          540                         /* Get number of days that have elapsed between the transition day
          541                          * and the requested date
          542                          */
          543                         if( (ierr = ccs_date2jday( calendar->late_cal, year, month, day, &jd_args )) != 0 )
          544                                 return( ierr );
          545                         ndays_elapsed = jd_args - calendar->jday_x + 1;                /* if this IS the transition day, ndays_elapsed==1 */
          546 
          547                         /* Finally, the day of the year is the day number of the day BEFORE the
          548                          * transition day plus the number of elapsed days since the
          549                          * transition day.
          550                          */
          551                         *doy = doy_px + ndays_elapsed;
          552 
          553                         return(0);
          554                         }
          555                 }
          556         else
          557                 c2use = calendar;
          558 
          559         /* Get Julian day number of Jan 1st of the specified year */
          560         if( (ierr = c2use->c_date2jday( year, 1, 1, &jd0 )) != 0 ) 
          561                 return( ierr );
          562 
          563         /* Get Julian day number of the specified date */
          564         if( (ierr = c2use->c_date2jday( year, month, day, &jd1 )) != 0 ) 
          565                 return( ierr );
          566 
          567         *doy = jd1 - jd0 + 1;        /* Add 1 because numbering starts at 1 */
          568 
          569         return(0);
          570 }
          571 
          572 /********************************************************************************************
          573  *
          574  * ccs_doy2date: given a year and a day number in that year (with counting starting at 1 for 
          575  *         Jan 1st), this returns the month and day of the month that the doy refers to.
          576  *
          577  * Returns 0 on success, and a negative value on error (for example, a day of the year
          578  * that is less than 1 or greater than 366).
          579  */
          580 int ccs_doy2date( calcalcs_cal *calendar, int year, int doy, int *month, int *day )
          581 {
          582         int        ierr, leap, jd0, jd1, tyear, doy_px, jd_want,
          583                 xition_date_first_day_of_year, ndays_max;
          584         calcalcs_cal        *c2use;
          585 
          586         if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
          587         if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
          588 
          589         if( calendar->mixed ) {
          590                 xition_date_first_day_of_year = ((year == calendar->year_x) && (calendar->month_x == 1) && (calendar->day_x == 1));
          591                 if( year < calendar->year_x )
          592                         c2use = calendar->early_cal;
          593                 else if( (year > calendar->year_x) || xition_date_first_day_of_year )
          594                         c2use = calendar->late_cal;
          595                 else
          596                         {
          597                         /* Get the doy of the day BEFORE the transition day 
          598                          * in the earlier calendar
          599                         */
          600                         if( (ierr = ccs_date2doy( calendar->early_cal, calendar->year_px, calendar->month_px, calendar->day_px, &doy_px )) != 0 )
          601                                 return( ierr );
          602 
          603                         /* If our requested doy is before the transition doy, we
          604                          * can just easily calculate it with the early calendar
          605                          */
          606                         if( doy <= doy_px ) 
          607                                 return( ccs_doy2date( calendar->early_cal, year, doy, month, day ));
          608 
          609                         /* Finally calculate the Julian day we want, and convert it to a date */
          610                         jd_want = calendar->jday_x + (doy - doy_px - 1); 
          611                         if( (ierr = ccs_jday2date( calendar->late_cal, jd_want, &tyear, month, day)) != 0 ) 
          612                                 return(ierr);
          613 
          614                         /* If the year we got from that Julian day is different from the original
          615                          * year specified, it means we have gone off the end of the transition year,
          616                          * probably because that year has less days than regular years.  In that
          617                          * event, return an error.
          618                          */
          619                         if( tyear != year ) {
          620                                 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",
          621                                         year, calendar->name, calendar->year_x, calendar->month_x, calendar->day_x, doy );
          622                                 return( CALCALCS_ERR_INVALID_DAY_OF_YEAR );
          623                                 }
          624 
          625                         return(0);
          626                         }
          627                 }
          628         else
          629                 c2use = calendar;
          630 
          631         /* Check to make sure we are not asking for a doy that does not exist,
          632          * esp. as regards to the number of days in leap vs. non-leap years
          633          */
          634         if( (ierr = c2use->c_isleap( year, &leap )) != 0 )
          635                 return( ierr );
          636         if( leap == 1 )
          637                 ndays_max = c2use->ndays_leap;
          638         else
          639                 ndays_max = c2use->ndays_reg;
          640 
          641         if( (doy < 1) || (doy > ndays_max)) {
          642                 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",
          643                         doy, year, c2use->name, ndays_max );
          644                 return( CALCALCS_ERR_INVALID_DAY_OF_YEAR );
          645                 }
          646 
          647         /* Get Julian day number of Jan 1st of the specified year */
          648         if( (ierr = c2use->c_date2jday( year, 1, 1, &jd0 )) != 0 ) 
          649                 return( ierr );
          650 
          651         /* Calculate new Julian day */
          652         jd1 = jd0 + doy - 1;
          653 
          654         /* Get date for new Julian day */
          655         if( (ierr = c2use->c_jday2date( jd1, &tyear, month, day )) != 0 ) 
          656                 return( ierr );
          657 
          658         return(0);
          659 }
          660 
          661 /********************************************************************************************
          662  * ccs_dayssince: Given a Y/M/D date in a specified calendar, and the number of days since 
          663  *        that date, this returns the new Y/M/D date in a (possibly different) calendar.
          664  *
          665  * Note that specifying "zero" days since, and giving different calendars as the original
          666  *        and new calendars, essentially converts dates between calendars.
          667  *
          668  * Returns 0 on success, and a negative value on error.
          669  */
          670 int ccs_dayssince( calcalcs_cal *calendar_orig, int year_orig, int month_orig, int day_orig,
          671                 int ndays_since, calcalcs_cal *calendar_new, int *year_new, int *month_new, int *day_new )
          672 {
          673         int                ierr, jd0, jd1;
          674         calcalcs_cal        *c2use_orig, *c2use_new;
          675 
          676         if( calendar_orig == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
          677         if( calendar_orig->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
          678 
          679         if( calendar_new == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
          680         if( calendar_new->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
          681 
          682         /* Figure out which calendar of the ORIGINAL calendar to use if it's a mixed calendar
          683          */
          684         if( calendar_orig->mixed ) {
          685                 if( date_ge( year_orig, month_orig, day_orig, 
          686                                 calendar_orig->year_x, calendar_orig->month_x, calendar_orig->day_x ))
          687                         c2use_orig = calendar_orig->late_cal;
          688                 else if( date_le( year_orig, month_orig, day_orig, 
          689                                 calendar_orig->year_px, calendar_orig->month_px, calendar_orig->day_px ))
          690                         c2use_orig = calendar_orig->early_cal;
          691                 else
          692                         {
          693                         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)", 
          694                                 year_orig, month_orig, day_orig, calendar_orig->name, 
          695                                 calendar_orig->early_cal->name,
          696                                 calendar_orig->year_px, calendar_orig->month_px, calendar_orig->day_px,
          697                                 calendar_orig->late_cal->name,
          698                                 calendar_orig->year_x, calendar_orig->month_x, calendar_orig->day_x );
          699                         return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
          700                         }
          701                 }
          702         else        
          703                 c2use_orig = calendar_orig;
          704 
          705         /* Get Julian day in the original calendar and date combo */
          706         if( (ierr = c2use_orig->c_date2jday( year_orig, month_orig, day_orig, &jd0 )) != 0 )
          707                 return(ierr);
          708 
          709         /* Get new Julian day */
          710         jd1 = jd0 + ndays_since;
          711 
          712         if( calendar_new->mixed ) {
          713                 /* Figure out which calendar of the NEW calendar to use if it's a mixed calendar.
          714                  */
          715                 if( jd1 >= calendar_new->jday_x )
          716                         c2use_new = calendar_new->late_cal;
          717                 else
          718                         c2use_new = calendar_new->early_cal;
          719                 }
          720         else
          721                 c2use_new = calendar_new;
          722 
          723         /* Convert the new Julian day to a date in the new calendar */
          724         if( (ierr = c2use_new->c_jday2date( jd1, year_new, month_new, day_new )) != 0 )
          725                 return( ierr );
          726         
          727         return(0);
          728 }
          729 
          730 /********************************************************************************************/
          731 static void ccs_gxd_add_country( char *code, char *longname, int year, int month, int day ) 
          732 {
          733         if( ccs_n_country_codes >= CCS_MAX_N_COUNTRY_CODES ) {
          734                 fprintf( stderr, "Error, the calcalcs library is attempting to store more country codes than is possible; max is %d\n",
          735                         CCS_MAX_N_COUNTRY_CODES );
          736                 fprintf( stderr, "To fix, recompile with a larger number for CCS_MAX_N_COUNTRY_CODES\n" );
          737                 exit( -1 );
          738                 }
          739 
          740         ccs_xition_dates[ccs_n_country_codes] = (ccs_country_code *)malloc( sizeof( ccs_country_code ));
          741         if( ccs_xition_dates[ccs_n_country_codes] == NULL ) {
          742                 fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code %s\n",
          743                         code );
          744                 exit(-1);
          745                 }
          746 
          747         ccs_xition_dates[ccs_n_country_codes]->code = (char *)malloc( sizeof(char) * (strlen(code)+1) );
          748         if( ccs_xition_dates[ccs_n_country_codes]->code == NULL ) {
          749                 fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code named %s\n",
          750                         code );
          751                 exit(-1);
          752                 }
          753         strcpy( ccs_xition_dates[ccs_n_country_codes]->code, code );
          754 
          755         ccs_xition_dates[ccs_n_country_codes]->longname = (char *)malloc( sizeof(char) * (strlen(longname)+1) );
          756         if( ccs_xition_dates[ccs_n_country_codes]->longname == NULL ) {
          757                 fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code long name %s\n",
          758                         longname );
          759                 exit(-1);
          760                 }
          761         strcpy( ccs_xition_dates[ccs_n_country_codes]->longname, longname );
          762 
          763          ccs_xition_dates[ccs_n_country_codes]->year  = year;
          764          ccs_xition_dates[ccs_n_country_codes]->month = month;
          765          ccs_xition_dates[ccs_n_country_codes]->day   = day;
          766 
          767         ccs_n_country_codes++;
          768 }
          769 
          770 /********************************************************************************************/
          771 static void ccs_init_country_database()
          772 {
          773         ccs_gxd_add_country( "AK", "Alaska",                 1867, 10, 18 );
          774         ccs_gxd_add_country( "AL", "Albania",                 1912, 12,  1 );
          775         ccs_gxd_add_country( "AT", "Austria",                 1583, 10, 16 );
          776         ccs_gxd_add_country( "BE", "Belgium",                 1582, 12, 25 );
          777         ccs_gxd_add_country( "BG", "Bulgaria",                 1916,  4,  1 );
          778         ccs_gxd_add_country( "CN", "China",                   1929,  1,  1 );
          779         ccs_gxd_add_country( "CZ", "Czechoslovakia",         1584,  1, 17 );
          780         ccs_gxd_add_country( "DK", "Denmark",                1700,  3,  1 );
          781         ccs_gxd_add_country( "NO", "Norway",                 1700,  3,  1 );
          782         ccs_gxd_add_country( "EG", "Egypt",                1875,  1,  1 );
          783         ccs_gxd_add_country( "EE", "Estonia",                1918,  1,  1 );
          784         ccs_gxd_add_country( "FI", "Finland",                1753,  3,  1 );
          785         ccs_gxd_add_country( "FR", "France",                 1582, 12, 20 );
          786         ccs_gxd_add_country( "DE", "Germany",                1583, 11, 22 );
          787         ccs_gxd_add_country( "UK", "United Kingdom",        1752,  9, 14 );
          788         ccs_gxd_add_country( "GR", "Greece",                1924,  3, 23 );
          789         ccs_gxd_add_country( "HU", "Hungary",                1587, 11,  1 );
          790         ccs_gxd_add_country( "IT", "Italy",                1582, 10, 15 );
          791         ccs_gxd_add_country( "JP", "Japan",                1918,  1,  1 );
          792         ccs_gxd_add_country( "LV", "Latvia",                1915,  1,  1 );
          793         ccs_gxd_add_country( "LT", "Lithuania",                1915,  1,  1 );
          794         ccs_gxd_add_country( "LU", "Luxemburg",                1582, 12, 15 );
          795         ccs_gxd_add_country( "NL", "Netherlands",        1582, 10, 15 );
          796         ccs_gxd_add_country( "PL", "Poland",                1582, 10, 15 );
          797         ccs_gxd_add_country( "PT", "Portugal",                1582, 10, 15 );
          798         ccs_gxd_add_country( "RO", "Romania",                1919,  4, 14 );
          799         ccs_gxd_add_country( "ES", "Spain",                1582, 10, 15 );
          800         ccs_gxd_add_country( "SE", "Sweden",                1753,  3,  1 );
          801         ccs_gxd_add_country( "CH", "Switzerland",        1584,  1, 22 );
          802         ccs_gxd_add_country( "TR", "Turkey",                1927,  1,  1 );
          803         ccs_gxd_add_country( "YU", "Yugoslavia",        1919,  1,  1 );
          804         ccs_gxd_add_country( "US", "United States",        1752,  9, 14 );
          805         ccs_gxd_add_country( "SU", "Soviet Union",        1918,  2,  1 );
          806         ccs_gxd_add_country( "RU", "Russia",                1918,  2,  1 );
          807 
          808         have_initted_country_codes = 1;
          809 }
          810 
          811 /********************************************************************************************/
          812 int ccs_get_xition_date( const char *country_code, int *year, int *month, int *day )
          813 {
          814         int        i;
          815 
          816         if( ! have_initted_country_codes )
          817                 ccs_init_country_database();
          818 
          819         if( strcmp( country_code, "??" ) == 0 ) {
          820                 ccs_dump_xition_dates();
          821                 *year  = 0;
          822                 *month = 0;
          823                 *day   = 0;
          824                 return(0);
          825                 }
          826                 
          827         /* Find the passed country code in our list */
          828         for( i=0; i<ccs_n_country_codes; i++ ) {
          829                 if( strcmp( country_code, ccs_xition_dates[i]->code ) == 0 ) {
          830                         *year  =  ccs_xition_dates[i]->year;
          831                         *month =  ccs_xition_dates[i]->month;
          832                         *day   =  ccs_xition_dates[i]->day;
          833                         return(0);
          834                         }
          835                 }
          836 
          837         /* Maybe they passed a longname? */
          838         for( i=0; i<ccs_n_country_codes; i++ ) {
          839                 if( strcmp( country_code, ccs_xition_dates[i]->longname ) == 0 ) {
          840                         *year  =  ccs_xition_dates[i]->year;
          841                         *month =  ccs_xition_dates[i]->month;
          842                         *day   =  ccs_xition_dates[i]->day;
          843                         return(0);
          844                         }
          845                 }
          846 
          847         sprintf( error_message, "ccs_get_xition_date: unknown calendar country/region code: \"%s\". Known codes: ", country_code );
          848         for( i=0; i<ccs_n_country_codes; i++ ) {
          849                 if( (strlen(error_message) + strlen(ccs_xition_dates[i]->code) + strlen(ccs_xition_dates[i]->longname) + 10) < CCS_ERROR_MESSAGE_LEN ) {
          850                         strcat( error_message, ccs_xition_dates[i]->code );
          851                         strcat( error_message, " (" );
          852                         strcat( error_message, ccs_xition_dates[i]->longname );
          853                         strcat( error_message, ") " );
          854                         }
          855                 }
          856 
          857         return(CALCALCS_ERR_UNKNOWN_COUNTRY_CODE);
          858 }
          859 
          860 /********************************************************************************************/
          861 static void ccs_dump_xition_dates( void )
          862 {
          863         int        i;
          864 
          865         printf( "Calcalcs library known country codes:\n" );
          866         for( i=0; i<ccs_n_country_codes; i++ ) {
          867                 printf( "Code: %s     Transition date: %04d-%02d-%02d   Country/Region: %s\n",
          868                         ccs_xition_dates[i]->code,
          869                         ccs_xition_dates[i]->year,
          870                         ccs_xition_dates[i]->month,
          871                         ccs_xition_dates[i]->day,
          872                         ccs_xition_dates[i]->longname );
          873                 if( i%3 == 2 )
          874                         printf( "\n" );
          875                 }
          876 }
          877 
          878 /********************************************************************************************/
          879 int ccs_set_xition_date( calcalcs_cal *calendar, int year, int month, int day )
          880 {
          881         int        ierr, dpm;
          882 
          883         if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
          884         if( calendar->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
          885 
          886         if( calendar->mixed == 0 ) 
          887                 return( CALCALCS_ERR_NOT_A_MIXED_CALENDAR );
          888 
          889         /* Check to make sure the specified date is a valid date in the
          890          * LATE calendar (since the transition date is the first date
          891          * that the late calendar is used)
          892          */
          893          if( (ierr = ccs_dpm( calendar->late_cal, year, month, &dpm )) != 0 )
          894                 return( ierr );
          895 
          896         if( (month < 1) || (month > 12) || (day < 1) || (day > dpm)) {
          897                 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 );
          898                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
          899                 }
          900 
          901         calendar->year_x    = year;
          902         calendar->month_x   = month;
          903         calendar->day_x     = day;
          904 
          905         if( (ierr = set_xition_extra_info( calendar )) != 0 ) 
          906                 return( ierr );
          907 
          908         return(0);
          909 }
          910 
          911 /********************************************************************************************/
          912 char *ccs_err_str( int errno ) 
          913 {
          914         if( errno == 0 ) 
          915                 sprintf( error_message, "no error from calcalcs routines version %f", CALCALCS_VERSION_NUMBER );
          916 
          917         else if( errno == CALCALCS_ERR_NULL_CALENDAR ) 
          918                 sprintf( error_message, "a NULL calendar was passed to the calcalcs routine" );
          919 
          920         else if( errno == CALCALCS_ERR_INVALID_CALENDAR )
          921                 sprintf( error_message, "an invalid, malformed, previously-freed, or uninitialized calendar was passed to the calcalcs routine" );
          922 
          923         return( error_message );
          924 }
          925 
          926 /*==================================================================================================
          927  * Returns 0 on success, <0 on error.
          928  */
          929 int c_isleap_julian( int year, int *leap )
          930 {
          931         int        tyear;
          932 
          933         if( year == 0 ) {
          934                 sprintf( error_message, "the Julian calendar has no year 0" );
          935                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
          936                 }
          937 
          938         /* Because there is no year 0 in the Julian calendar, years -1, -5, -9, etc
          939          * are leap years.
          940          */
          941         if( year < 0 ) 
          942                 tyear = year + 1;
          943         else
          944                 tyear = year;
          945 
          946         *leap = ((tyear % 4) == 0);
          947 
          948         return(0);
          949 }
          950 
          951 /*==================================================================================================
          952  * Returns 0 on success, <0 on error.
          953  */
          954 int c_isleap_gregorian( int year, int *leap )
          955 {
          956         int        tyear;
          957 
          958         if( year == 0 ) {
          959                 sprintf( error_message, "the Gregorian calendar has no year 0. Use the \"Gregorian_y0\" calendar if you want to include year 0." );
          960                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
          961                 }
          962 
          963         /* Because there is no year 0 in the gregorian calendar, years -1, -5, -9, etc
          964          * are leap years.
          965          */
          966         if( year < 0 ) 
          967                 tyear = year + 1;
          968         else
          969                 tyear = year;
          970 
          971         *leap = (((tyear % 4) == 0) && ((tyear % 100) != 0)) || ((tyear % 400) == 0);
          972 
          973         return(0);
          974 }
          975 
          976 /*==================================================================================================
          977  * Returns 0 on success, <0 on error.
          978  */
          979 int c_isleap_gregorian_y0( int year, int *leap )
          980 {
          981         *leap = (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0);
          982 
          983         return(0);
          984 }
          985 
          986 /*==================================================================================================
          987  * Given a Y/M/D in the Gregorian calendar, this computes the (true) Julian day number of the 
          988  * specified date.  Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian 
          989  * calendar.  The algorithm is based on the "counting" algorithm in the C++ code I obtained from 
          990  * Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C. 
          991  * In that file, the work is declared to be in the public domain.  I modified it by 
          992  * extending it to negative years (years BC) in addition to positive years, and to use
          993  * actual Julian Days as the counter.  Otherwise, the spirit of the algorithm is similar.
          994  *
          995  * Returns 0 on success, <0 on error.
          996  */
          997 int c_date2jday_gregorian( int year, int month, int day, int *jday )
          998 {
          999         int        m, leap, *dpm2use, err;
         1000 
         1001         if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
         1002                 sprintf( error_message, "date %04d-%02d-%02d does not exist in the Gregorian calendar", 
         1003                         year, month, day );
         1004                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1005                 }
         1006 
         1007         if( year == 0 ) {
         1008                 sprintf( error_message, "year 0 does not exist in the Gregorian calendar.  Use the \"Gregorian_y0\" calendar if you want to include year 0" );
         1009                 return( CALCALCS_ERR_NO_YEAR_ZERO );
         1010                 }
         1011 
         1012         /* Limit ourselves to positive Julian Days */
         1013         if( year < -4714 ) {        
         1014                 sprintf( error_message, "year %d is out of range of the Gregorian calendar routines; must have year >= -4714", year );
         1015                 return( CALCALCS_ERR_OUT_OF_RANGE );
         1016                 }
         1017 
         1018         /* Following is necessary because Gregorian calendar skips year 0, so the
         1019          * offst for negative years is different than offset for positive years
         1020          */
         1021         if( year < 0 ) 
         1022                 year += 4801;
         1023         else
         1024                 year += 4800;
         1025 
         1026         if( (err = c_isleap_gregorian( year, &leap )) != 0 )
         1027                 return( err );
         1028 
         1029         if( leap )
         1030                 dpm2use = dpm_leap_idx1;
         1031         else
         1032                 dpm2use = dpm_idx1;
         1033 
         1034         *jday = day;
         1035         for( m=month-1; m>0; m-- )
         1036                 *jday += dpm2use[m];
         1037 
         1038         *jday += 365*(year-1) + (year-1)/4 - (year-1)/100 + (year-1)/400;
         1039 
         1040         /* Ajust to "true" Julian days. This constant is how many days difference there is 
         1041          * between the Julian Day origin date of 4713 BC and our offset date of 4800 BC 
         1042          */
         1043         *jday -= 31739;                
         1044 
         1045         return(0);
         1046 }
         1047 
         1048 /*==================================================================================================
         1049  * Given a Y/M/D in the Gregorian calendar, this computes the (true) Julian day number of the 
         1050  * specified date.  Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian 
         1051  * calendar.  The algorithm is based on the "counting" algorithm in the C++ code I obtained from 
         1052  * Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C. 
         1053  * In that file, the work is declared to be in the public domain.  I modified it by 
         1054  * extending it to negative years (years BC) in addition to positive years, and to use
         1055  * actual Julian Days as the counter.  Otherwise, the spirit of the algorithm is similar.
         1056  *
         1057  * Returns 0 on success, <0 on error.
         1058  */
         1059 int c_date2jday_gregorian_y0( int year, int month, int day, int *jday )
         1060 {
         1061         int        m, leap, *dpm2use, err;
         1062 
         1063         if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
         1064                 sprintf( error_message, "date %04d-%02d-%02d does not exist in the Gregorian calendar", 
         1065                         year, month, day );
         1066                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1067                 }
         1068 
         1069         /* Limit ourselves to positive Julian Days */
         1070         if( year < -4714 ) {        
         1071                 sprintf( error_message, "year %d is out of range of the Gregorian calendar routines; must have year >= -4714", year );
         1072                 return( CALCALCS_ERR_OUT_OF_RANGE );
         1073                 }
         1074 
         1075         year += 4800;
         1076 
         1077         if( (err = c_isleap_gregorian_y0( year, &leap )) != 0 )
         1078                 return( err );
         1079 
         1080         if( leap )
         1081                 dpm2use = dpm_leap_idx1;
         1082         else
         1083                 dpm2use = dpm_idx1;
         1084 
         1085         *jday = day;
         1086         for( m=month-1; m>0; m-- )
         1087                 *jday += dpm2use[m];
         1088 
         1089         *jday += 365*(year-1) + (year-1)/4 - (year-1)/100 + (year-1)/400;
         1090 
         1091         /* Ajust to "true" Julian days. This constant is how many days difference there is 
         1092          * between the Julian Day origin date of 4713 BC and our offset date of 4800 BC 
         1093          */
         1094         *jday -= 31739;                
         1095 
         1096         return(0);
         1097 }
         1098 
         1099 /*==========================================================================================
         1100  * Given a (true) Julian Day, this converts to a date in the Gregorian calendar.  
         1101  * Technically, in the proleptic Gregorian calendar, since this works for dates
         1102  * back to 4713 BC.  Again based on the same public domain code from Edward Reingold's
         1103  * web site as the date2jday routine, extended by me to apply to negative years (years BC).
         1104  *
         1105  * Returns 0 on success, <0 on error.
         1106  */
         1107 int c_jday2date_gregorian( int jday, int *year, int *month, int *day )
         1108 {
         1109         int        tjday, leap, *dpm2use, ierr, yp1;
         1110 
         1111         /* Make first estimate for year. We subtract 4714 because Julian Day number
         1112          * 0 occurs in year 4714 BC in the Gregorian calendar (recall that it occurs
         1113          * in year 4713 BC in the JULIAN calendar 
         1114          */
         1115         *year = jday/366 - 4714;
         1116 
         1117         /* Advance years until we find the right one */
         1118         yp1 = *year + 1;
         1119         if( yp1 == 0 ) yp1 = 1;        /* no year 0 in the Gregorian calendar */
         1120         if( (ierr = c_date2jday_gregorian( yp1, 1, 1, &tjday )) != 0 ) 
         1121                 return( ierr );
         1122         while( jday >= tjday ) {
         1123                 (*year)++;
         1124                 if( *year == 0 ) 
         1125                         *year = 1;        /* no year 0 in the Gregorian calendar */
         1126                 yp1 = *year + 1;
         1127                 if( yp1 == 0 ) yp1 = 1;        /* no year 0 in the Gregorian calendar */
         1128                 if( (ierr = c_date2jday_gregorian( yp1, 1, 1, &tjday )) != 0 ) 
         1129                         return( ierr );
         1130                 }
         1131 
         1132         if( (ierr = c_isleap_gregorian( *year, &leap )) != 0 ) 
         1133                 return( ierr );
         1134         if( leap )
         1135                 dpm2use = dpm_leap_idx1;
         1136         else
         1137                 dpm2use = dpm_idx1;
         1138 
         1139         *month = 1;
         1140         if( (ierr = c_date2jday_gregorian( *year, *month, dpm2use[*month], &tjday)) != 0)
         1141                 return( ierr );
         1142         while( jday > tjday ) {
         1143                 (*month)++;
         1144                 if( (ierr = c_date2jday_gregorian( *year, *month, dpm2use[*month], &tjday) != 0))
         1145                         return( ierr );
         1146                 }
         1147 
         1148         if( (ierr = c_date2jday_gregorian( *year, *month, 1, &tjday)) != 0 )
         1149                 return( ierr );
         1150         *day = jday - tjday + 1;
         1151 
         1152         return(0);
         1153 }
         1154 
         1155 /*==========================================================================================
         1156  * Given a (true) Julian Day, this converts to a date in the Gregorian calendar.  
         1157  * Technically, in the proleptic Gregorian calendar, since this works for dates
         1158  * back to 4713 BC.  Again based on the same public domain code from Edward Reingold's
         1159  * web site as the date2jday routine, extended by me to apply to negative years (years BC).
         1160  *
         1161  * Returns 0 on success, <0 on error.
         1162  */
         1163 int c_jday2date_gregorian_y0( int jday, int *year, int *month, int *day )
         1164 {
         1165         int        tjday, leap, *dpm2use, ierr, yp1;
         1166 
         1167         /* Make first estimate for year. We subtract 4714 because Julian Day number
         1168          * 0 occurs in year 4714 BC in the Gregorian calendar (recall that it occurs
         1169          * in year 4713 BC in the JULIAN calendar 
         1170          */
         1171         *year = jday/366 - 4715;
         1172 
         1173         /* Advance years until we find the right one */
         1174         yp1 = *year + 1;
         1175         if( (ierr = c_date2jday_gregorian_y0( yp1, 1, 1, &tjday )) != 0 ) 
         1176                 return( ierr );
         1177         while( jday >= tjday ) {
         1178                 (*year)++;
         1179                 yp1 = *year + 1;
         1180                 if( (ierr = c_date2jday_gregorian_y0( yp1, 1, 1, &tjday )) != 0 ) 
         1181                         return( ierr );
         1182                 }
         1183 
         1184         if( (ierr = c_isleap_gregorian_y0( *year, &leap )) != 0 ) 
         1185                 return( ierr );
         1186         if( leap )
         1187                 dpm2use = dpm_leap_idx1;
         1188         else
         1189                 dpm2use = dpm_idx1;
         1190 
         1191         *month = 1;
         1192         if( (ierr = c_date2jday_gregorian_y0( *year, *month, dpm2use[*month], &tjday)) != 0)
         1193                 return( ierr );
         1194         while( jday > tjday ) {
         1195                 (*month)++;
         1196                 if( (ierr = c_date2jday_gregorian_y0( *year, *month, dpm2use[*month], &tjday) != 0))
         1197                         return( ierr );
         1198                 }
         1199 
         1200         if( (ierr = c_date2jday_gregorian_y0( *year, *month, 1, &tjday)) != 0 )
         1201                 return( ierr );
         1202         *day = jday - tjday + 1;
         1203 
         1204         return(0);
         1205 }
         1206 
         1207 /*==================================================================================================
         1208  * Given a Y/M/D in the Julian calendar, this computes the (true) Julian day number of the 
         1209  * specified date.  Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian 
         1210  * calendar.  The algorithm is based on the "counting" algorithm in the C++ code I obtained from 
         1211  * Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C. 
         1212  * In that file, the work is declared to be in the public domain.  I modified it by 
         1213  * extending it to negative years (years BC) in addition to positive years, and to use
         1214  * actual Julian Days as the counter.  Otherwise, the spirit of the algorithm is similar.
         1215  *
         1216  * Returns 0 on success, <0 on error.
         1217  */
         1218 int c_date2jday_julian( int year, int month, int day, int *jday )
         1219 {
         1220         int        m, leap, *dpm2use, err;
         1221 
         1222         if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
         1223                 sprintf( error_message, "date %04d-%02d-%02d does not exist in the Julian calendar", 
         1224                         year, month, day );
         1225                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1226                 }
         1227 
         1228         if( year == 0 ) {
         1229                 sprintf( error_message, "year 0 does not exist in the Julian calendar" );
         1230                 return( CALCALCS_ERR_NO_YEAR_ZERO );
         1231                 }
         1232 
         1233         /* Limit ourselves to positive Julian Days */
         1234         if( year < -4713 ) {        
         1235                 sprintf( error_message, "year %d is out of range of the Julian calendar routines; must have year >= -4713", year );
         1236                 return( CALCALCS_ERR_OUT_OF_RANGE );
         1237                 }
         1238 
         1239         /* Following is necessary because Julian calendar skips year 0, so the
         1240          * offst for negative years is different than offset for positive years
         1241          */
         1242         if( year < 0 ) 
         1243                 year += 4801;
         1244         else
         1245                 year += 4800;
         1246 
         1247         if( (err = c_isleap_julian( year, &leap )) != 0 )
         1248                 return( err );
         1249 
         1250         if( leap )
         1251                 dpm2use = dpm_leap_idx1;
         1252         else
         1253                 dpm2use = dpm_idx1;
         1254 
         1255         *jday = day;
         1256         for( m=month-1; m>0; m-- )
         1257                 *jday += dpm2use[m];
         1258 
         1259         *jday += 365*(year-1) + (year-1)/4;
         1260 
         1261         /* Ajust to "true" Julian days. This constant is how many days difference there is 
         1262          * between the Julian Day origin date of 4713 BC and our offset date of 4800 BC 
         1263          */
         1264         *jday -= 31777;                
         1265 
         1266         return(0);
         1267 }
         1268 
         1269 /*==========================================================================================
         1270  * Given a (true) Julian Day, this converts to a date in the Julian calendar.  
         1271  * Technically, in the proleptic Julian calendar, since this works for dates
         1272  * back to 4713 BC.  Again based on the same public domain code from Edward Reingold's
         1273  * web site as the date2jday routine, extended by me to apply to negative years (years BC).
         1274  *
         1275  * Returns 0 on success, <0 on error.
         1276  */
         1277 int c_jday2date_julian( int jday, int *year, int *month, int *day )
         1278 {
         1279         int        tjday, leap, *dpm2use, ierr, yp1;
         1280 
         1281         /* Make first estimate for year. We subtract 4713 because Julian Day number
         1282          * 0 occurs in year 4713 BC in the Julian calendar
         1283          */
         1284         *year = jday/366 - 4713;
         1285 
         1286         /* Advance years until we find the right one */
         1287         yp1 = *year + 1;
         1288         if( yp1 == 0 ) yp1 = 1;        /* no year 0 in the Julian calendar */
         1289         if( (ierr = c_date2jday_julian( yp1, 1, 1, &tjday )) != 0 ) 
         1290                 return( ierr );
         1291         while( jday >= tjday ) {
         1292                 (*year)++;
         1293                 if( *year == 0 ) 
         1294                         *year = 1;        /* no year 0 in the Julian calendar */
         1295                 yp1 = *year + 1;
         1296                 if( yp1 == 0 ) yp1 = 1;        /* no year 0 in the Julian calendar */
         1297                 if( (ierr = c_date2jday_julian( yp1, 1, 1, &tjday )) != 0 ) 
         1298                         return( ierr );
         1299                 }
         1300 
         1301         if( (ierr = c_isleap_julian( *year, &leap )) != 0 ) 
         1302                 return( ierr );
         1303         if( leap )
         1304                 dpm2use = dpm_leap_idx1;
         1305         else
         1306                 dpm2use = dpm_idx1;
         1307 
         1308         *month = 1;
         1309         if( (ierr = c_date2jday_julian( *year, *month, dpm2use[*month], &tjday)) != 0)
         1310                 return( ierr );
         1311         while( jday > tjday ) {
         1312                 (*month)++;
         1313                 if( (ierr = c_date2jday_julian( *year, *month, dpm2use[*month], &tjday) != 0))
         1314                         return( ierr );
         1315                 }
         1316 
         1317         if( (ierr = c_date2jday_julian( *year, *month, 1, &tjday)) != 0 )
         1318                 return( ierr );
         1319         *day = jday - tjday + 1;
         1320 
         1321         return(0);
         1322 }
         1323 
         1324 /*==================================================================================================
         1325  * Free the storage associated with a calendar
         1326  */
         1327 void ccs_free_calendar( calcalcs_cal *cc )
         1328 {
         1329         if( cc == NULL )
         1330                 return;
         1331 
         1332         if( cc->mixed == 1 ) {
         1333                 ccs_free_calendar( cc->early_cal );
         1334                 ccs_free_calendar( cc->late_cal );
         1335                 }
         1336 
         1337         if( cc->sig != CCS_VALID_SIG ) {
         1338                 fprintf( stderr, "Warning: invalid calendar passed to routine ccs_free_calendar!\n" );
         1339                 return;
         1340                 }
         1341 
         1342         cc->sig = 0;
         1343 
         1344         if( cc->name != NULL )
         1345                 free( cc->name );
         1346 
         1347         free( cc );
         1348 }
         1349 
         1350 /**********************************************************************************************/
         1351 static int date_gt( int year, int month, int day, int y2, int m2, int d2 ) 
         1352 {
         1353         return( ! date_le( year, month, day, y2, m2, d2 ));
         1354 }
         1355 
         1356 /**********************************************************************************************/
         1357 static int date_lt( int year, int month, int day, int y2, int m2, int d2 ) 
         1358 {
         1359         return( ! date_ge( year, month, day, y2, m2, d2 ));
         1360 }
         1361 
         1362 /**********************************************************************************************/
         1363 static int date_le( int year, int month, int day, int y2, int m2, int d2 ) {
         1364 
         1365         if( year > y2 )
         1366                 return(0);
         1367 
         1368         if( year < y2 )
         1369                 return(1);
         1370 
         1371         /* If get here, must be same year */
         1372         if( month > m2 )
         1373                 return(0);
         1374         if( month < m2)
         1375                 return(1);
         1376 
         1377         /* If get here, must be same month */
         1378         if( day > d2 )
         1379                 return(0);
         1380 
         1381         return(1);
         1382 }
         1383 
         1384 /**********************************************************************************************/
         1385 static int date_ge( int year, int month, int day, int y2, int m2, int d2 ) {
         1386 
         1387         if( year < y2 )
         1388                 return(0);
         1389 
         1390         if( year > y2 )
         1391                 return(1);
         1392 
         1393         /* If get here, must be same year */
         1394         if( month < m2 )
         1395                 return(0);
         1396         if( month > m2)
         1397                 return(1);
         1398 
         1399         /* If get here, must be same month */
         1400         if( day < d2 )
         1401                 return(0);
         1402 
         1403         return(1);
         1404 }
         1405 
         1406 /**********************************************************************************************/
         1407 int c_isleap_never( int year, int *leap )
         1408 {
         1409         *leap = 0;
         1410         return( 0 );
         1411 }
         1412 
         1413 /**********************************************************************************************/
         1414 int c_date2jday_360_day( int year, int month, int day, int *jday )
         1415 {
         1416         int spm;
         1417 
         1418         if( day > 30) {
         1419                 sprintf( error_message, "date %04d-%02d-%02d does not exist in the 360_day calendar",
         1420                         year, month, day );
         1421                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1422                 }
         1423                 
         1424         spm = (month-1)*30;        /* sum of days in the previous months */
         1425 
         1426         *jday = year*360 + spm + (day-1);
         1427 
         1428         return( 0 );
         1429 }
         1430 
         1431 /**********************************************************************************************/
         1432 int c_date2jday_noleap( int year, int month, int day, int *jday )
         1433 {
         1434         if( (month == 2) && (day == 29)) {
         1435                 sprintf( error_message, "date %04d-%02d-%02d does not exist in the noleap calendar",
         1436                         year, month, day );
         1437                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1438                 }
         1439 
         1440         *jday = year*365 + spm_idx1[month] + (day-1);
         1441 
         1442         return( 0 );
         1443 }
         1444 
         1445 /**********************************************************************************************/
         1446 int c_jday2date_360_day( int jday, int *year, int *month, int *day )
         1447 {
         1448         int        nextra, yr_offset, doy;
         1449 
         1450         yr_offset = 0;
         1451         if( jday < 0 ) {
         1452                 yr_offset = (-jday)/360+1;                   
         1453                 jday += 360*yr_offset;
         1454                 }
         1455 
         1456         *year = jday/360;
         1457 
         1458         nextra = jday - *year*360;
         1459         doy    = nextra + 1;        /* Julday numbering starts at 0, doy starts at 1 */
         1460         *month = nextra/30 + 1;
         1461         *day   = doy - (*month-1)*30;
         1462 
         1463         *year -= yr_offset;
         1464 
         1465         return(0);
         1466 }
         1467 
         1468 /**********************************************************************************************/
         1469 int c_jday2date_noleap( int jday, int *year, int *month, int *day )
         1470 {
         1471         int        nextra, yr_offset, doy;
         1472 
         1473         yr_offset = 0;
         1474         if( jday < 0 ) {
         1475                 yr_offset = (-jday)/365+1;                   
         1476                 jday += 365*yr_offset;
         1477                 }
         1478 
         1479         *year = jday/365;
         1480 
         1481         nextra = jday - *year*365;
         1482         doy    = nextra + 1;        /* Julday numbering starts at 0, doy starts at 1 */
         1483         *month = 1;
         1484         while( doy > spm_idx1[*month + 1] )
         1485                 *month += 1;
         1486 
         1487         *day = doy - spm_idx1[*month];
         1488 
         1489         *year -= yr_offset;
         1490 
         1491         return(0);
         1492 }
         1493 
         1494 /**********************************************************************************************/
         1495 int c_dpm_gregorian( int year, int month, int *dpm )
         1496 {
         1497         int        ierr, leap;
         1498 
         1499         if( (month<1) || (month>12)) {
         1500                 sprintf( error_message, "month %d does not exist in the Gregorian calendar", month );
         1501                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1502                 }
         1503 
         1504         if( (ierr = c_isleap_gregorian( year, &leap )) != 0 )
         1505                 return( ierr );
         1506 
         1507         if( leap ) 
         1508                 *dpm = dpm_leap_idx1[month];
         1509         else
         1510                 *dpm = dpm_idx1[month];
         1511 
         1512         return(0);
         1513 }
         1514 
         1515 
         1516 /**********************************************************************************************/
         1517 int c_dpm_gregorian_y0( int year, int month, int *dpm )
         1518 {
         1519         int        ierr, leap;
         1520 
         1521         if( (month<1) || (month>12)) {
         1522                 sprintf( error_message, "month %d does not exist in the Gregorian calendar", month );
         1523                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1524                 }
         1525 
         1526         if( (ierr = c_isleap_gregorian_y0( year, &leap )) != 0 )
         1527                 return( ierr );
         1528 
         1529         if( leap ) 
         1530                 *dpm = dpm_leap_idx1[month];
         1531         else
         1532                 *dpm = dpm_idx1[month];
         1533 
         1534         return(0);
         1535 }
         1536 
         1537 /**********************************************************************************************/
         1538 int c_dpm_julian( int year, int month, int *dpm )
         1539 {
         1540         int        ierr, leap;
         1541 
         1542         if( (month<1) || (month>12)) {
         1543                 sprintf( error_message, "month %d does not exist in the Julian calendar", month );
         1544                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1545                 }
         1546 
         1547         if( (ierr = c_isleap_julian( year, &leap )) != 0 )
         1548                 return( ierr );
         1549 
         1550         if( leap ) 
         1551                 *dpm = dpm_leap_idx1[month];
         1552         else
         1553                 *dpm = dpm_idx1[month];
         1554 
         1555         return(0);
         1556 }
         1557 
         1558 /**********************************************************************************************/
         1559 int c_dpm_360_day( int year, int month, int *dpm )
         1560 {
         1561         if( (month<1) || (month>12)) {
         1562                 sprintf( error_message, "month %d does not exist in the 360_day calendar", month );
         1563                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1564                 }
         1565 
         1566         *dpm = 30;
         1567 
         1568         return(0);
         1569 }
         1570 
         1571 /**********************************************************************************************/
         1572 int c_dpm_noleap( int year, int month, int *dpm )
         1573 {
         1574         if( (month<1) || (month>12)) {
         1575                 sprintf( error_message, "month %d does not exist in the noleap calendar", month );
         1576                 return( CALCALCS_ERR_DATE_NOT_IN_CALENDAR );
         1577                 }
         1578 
         1579         *dpm = dpm_idx1[month];
         1580 
         1581         return(0);
         1582 }
         1583 
         1584 /*******************************************************************************************/
         1585 static int set_xition_extra_info( calcalcs_cal *cal )
         1586 {
         1587         int        ierr;
         1588 
         1589         /* This is the Julian Day of the transition date */
         1590         ierr = ccs_date2jday( cal->late_cal, cal->year_x, cal->month_x, cal->day_x, &(cal->jday_x) );
         1591         if( ierr != 0 ) {
         1592                 sprintf( error_message, "Failed to turn the mixed calendar transition day %04d-%02d-%02d in the %s calendar into a Julian day!\n",
         1593                         cal->year_x, cal->month_x, cal->day_x, cal->name );
         1594                 return(ierr);
         1595                 }
         1596 
         1597         /* This is the date of the day BEFORE the transition day,
         1598          * i.e., the last day that the early calendar was used
         1599          */ 
         1600         ierr = ccs_jday2date( cal->early_cal, (cal->jday_x-1), &(cal->year_px), &(cal->month_px), &(cal->day_px));
         1601         if( ierr != 0 ) {
         1602                 sprintf( error_message, "Failed to turn the day before the mixed calendar transition day into a date! %s\n",
         1603                         ccs_err_str(ierr) );
         1604                 return(ierr);
         1605                 }
         1606 
         1607         return(0);
         1608 }
         1609