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