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