PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
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 <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
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;
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 
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;
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;
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);
342 
343  if( calendar->mixed ) {
344  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! */
345  c2use = calendar->late_cal;
346  else
347  c2use = calendar->early_cal;
348  }
349  else
350  c2use = calendar;
351 
352  ierr = c2use->c_isleap( year, leap );
353  return( ierr );
354 }
355 
356 /**********************************************************************************************
357  * ccs_dpm: returns the number of days per month for the passed year/month/calendar combo
358  *
359  * Returns 0 on success, and a negative number on error (for example, an illegal month number)
360  */
361 int ccs_dpm( calcalcs_cal *calendar, int year, int month, int *dpm )
362 {
363  int ndays_reg, ierr;
364  int overlap_px_month, overlap_x_month;
365  calcalcs_cal *c2use;
366 
367  if( calendar->mixed ) {
368  /* A calendar transition potentially affects two months -- the month containing the
369  * last day of the old calendar, and the month containing the first day of the
370  * new calendar. If we are in either of those months, things get much harder.
371  * (Note that these can easily be the same month)
372  */
373  overlap_px_month = ((year == calendar->year_px) && (month == calendar->month_px));
374  overlap_x_month = ((year == calendar->year_x ) && (month == calendar->month_x ));
375  if( overlap_px_month || overlap_x_month ) {
376  if( overlap_px_month && (!overlap_x_month)) {
377  /* Last day of the month must have been last day the early calendar was used */
378  *dpm = calendar->day_px;
379  return(0);
380  }
381  else if( overlap_x_month && (!overlap_px_month)) {
382  if( (ierr = ccs_dpm( calendar->late_cal, year, month, &ndays_reg )) != 0 )
383  return( ierr );
384  *dpm = ndays_reg - calendar->day_x + 1;
385  return(0);
386  }
387 
388  else /* overlap_px_month && overlap_x_month */
389  {
390  if( (ierr = ccs_dpm( calendar->late_cal, year, month, &ndays_reg )) != 0 )
391  return( ierr );
392  *dpm = calendar->day_px + (ndays_reg - calendar->day_x + 1);
393  return(0);
394  }
395  }
396  else if( date_ge( year, month, 1, calendar->year_x, calendar->month_x, calendar->day_x ))
397  c2use = calendar->late_cal;
398  else
399  c2use = calendar->early_cal;
400  }
401  else
402  c2use = calendar;
403 
404  return( c2use->c_dpm( year, month, dpm ));
405 }
406 
407 /**********************************************************************************************
408  * ccs_jday2date: give a Julian day number, return the corresponding date in the
409  * selected calendar
410  *
411  * Returns 0 on success, <0 on error and fills string error_message
412  */
413 int ccs_jday2date( calcalcs_cal *calendar, int jday, int *year, int *month, int *day )
414 {
415  calcalcs_cal *c2use;
416 
417  if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
419 
420  if( calendar->mixed ) {
421  if( jday >= calendar->jday_x )
422  c2use = calendar->late_cal;
423  else
424  c2use = calendar->early_cal;
425  }
426  else
427  c2use = calendar;
428 
429  return( c2use->c_jday2date( jday, year, month, day ));
430 }
431 
432 /**********************************************************************************************
433  * ccs_date2jday: given a date, return the (true) Julian day number
434  *
435  * Note that "Julian day number" is not the day number of the year, but rather the
436  * day number starting on Jan 1st 4713 BC (in the proleptic Julian calendar) and
437  * counting consecutively.
438  *
439  * Returns 0 on success, <0 on error and fills string error_message
440  */
441 int ccs_date2jday( calcalcs_cal *calendar, int year, int month, int day, int *jday )
442 {
443  int dpm, ierr;
444  calcalcs_cal *c2use;
445 
446  if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
448 
449  if( calendar->mixed ) {
450  if( date_ge( year, month, day, calendar->year_x, calendar->month_x, calendar->day_x ))
451  c2use = calendar->late_cal;
452  else if( date_le( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ))
453  c2use = calendar->early_cal;
454  else
455  {
456  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)",
457  year, month, day, calendar->name,
458  calendar->early_cal->name,
459  calendar->year_px, calendar->month_px, calendar->day_px,
460  calendar->late_cal->name,
461  calendar->year_x, calendar->month_x, calendar->day_x );
463  }
464  }
465  else
466  c2use = calendar;
467 
468  if( (ierr = ccs_dpm( c2use, year, month, &dpm )) != 0 )
469  return( ierr );
470 
471  if( (month < 1) || (month > 12) || (day < 1) || (day > dpm)) {
472  sprintf( error_message, "date2jday passed an date that is invalid in the %s calendar: %04d-%02d-%02d",
473  c2use->name, year, month, day );
475  }
476 
477  return( c2use->c_date2jday( year, month, day, jday ));
478 }
479 
480 /********************************************************************************************
481  *
482  * ccs_date2doy: given a Y/M/D date, calculates the day number of the year, starting at 1 for
483  * January 1st.
484  *
485  * Returns 0 on success, and a negative value on error (for example, an illegal date [one
486  * that does not exist in the specified calendar])
487  */
488 int ccs_date2doy( calcalcs_cal *calendar, int year, int month, int day, int *doy )
489 {
490  int ierr, jd0, jd1, doy_px, jd_args, xition_date_first_day_of_year,
491  ndays_elapsed;
492  calcalcs_cal *c2use;
493 
494  if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
496 
497  if( calendar->mixed ) {
498 
499  /* If we fall in the twilight zone after the old calendar was stopped but before
500  * the new calendar was used, it's an error
501  */
502  if( date_gt( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ) &&
503  date_lt( year, month, day, calendar->year_x, calendar->month_x, calendar->day_x )) {
504  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)",
505  year, month, day, calendar->name,
506  calendar->early_cal->name,
507  calendar->year_px, calendar->month_px, calendar->day_px,
508  calendar->late_cal->name,
509  calendar->year_x, calendar->month_x, calendar->day_x );
511  }
512 
513  xition_date_first_day_of_year = ((year == calendar->year_x) && (calendar->month_x == 1) && (calendar->day_x == 1));
514  if( (year > calendar->year_x) || xition_date_first_day_of_year )
515  c2use = calendar->late_cal;
516  else if( date_le( year, month, day, calendar->year_px, calendar->month_px, calendar->day_px ))
517  c2use = calendar->early_cal;
518  else
519  {
520  /* Complicated if we are asking for the day of the year during
521  * the transition year and after the transition date. I'm choosing
522  * to define the day numbering during a transition year as
523  * consecutive, which means that the doy for dates
524  * after the transition date equals the doy of the last
525  * day of the earlier calendar plus the number of days
526  * that have elapsed since the transition day.
527  */
528 
529  /* Get the doy of the day BEFORE the transition day
530  * in the earlier calendar
531  */
532  if( (ierr = ccs_date2doy( calendar->early_cal, calendar->year_px, calendar->month_px, calendar->day_px, &doy_px )) != 0 )
533  return( ierr );
534 
535  /* Get number of days that have elapsed between the transition day
536  * and the requested date
537  */
538  if( (ierr = ccs_date2jday( calendar->late_cal, year, month, day, &jd_args )) != 0 )
539  return( ierr );
540  ndays_elapsed = jd_args - calendar->jday_x + 1; /* if this IS the transition day, ndays_elapsed==1 */
541 
542  /* Finally, the day of the year is the day number of the day BEFORE the
543  * transition day plus the number of elapsed days since the
544  * transition day.
545  */
546  *doy = doy_px + ndays_elapsed;
547 
548  return(0);
549  }
550  }
551  else
552  c2use = calendar;
553 
554  /* Get Julian day number of Jan 1st of the specified year */
555  if( (ierr = c2use->c_date2jday( year, 1, 1, &jd0 )) != 0 )
556  return( ierr );
557 
558  /* Get Julian day number of the specified date */
559  if( (ierr = c2use->c_date2jday( year, month, day, &jd1 )) != 0 )
560  return( ierr );
561 
562  *doy = jd1 - jd0 + 1; /* Add 1 because numbering starts at 1 */
563 
564  return(0);
565 }
566 
567 /********************************************************************************************
568  *
569  * ccs_doy2date: given a year and a day number in that year (with counting starting at 1 for
570  * Jan 1st), this returns the month and day of the month that the doy refers to.
571  *
572  * Returns 0 on success, and a negative value on error (for example, a day of the year
573  * that is less than 1 or greater than 366).
574  */
575 int ccs_doy2date( calcalcs_cal *calendar, int year, int doy, int *month, int *day )
576 {
577  int ierr, leap, jd0, jd1, tyear, doy_px, jd_want,
578  xition_date_first_day_of_year, ndays_max;
579  calcalcs_cal *c2use;
580 
581  if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
583 
584  if( calendar->mixed ) {
585  xition_date_first_day_of_year = ((year == calendar->year_x) && (calendar->month_x == 1) && (calendar->day_x == 1));
586  if( year < calendar->year_x )
587  c2use = calendar->early_cal;
588  else if( (year > calendar->year_x) || xition_date_first_day_of_year )
589  c2use = calendar->late_cal;
590  else
591  {
592  /* Get the doy of the day BEFORE the transition day
593  * in the earlier calendar
594  */
595  if( (ierr = ccs_date2doy( calendar->early_cal, calendar->year_px, calendar->month_px, calendar->day_px, &doy_px )) != 0 )
596  return( ierr );
597 
598  /* If our requested doy is before the transition doy, we
599  * can just easily calculate it with the early calendar
600  */
601  if( doy <= doy_px )
602  return( ccs_doy2date( calendar->early_cal, year, doy, month, day ));
603 
604  /* Finally calculate the Julian day we want, and convert it to a date */
605  jd_want = calendar->jday_x + (doy - doy_px - 1);
606  if( (ierr = ccs_jday2date( calendar->late_cal, jd_want, &tyear, month, day)) != 0 )
607  return(ierr);
608 
609  /* If the year we got from that Julian day is different from the original
610  * year specified, it means we have gone off the end of the transition year,
611  * probably because that year has less days than regular years. In that
612  * event, return an error.
613  */
614  if( tyear != year ) {
615  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",
616  year, calendar->name, calendar->year_x, calendar->month_x, calendar->day_x, doy );
618  }
619 
620  return(0);
621  }
622  }
623  else
624  c2use = calendar;
625 
626  /* Check to make sure we are not asking for a doy that does not exist,
627  * esp. as regards to the number of days in leap vs. non-leap years
628  */
629  if( (ierr = c2use->c_isleap( year, &leap )) != 0 )
630  return( ierr );
631  if( leap == 1 )
632  ndays_max = c2use->ndays_leap;
633  else
634  ndays_max = c2use->ndays_reg;
635 
636  if( (doy < 1) || (doy > ndays_max)) {
637  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",
638  doy, year, c2use->name, ndays_max );
640  }
641 
642  /* Get Julian day number of Jan 1st of the specified year */
643  if( (ierr = c2use->c_date2jday( year, 1, 1, &jd0 )) != 0 )
644  return( ierr );
645 
646  /* Calculate new Julian day */
647  jd1 = jd0 + doy - 1;
648 
649  /* Get date for new Julian day */
650  if( (ierr = c2use->c_jday2date( jd1, &tyear, month, day )) != 0 )
651  return( ierr );
652 
653  return(0);
654 }
655 
656 /********************************************************************************************
657  * ccs_dayssince: Given a Y/M/D date in a specified calendar, and the number of days since
658  * that date, this returns the new Y/M/D date in a (possibly different) calendar.
659  *
660  * Note that specifying "zero" days since, and giving different calendars as the original
661  * and new calendars, essentially converts dates between calendars.
662  *
663  * Returns 0 on success, and a negative value on error.
664  */
665 int ccs_dayssince( calcalcs_cal *calendar_orig, int year_orig, int month_orig, int day_orig,
666  int ndays_since, calcalcs_cal *calendar_new, int *year_new, int *month_new, int *day_new )
667 {
668  int ierr, jd0, jd1;
669  calcalcs_cal *c2use_orig, *c2use_new;
670 
671  if( calendar_orig == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
672  if( calendar_orig->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
673 
674  if( calendar_new == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
675  if( calendar_new->sig != CCS_VALID_SIG ) return(CALCALCS_ERR_INVALID_CALENDAR);
676 
677  /* Figure out which calendar of the ORIGINAL calendar to use if it's a mixed calendar
678  */
679  if( calendar_orig->mixed ) {
680  if( date_ge( year_orig, month_orig, day_orig,
681  calendar_orig->year_x, calendar_orig->month_x, calendar_orig->day_x ))
682  c2use_orig = calendar_orig->late_cal;
683  else if( date_le( year_orig, month_orig, day_orig,
684  calendar_orig->year_px, calendar_orig->month_px, calendar_orig->day_px ))
685  c2use_orig = calendar_orig->early_cal;
686  else
687  {
688  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)",
689  year_orig, month_orig, day_orig, calendar_orig->name,
690  calendar_orig->early_cal->name,
691  calendar_orig->year_px, calendar_orig->month_px, calendar_orig->day_px,
692  calendar_orig->late_cal->name,
693  calendar_orig->year_x, calendar_orig->month_x, calendar_orig->day_x );
695  }
696  }
697  else
698  c2use_orig = calendar_orig;
699 
700  /* Get Julian day in the original calendar and date combo */
701  if( (ierr = c2use_orig->c_date2jday( year_orig, month_orig, day_orig, &jd0 )) != 0 )
702  return(ierr);
703 
704  /* Get new Julian day */
705  jd1 = jd0 + ndays_since;
706 
707  if( calendar_new->mixed ) {
708  /* Figure out which calendar of the NEW calendar to use if it's a mixed calendar.
709  */
710  if( jd1 >= calendar_new->jday_x )
711  c2use_new = calendar_new->late_cal;
712  else
713  c2use_new = calendar_new->early_cal;
714  }
715  else
716  c2use_new = calendar_new;
717 
718  /* Convert the new Julian day to a date in the new calendar */
719  if( (ierr = c2use_new->c_jday2date( jd1, year_new, month_new, day_new )) != 0 )
720  return( ierr );
721 
722  return(0);
723 }
724 
725 /********************************************************************************************/
726 static void ccs_gxd_add_country( char *code, char *longname, int year, int month, int day )
727 {
729  fprintf( stderr, "Error, the calcalcs library is attempting to store more country codes than is possible; max is %d\n",
731  fprintf( stderr, "To fix, recompile with a larger number for CCS_MAX_N_COUNTRY_CODES\n" );
732  exit( -1 );
733  }
734 
736  if( ccs_xition_dates[ccs_n_country_codes] == NULL ) {
737  fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code %s\n",
738  code );
739  exit(-1);
740  }
741 
742  ccs_xition_dates[ccs_n_country_codes]->code = (char *)malloc( sizeof(char) * (strlen(code)+1) );
743  if( ccs_xition_dates[ccs_n_country_codes]->code == NULL ) {
744  fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code named %s\n",
745  code );
746  exit(-1);
747  }
748  strcpy( ccs_xition_dates[ccs_n_country_codes]->code, code );
749 
750  ccs_xition_dates[ccs_n_country_codes]->longname = (char *)malloc( sizeof(char) * (strlen(longname)+1) );
751  if( ccs_xition_dates[ccs_n_country_codes]->longname == NULL ) {
752  fprintf( stderr, "calcalcs routine ccs_gxd_add_country: Error trying to allocate space for country code long name %s\n",
753  longname );
754  exit(-1);
755  }
756  strcpy( ccs_xition_dates[ccs_n_country_codes]->longname, longname );
757 
761 
763 }
764 
765 /********************************************************************************************/
767 {
768  ccs_gxd_add_country( "AK", "Alaska", 1867, 10, 18 );
769  ccs_gxd_add_country( "AL", "Albania", 1912, 12, 1 );
770  ccs_gxd_add_country( "AT", "Austria", 1583, 10, 16 );
771  ccs_gxd_add_country( "BE", "Belgium", 1582, 12, 25 );
772  ccs_gxd_add_country( "BG", "Bulgaria", 1916, 4, 1 );
773  ccs_gxd_add_country( "CN", "China", 1929, 1, 1 );
774  ccs_gxd_add_country( "CZ", "Czechoslovakia", 1584, 1, 17 );
775  ccs_gxd_add_country( "DK", "Denmark", 1700, 3, 1 );
776  ccs_gxd_add_country( "NO", "Norway", 1700, 3, 1 );
777  ccs_gxd_add_country( "EG", "Egypt", 1875, 1, 1 );
778  ccs_gxd_add_country( "EE", "Estonia", 1918, 1, 1 );
779  ccs_gxd_add_country( "FI", "Finland", 1753, 3, 1 );
780  ccs_gxd_add_country( "FR", "France", 1582, 12, 20 );
781  ccs_gxd_add_country( "DE", "Germany", 1583, 11, 22 );
782  ccs_gxd_add_country( "UK", "United Kingdom", 1752, 9, 14 );
783  ccs_gxd_add_country( "GR", "Greece", 1924, 3, 23 );
784  ccs_gxd_add_country( "HU", "Hungary", 1587, 11, 1 );
785  ccs_gxd_add_country( "IT", "Italy", 1582, 10, 15 );
786  ccs_gxd_add_country( "JP", "Japan", 1918, 1, 1 );
787  ccs_gxd_add_country( "LV", "Latvia", 1915, 1, 1 );
788  ccs_gxd_add_country( "LT", "Lithuania", 1915, 1, 1 );
789  ccs_gxd_add_country( "LU", "Luxemburg", 1582, 12, 15 );
790  ccs_gxd_add_country( "NL", "Netherlands", 1582, 10, 15 );
791  ccs_gxd_add_country( "PL", "Poland", 1582, 10, 15 );
792  ccs_gxd_add_country( "PT", "Portugal", 1582, 10, 15 );
793  ccs_gxd_add_country( "RO", "Romania", 1919, 4, 14 );
794  ccs_gxd_add_country( "ES", "Spain", 1582, 10, 15 );
795  ccs_gxd_add_country( "SE", "Sweden", 1753, 3, 1 );
796  ccs_gxd_add_country( "CH", "Switzerland", 1584, 1, 22 );
797  ccs_gxd_add_country( "TR", "Turkey", 1927, 1, 1 );
798  ccs_gxd_add_country( "YU", "Yugoslavia", 1919, 1, 1 );
799  ccs_gxd_add_country( "US", "United States", 1752, 9, 14 );
800  ccs_gxd_add_country( "SU", "Soviet Union", 1918, 2, 1 );
801  ccs_gxd_add_country( "RU", "Russia", 1918, 2, 1 );
802 
804 }
805 
806 /********************************************************************************************/
807 int ccs_get_xition_date( const char *country_code, int *year, int *month, int *day )
808 {
809  int i;
810 
813 
814  if( strcmp( country_code, "??" ) == 0 ) {
816  *year = 0;
817  *month = 0;
818  *day = 0;
819  return(0);
820  }
821 
822  /* Find the passed country code in our list */
823  for( i=0; i<ccs_n_country_codes; i++ ) {
824  if( strcmp( country_code, ccs_xition_dates[i]->code ) == 0 ) {
825  *year = ccs_xition_dates[i]->year;
826  *month = ccs_xition_dates[i]->month;
827  *day = ccs_xition_dates[i]->day;
828  return(0);
829  }
830  }
831 
832  /* Maybe they passed a longname? */
833  for( i=0; i<ccs_n_country_codes; i++ ) {
834  if( strcmp( country_code, ccs_xition_dates[i]->longname ) == 0 ) {
835  *year = ccs_xition_dates[i]->year;
836  *month = ccs_xition_dates[i]->month;
837  *day = ccs_xition_dates[i]->day;
838  return(0);
839  }
840  }
841 
842  sprintf( error_message, "ccs_get_xition_date: unknown calendar country/region code: \"%s\". Known codes: ", country_code );
843  for( i=0; i<ccs_n_country_codes; i++ ) {
844  if( (strlen(error_message) + strlen(ccs_xition_dates[i]->code) + strlen(ccs_xition_dates[i]->longname) + 10) < CCS_ERROR_MESSAGE_LEN ) {
845  strcat( error_message, ccs_xition_dates[i]->code );
846  strcat( error_message, " (" );
847  strcat( error_message, ccs_xition_dates[i]->longname );
848  strcat( error_message, ") " );
849  }
850  }
851 
853 }
854 
855 /********************************************************************************************/
856 static void ccs_dump_xition_dates( void )
857 {
858  int i;
859 
860  printf( "Calcalcs library known country codes:\n" );
861  for( i=0; i<ccs_n_country_codes; i++ ) {
862  printf( "Code: %s Transition date: %04d-%02d-%02d Country/Region: %s\n",
863  ccs_xition_dates[i]->code,
864  ccs_xition_dates[i]->year,
865  ccs_xition_dates[i]->month,
866  ccs_xition_dates[i]->day,
867  ccs_xition_dates[i]->longname );
868  if( i%3 == 2 )
869  printf( "\n" );
870  }
871 }
872 
873 /********************************************************************************************/
874 int ccs_set_xition_date( calcalcs_cal *calendar, int year, int month, int day )
875 {
876  int ierr, dpm;
877 
878  if( calendar == NULL ) return(CALCALCS_ERR_NULL_CALENDAR);
880 
881  if( calendar->mixed == 0 )
883 
884  /* Check to make sure the specified date is a valid date in the
885  * LATE calendar (since the transition date is the first date
886  * that the late calendar is used)
887  */
888  if( (ierr = ccs_dpm( calendar->late_cal, year, month, &dpm )) != 0 )
889  return( ierr );
890 
891  if( (month < 1) || (month > 12) || (day < 1) || (day > dpm)) {
892  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 );
894  }
895 
896  calendar->year_x = year;
897  calendar->month_x = month;
898  calendar->day_x = day;
899 
900  if( (ierr = set_xition_extra_info( calendar )) != 0 )
901  return( ierr );
902 
903  return(0);
904 }
905 
906 /********************************************************************************************/
907 char *ccs_err_str( int error_code )
908 {
909  if( error_code == 0 )
910  sprintf( error_message, "no error from calcalcs routines version %f", CALCALCS_VERSION_NUMBER );
911 
912  else if( error_code == CALCALCS_ERR_NULL_CALENDAR )
913  sprintf( error_message, "a NULL calendar was passed to the calcalcs routine" );
914 
915  else if( error_code == CALCALCS_ERR_INVALID_CALENDAR )
916  sprintf( error_message, "an invalid, malformed, previously-freed, or uninitialized calendar was passed to the calcalcs routine" );
917 
918  return( error_message );
919 }
920 
921 /*==================================================================================================
922  * Returns 0 on success, <0 on error.
923  */
924 int c_isleap_julian( int year, int *leap )
925 {
926  int tyear;
927 
928  if( year < -4714 ) {
929  sprintf( error_message, "ccs_isleap: year %d is out of range for the Julian calendar; dates must not be before 4713 B.C.", year);
930  return( CALCALCS_ERR_OUT_OF_RANGE );
931  }
932 
933  if( year == 0 ) {
934  sprintf( error_message, "the Julian calendar has no year 0" );
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 < -4714 ) {
959  sprintf( error_message, "ccs_isleap: year %d is out of range for the Gregorian calendar; dates must not be before 4713 B.C.", year);
960  return( CALCALCS_ERR_OUT_OF_RANGE );
961  }
962 
963  if( year == 0 ) {
964  sprintf( error_message, "the Gregorian calendar has no year 0. Use the \"Gregorian_y0\" calendar if you want to include year 0." );
966  }
967 
968  /* Because there is no year 0 in the gregorian calendar, years -1, -5, -9, etc
969  * are leap years.
970  */
971  if( year < 0 )
972  tyear = year + 1;
973  else
974  tyear = year;
975 
976  *leap = (((tyear % 4) == 0) && ((tyear % 100) != 0)) || ((tyear % 400) == 0);
977 
978  return(0);
979 }
980 
981 /*==================================================================================================
982  * Returns 0 on success, <0 on error.
983  */
984 int c_isleap_gregorian_y0( int year, int *leap )
985 {
986  if( year < -4714 ) {
987  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);
988  return( CALCALCS_ERR_OUT_OF_RANGE );
989  }
990 
991  *leap = (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0);
992 
993  return(0);
994 }
995 
996 /*==================================================================================================
997  * Given a Y/M/D in the Gregorian calendar, this computes the (true) Julian day number of the
998  * specified date. Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian
999  * calendar. The algorithm is based on the "counting" algorithm in the C++ code I obtained from
1000  * Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C.
1001  * In that file, the work is declared to be in the public domain. I modified it by
1002  * extending it to negative years (years BC) in addition to positive years, and to use
1003  * actual Julian Days as the counter. Otherwise, the spirit of the algorithm is similar.
1004  *
1005  * Returns 0 on success, <0 on error.
1006  */
1007 int c_date2jday_gregorian( int year, int month, int day, int *jday )
1008 {
1009  int m, leap, *dpm2use, err;
1010 
1011  if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
1012  sprintf( error_message, "date %04d-%02d-%02d does not exist in the Gregorian calendar",
1013  year, month, day );
1015  }
1016 
1017  if( year == 0 ) {
1018  sprintf( error_message, "year 0 does not exist in the Gregorian calendar. Use the \"Gregorian_y0\" calendar if you want to include year 0" );
1019  return( CALCALCS_ERR_NO_YEAR_ZERO );
1020  }
1021 
1022  /* Limit ourselves to positive Julian Days */
1023  if( year < -4714 ) {
1024  sprintf( error_message, "year %d is out of range of the Gregorian calendar routines; must have year >= -4714", year );
1025  return( CALCALCS_ERR_OUT_OF_RANGE );
1026  }
1027 
1028  /* Following is necessary because Gregorian calendar skips year 0, so the
1029  * offst for negative years is different than offset for positive years
1030  */
1031  if( year < 0 )
1032  year += 4801;
1033  else
1034  year += 4800;
1035 
1036  if( (err = c_isleap_gregorian( year, &leap )) != 0 )
1037  return( err );
1038 
1039  if( leap )
1040  dpm2use = dpm_leap_idx1;
1041  else
1042  dpm2use = dpm_idx1;
1043 
1044  *jday = day;
1045  for( m=month-1; m>0; m-- )
1046  *jday += dpm2use[m];
1047 
1048  *jday += 365*(year-1) + (year-1)/4 - (year-1)/100 + (year-1)/400;
1049 
1050  /* Ajust to "true" Julian days. This constant is how many days difference there is
1051  * between the Julian Day origin date of 4713 BC and our offset date of 4800 BC
1052  */
1053  *jday -= 31739;
1054 
1055  return(0);
1056 }
1057 
1058 /*==================================================================================================
1059  * Given a Y/M/D in the Gregorian calendar, this computes the (true) Julian day number of the
1060  * specified date. Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian
1061  * calendar. The algorithm is based on the "counting" algorithm in the C++ code I obtained from
1062  * Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C.
1063  * In that file, the work is declared to be in the public domain. I modified it by
1064  * extending it to negative years (years BC) in addition to positive years, and to use
1065  * actual Julian Days as the counter. Otherwise, the spirit of the algorithm is similar.
1066  *
1067  * Returns 0 on success, <0 on error.
1068  */
1069 int c_date2jday_gregorian_y0( int year, int month, int day, int *jday )
1070 {
1071  int m, leap, *dpm2use, err;
1072 
1073  if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
1074  sprintf( error_message, "date %04d-%02d-%02d does not exist in the Gregorian calendar",
1075  year, month, day );
1077  }
1078 
1079  /* Limit ourselves to positive Julian Days */
1080  if( year < -4714 ) {
1081  sprintf( error_message, "year %d is out of range of the Gregorian calendar routines; must have year >= -4714", year );
1082  return( CALCALCS_ERR_OUT_OF_RANGE );
1083  }
1084 
1085  year += 4800;
1086 
1087  if( (err = c_isleap_gregorian_y0( year, &leap )) != 0 )
1088  return( err );
1089 
1090  if( leap )
1091  dpm2use = dpm_leap_idx1;
1092  else
1093  dpm2use = dpm_idx1;
1094 
1095  *jday = day;
1096  for( m=month-1; m>0; m-- )
1097  *jday += dpm2use[m];
1098 
1099  *jday += 365*(year-1) + (year-1)/4 - (year-1)/100 + (year-1)/400;
1100 
1101  /* Ajust to "true" Julian days. This constant is how many days difference there is
1102  * between the Julian Day origin date of 4713 BC and our offset date of 4800 BC
1103  */
1104  *jday -= 31739;
1105 
1106  return(0);
1107 }
1108 
1109 /*==========================================================================================
1110  * Given a (true) Julian Day, this converts to a date in the Gregorian calendar.
1111  * Technically, in the proleptic Gregorian calendar, since this works for dates
1112  * back to 4713 BC. Again based on the same public domain code from Edward Reingold's
1113  * web site as the date2jday routine, extended by me to apply to negative years (years BC).
1114  *
1115  * Returns 0 on success, <0 on error.
1116  */
1117 int c_jday2date_gregorian( int jday, int *year, int *month, int *day )
1118 {
1119  int tjday, leap, *dpm2use, ierr, yp1;
1120 
1121  /* Make first estimate for year. We subtract 4714 because Julian Day number
1122  * 0 occurs in year 4714 BC in the Gregorian calendar (recall that it occurs
1123  * in year 4713 BC in the JULIAN calendar
1124  */
1125  *year = jday/366 - 4714;
1126 
1127  /* Advance years until we find the right one */
1128  yp1 = *year + 1;
1129  if( yp1 == 0 ) yp1 = 1; /* no year 0 in the Gregorian calendar */
1130  if( (ierr = c_date2jday_gregorian( yp1, 1, 1, &tjday )) != 0 )
1131  return( ierr );
1132  while( jday >= tjday ) {
1133  (*year)++;
1134  if( *year == 0 )
1135  *year = 1; /* no year 0 in the Gregorian calendar */
1136  yp1 = *year + 1;
1137  if( yp1 == 0 ) yp1 = 1; /* no year 0 in the Gregorian calendar */
1138  if( (ierr = c_date2jday_gregorian( yp1, 1, 1, &tjday )) != 0 )
1139  return( ierr );
1140  }
1141 
1142  if( (ierr = c_isleap_gregorian( *year, &leap )) != 0 )
1143  return( ierr );
1144  if( leap )
1145  dpm2use = dpm_leap_idx1;
1146  else
1147  dpm2use = dpm_idx1;
1148 
1149  *month = 1;
1150  if( (ierr = c_date2jday_gregorian( *year, *month, dpm2use[*month], &tjday)) != 0)
1151  return( ierr );
1152  while( jday > tjday ) {
1153  (*month)++;
1154  if( (ierr = c_date2jday_gregorian( *year, *month, dpm2use[*month], &tjday) != 0))
1155  return( ierr );
1156  }
1157 
1158  if( (ierr = c_date2jday_gregorian( *year, *month, 1, &tjday)) != 0 )
1159  return( ierr );
1160  *day = jday - tjday + 1;
1161 
1162  return(0);
1163 }
1164 
1165 /*==========================================================================================
1166  * Given a (true) Julian Day, this converts to a date in the Gregorian calendar.
1167  * Technically, in the proleptic Gregorian calendar, since this works for dates
1168  * back to 4713 BC. Again based on the same public domain code from Edward Reingold's
1169  * web site as the date2jday routine, extended by me to apply to negative years (years BC).
1170  *
1171  * Returns 0 on success, <0 on error.
1172  */
1173 int c_jday2date_gregorian_y0( int jday, int *year, int *month, int *day )
1174 {
1175  int tjday, leap, *dpm2use, ierr, yp1;
1176 
1177  /* Make first estimate for year. We subtract 4714 because Julian Day number
1178  * 0 occurs in year 4714 BC in the Gregorian calendar (recall that it occurs
1179  * in year 4713 BC in the JULIAN calendar
1180  */
1181  *year = jday/366 - 4715;
1182 
1183  /* Advance years until we find the right one */
1184  yp1 = *year + 1;
1185  if( (ierr = c_date2jday_gregorian_y0( yp1, 1, 1, &tjday )) != 0 )
1186  return( ierr );
1187  while( jday >= tjday ) {
1188  (*year)++;
1189  yp1 = *year + 1;
1190  if( (ierr = c_date2jday_gregorian_y0( yp1, 1, 1, &tjday )) != 0 )
1191  return( ierr );
1192  }
1193 
1194  if( (ierr = c_isleap_gregorian_y0( *year, &leap )) != 0 )
1195  return( ierr );
1196  if( leap )
1197  dpm2use = dpm_leap_idx1;
1198  else
1199  dpm2use = dpm_idx1;
1200 
1201  *month = 1;
1202  if( (ierr = c_date2jday_gregorian_y0( *year, *month, dpm2use[*month], &tjday)) != 0)
1203  return( ierr );
1204  while( jday > tjday ) {
1205  (*month)++;
1206  if( (ierr = c_date2jday_gregorian_y0( *year, *month, dpm2use[*month], &tjday) != 0))
1207  return( ierr );
1208  }
1209 
1210  if( (ierr = c_date2jday_gregorian_y0( *year, *month, 1, &tjday)) != 0 )
1211  return( ierr );
1212  *day = jday - tjday + 1;
1213 
1214  return(0);
1215 }
1216 
1217 /*==================================================================================================
1218  * Given a Y/M/D in the Julian calendar, this computes the (true) Julian day number of the
1219  * specified date. Julian days are counted starting at 0 on 1 Jan 4713 BC using a proleptic Julian
1220  * calendar. The algorithm is based on the "counting" algorithm in the C++ code I obtained from
1221  * Edward M. Reingold's web site at http://emr.cs.uiuc.edu/~reingold/calendar.C.
1222  * In that file, the work is declared to be in the public domain. I modified it by
1223  * extending it to negative years (years BC) in addition to positive years, and to use
1224  * actual Julian Days as the counter. Otherwise, the spirit of the algorithm is similar.
1225  *
1226  * Returns 0 on success, <0 on error.
1227  */
1228 int c_date2jday_julian( int year, int month, int day, int *jday )
1229 {
1230  int m, leap, *dpm2use, err;
1231 
1232  if( (month < 1) || (month > 12) || (day < 1) || (day > 31)) {
1233  sprintf( error_message, "date %04d-%02d-%02d does not exist in the Julian calendar",
1234  year, month, day );
1236  }
1237 
1238  if( year == 0 ) {
1239  sprintf( error_message, "year 0 does not exist in the Julian calendar" );
1240  return( CALCALCS_ERR_NO_YEAR_ZERO );
1241  }
1242 
1243  /* Limit ourselves to positive Julian Days */
1244  if( year < -4713 ) {
1245  sprintf( error_message, "year %d is out of range of the Julian calendar routines; must have year >= -4713", year );
1246  return( CALCALCS_ERR_OUT_OF_RANGE );
1247  }
1248 
1249  /* Following is necessary because Julian calendar skips year 0, so the
1250  * offst for negative years is different than offset for positive years
1251  */
1252  if( year < 0 )
1253  year += 4801;
1254  else
1255  year += 4800;
1256 
1257  if( (err = c_isleap_julian( year, &leap )) != 0 )
1258  return( err );
1259 
1260  if( leap )
1261  dpm2use = dpm_leap_idx1;
1262  else
1263  dpm2use = dpm_idx1;
1264 
1265  *jday = day;
1266  for( m=month-1; m>0; m-- )
1267  *jday += dpm2use[m];
1268 
1269  *jday += 365*(year-1) + (year-1)/4;
1270 
1271  /* Ajust to "true" Julian days. This constant is how many days difference there is
1272  * between the Julian Day origin date of 4713 BC and our offset date of 4800 BC
1273  */
1274  *jday -= 31777;
1275 
1276  return(0);
1277 }
1278 
1279 /*==========================================================================================
1280  * Given a (true) Julian Day, this converts to a date in the Julian calendar.
1281  * Technically, in the proleptic Julian calendar, since this works for dates
1282  * back to 4713 BC. Again based on the same public domain code from Edward Reingold's
1283  * web site as the date2jday routine, extended by me to apply to negative years (years BC).
1284  *
1285  * Returns 0 on success, <0 on error.
1286  */
1287 int c_jday2date_julian( int jday, int *year, int *month, int *day )
1288 {
1289  int tjday, leap, *dpm2use, ierr, yp1;
1290 
1291  /* Make first estimate for year. We subtract 4713 because Julian Day number
1292  * 0 occurs in year 4713 BC in the Julian calendar
1293  */
1294  *year = jday/366 - 4713;
1295 
1296  /* Advance years until we find the right one */
1297  yp1 = *year + 1;
1298  if( yp1 == 0 ) yp1 = 1; /* no year 0 in the Julian calendar */
1299  if( (ierr = c_date2jday_julian( yp1, 1, 1, &tjday )) != 0 )
1300  return( ierr );
1301  while( jday >= tjday ) {
1302  (*year)++;
1303  if( *year == 0 )
1304  *year = 1; /* no year 0 in the Julian calendar */
1305  yp1 = *year + 1;
1306  if( yp1 == 0 ) yp1 = 1; /* no year 0 in the Julian calendar */
1307  if( (ierr = c_date2jday_julian( yp1, 1, 1, &tjday )) != 0 )
1308  return( ierr );
1309  }
1310 
1311  if( (ierr = c_isleap_julian( *year, &leap )) != 0 )
1312  return( ierr );
1313  if( leap )
1314  dpm2use = dpm_leap_idx1;
1315  else
1316  dpm2use = dpm_idx1;
1317 
1318  *month = 1;
1319  if( (ierr = c_date2jday_julian( *year, *month, dpm2use[*month], &tjday)) != 0)
1320  return( ierr );
1321  while( jday > tjday ) {
1322  (*month)++;
1323  if( (ierr = c_date2jday_julian( *year, *month, dpm2use[*month], &tjday) != 0))
1324  return( ierr );
1325  }
1326 
1327  if( (ierr = c_date2jday_julian( *year, *month, 1, &tjday)) != 0 )
1328  return( ierr );
1329  *day = jday - tjday + 1;
1330 
1331  return(0);
1332 }
1333 
1334 /*==================================================================================================
1335  * Free the storage associated with a calendar
1336  */
1338 {
1339  if( cc == NULL )
1340  return;
1341 
1342  if( cc->mixed == 1 ) {
1344  ccs_free_calendar( cc->late_cal );
1345  }
1346 
1347  if( cc->sig != CCS_VALID_SIG ) {
1348  fprintf( stderr, "Warning: invalid calendar passed to routine ccs_free_calendar!\n" );
1349  return;
1350  }
1351 
1352  cc->sig = 0;
1353 
1354  if( cc->name != NULL )
1355  free( cc->name );
1356 
1357  free( cc );
1358 }
1359 
1360 /**********************************************************************************************/
1361 static int date_gt( int year, int month, int day, int y2, int m2, int d2 )
1362 {
1363  return( ! date_le( year, month, day, y2, m2, d2 ));
1364 }
1365 
1366 /**********************************************************************************************/
1367 static int date_lt( int year, int month, int day, int y2, int m2, int d2 )
1368 {
1369  return( ! date_ge( year, month, day, y2, m2, d2 ));
1370 }
1371 
1372 /**********************************************************************************************/
1373 static int date_le( int year, int month, int day, int y2, int m2, int d2 ) {
1374 
1375  if( year > y2 )
1376  return(0);
1377 
1378  if( year < y2 )
1379  return(1);
1380 
1381  /* If get here, must be same year */
1382  if( month > m2 )
1383  return(0);
1384  if( month < m2)
1385  return(1);
1386 
1387  /* If get here, must be same month */
1388  if( day > d2 )
1389  return(0);
1390 
1391  return(1);
1392 }
1393 
1394 /**********************************************************************************************/
1395 static int date_ge( int year, int month, int day, int y2, int m2, int d2 ) {
1396 
1397  if( year < y2 )
1398  return(0);
1399 
1400  if( year > y2 )
1401  return(1);
1402 
1403  /* If get here, must be same year */
1404  if( month < m2 )
1405  return(0);
1406  if( month > m2)
1407  return(1);
1408 
1409  /* If get here, must be same month */
1410  if( day < d2 )
1411  return(0);
1412 
1413  return(1);
1414 }
1415 
1416 /**********************************************************************************************/
1417 int c_isleap_never( int year, int *leap )
1418 {
1419  *leap = 0;
1420  return( 0 );
1421 }
1422 
1423 /**********************************************************************************************/
1424 int c_date2jday_360_day( int year, int month, int day, int *jday )
1425 {
1426  int spm;
1427 
1428  if( day > 30) {
1429  sprintf( error_message, "date %04d-%02d-%02d does not exist in the 360_day calendar",
1430  year, month, day );
1432  }
1433 
1434  spm = (month-1)*30; /* sum of days in the previous months */
1435 
1436  *jday = year*360 + spm + (day-1);
1437 
1438  return( 0 );
1439 }
1440 
1441 /**********************************************************************************************/
1442 int c_date2jday_noleap( int year, int month, int day, int *jday )
1443 {
1444  if( (month == 2) && (day == 29)) {
1445  sprintf( error_message, "date %04d-%02d-%02d does not exist in the noleap calendar",
1446  year, month, day );
1448  }
1449 
1450  *jday = year*365 + spm_idx1[month] + (day-1);
1451 
1452  return( 0 );
1453 }
1454 
1455 /**********************************************************************************************/
1456 int c_jday2date_360_day( int jday, int *year, int *month, int *day )
1457 {
1458  int nextra, yr_offset, doy;
1459 
1460  yr_offset = 0;
1461  if( jday < 0 ) {
1462  yr_offset = (-jday)/360+1;
1463  jday += 360*yr_offset;
1464  }
1465 
1466  *year = jday/360;
1467 
1468  nextra = jday - *year*360;
1469  doy = nextra + 1; /* Julday numbering starts at 0, doy starts at 1 */
1470  *month = nextra/30 + 1;
1471  *day = doy - (*month-1)*30;
1472 
1473  *year -= yr_offset;
1474 
1475  return(0);
1476 }
1477 
1478 /**********************************************************************************************/
1479 int c_jday2date_noleap( int jday, int *year, int *month, int *day )
1480 {
1481  int nextra, yr_offset, doy;
1482 
1483  yr_offset = 0;
1484  if( jday < 0 ) {
1485  yr_offset = (-jday)/365+1;
1486  jday += 365*yr_offset;
1487  }
1488 
1489  *year = jday/365;
1490 
1491  nextra = jday - *year*365;
1492  doy = nextra + 1; /* Julday numbering starts at 0, doy starts at 1 */
1493  *month = 1;
1494  while( doy > spm_idx1[*month + 1] )
1495  *month += 1;
1496 
1497  *day = doy - spm_idx1[*month];
1498 
1499  *year -= yr_offset;
1500 
1501  return(0);
1502 }
1503 
1504 /**********************************************************************************************/
1505 int c_dpm_gregorian( int year, int month, int *dpm )
1506 {
1507  int ierr, leap;
1508 
1509  if( (month<1) || (month>12)) {
1510  sprintf( error_message, "month %d does not exist in the Gregorian calendar", month );
1512  }
1513 
1514  if( (ierr = c_isleap_gregorian( year, &leap )) != 0 )
1515  return( ierr );
1516 
1517  if( leap )
1518  *dpm = dpm_leap_idx1[month];
1519  else
1520  *dpm = dpm_idx1[month];
1521 
1522  return(0);
1523 }
1524 
1525 
1526 /**********************************************************************************************/
1527 int c_dpm_gregorian_y0( int year, int month, int *dpm )
1528 {
1529  int ierr, leap;
1530 
1531  if( (month<1) || (month>12)) {
1532  sprintf( error_message, "month %d does not exist in the Gregorian calendar", month );
1534  }
1535 
1536  if( (ierr = c_isleap_gregorian_y0( year, &leap )) != 0 )
1537  return( ierr );
1538 
1539  if( leap )
1540  *dpm = dpm_leap_idx1[month];
1541  else
1542  *dpm = dpm_idx1[month];
1543 
1544  return(0);
1545 }
1546 
1547 /**********************************************************************************************/
1548 int c_dpm_julian( int year, int month, int *dpm )
1549 {
1550  int ierr, leap;
1551 
1552  if( (month<1) || (month>12)) {
1553  sprintf( error_message, "month %d does not exist in the Julian calendar", month );
1555  }
1556 
1557  if( (ierr = c_isleap_julian( year, &leap )) != 0 )
1558  return( ierr );
1559 
1560  if( leap )
1561  *dpm = dpm_leap_idx1[month];
1562  else
1563  *dpm = dpm_idx1[month];
1564 
1565  return(0);
1566 }
1567 
1568 /**********************************************************************************************/
1569 int c_dpm_360_day( int year, int month, int *dpm )
1570 {
1571  if( (month<1) || (month>12)) {
1572  sprintf( error_message, "month %d does not exist in the 360_day calendar", month );
1574  }
1575 
1576  *dpm = 30;
1577 
1578  return(0);
1579 }
1580 
1581 /**********************************************************************************************/
1582 int c_dpm_noleap( int year, int month, int *dpm )
1583 {
1584  if( (month<1) || (month>12)) {
1585  sprintf( error_message, "month %d does not exist in the noleap calendar", month );
1587  }
1588 
1589  *dpm = dpm_idx1[month];
1590 
1591  return(0);
1592 }
1593 
1594 /*******************************************************************************************/
1596 {
1597  int ierr;
1598 
1599  /* This is the Julian Day of the transition date */
1600  ierr = ccs_date2jday( cal->late_cal, cal->year_x, cal->month_x, cal->day_x, &(cal->jday_x) );
1601  if( ierr != 0 ) {
1602  sprintf( error_message, "Failed to turn the mixed calendar transition day %04d-%02d-%02d in the %s calendar into a Julian day!\n",
1603  cal->year_x, cal->month_x, cal->day_x, cal->name );
1604  return(ierr);
1605  }
1606 
1607  /* This is the date of the day BEFORE the transition day,
1608  * i.e., the last day that the early calendar was used
1609  */
1610  ierr = ccs_jday2date( cal->early_cal, (cal->jday_x-1), &(cal->year_px), &(cal->month_px), &(cal->day_px));
1611  if( ierr != 0 ) {
1612  sprintf( error_message, "Failed to turn the day before the mixed calendar transition day into a date! %s\n",
1613  ccs_err_str(ierr) );
1614  return(ierr);
1615  }
1616 
1617  return(0);
1618 }
1619 
static int c_jday2date_gregorian(int jday, int *year, int *month, int *day)
Definition: calcalcs.c:1117
calcalcs_cal * ccs_init_calendar(const char *calname)
Definition: calcalcs.c:103
static int c_dpm_360_day(int year, int month, int *dpm)
Definition: calcalcs.c:1569
static int c_jday2date_julian(int jday, int *year, int *month, int *day)
Definition: calcalcs.c:1287
static int date_ge(int year, int month, int day, int y2, int m2, int d2)
Definition: calcalcs.c:1395
static void ccs_dump_xition_dates(void)
Definition: calcalcs.c:856
char * ccs_err_str(int error_code)
Definition: calcalcs.c:907
static int c_jday2date_360_day(int jday, int *year, int *month, int *day)
Definition: calcalcs.c:1456
static int c_date2jday_gregorian_y0(int year, int month, int day, int *jday)
Definition: calcalcs.c:1069
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:665
static int set_xition_extra_info(calcalcs_cal *cal)
Definition: calcalcs.c:1595
#define CCS_MAX_N_COUNTRY_CODES
Definition: calcalcs.c:83
int ccs_date2doy(calcalcs_cal *calendar, int year, int month, int day, int *doy)
Definition: calcalcs.c:488
static ccs_country_code * ccs_xition_dates[CCS_MAX_N_COUNTRY_CODES]
Definition: calcalcs.c:85
static int c_isleap_julian(int year, int *leap)
Definition: calcalcs.c:924
static int c_date2jday_noleap(int year, int month, int day, int *jday)
Definition: calcalcs.c:1442
static int c_date2jday_360_day(int year, int month, int day, int *jday)
Definition: calcalcs.c:1424
int ccs_jday2date(calcalcs_cal *calendar, int jday, int *year, int *month, int *day)
Definition: calcalcs.c:413
int ccs_isleap(calcalcs_cal *calendar, int year, int *leap)
Definition: calcalcs.c:335
static int date_le(int year, int month, int day, int y2, int m2, int d2)
Definition: calcalcs.c:1373
static int c_jday2date_noleap(int jday, int *year, int *month, int *day)
Definition: calcalcs.c:1479
static int have_initted_country_codes
Definition: calcalcs.c:86
static void ccs_init_country_database(void)
Definition: calcalcs.c:766
static int c_jday2date_gregorian_y0(int jday, int *year, int *month, int *day)
Definition: calcalcs.c:1173
int ccs_doy2date(calcalcs_cal *calendar, int year, int doy, int *month, int *day)
Definition: calcalcs.c:575
void ccs_free_calendar(calcalcs_cal *cc)
Definition: calcalcs.c:1337
static int c_dpm_noleap(int year, int month, int *dpm)
Definition: calcalcs.c:1582
static int date_lt(int year, int month, int day, int y2, int m2, int d2)
Definition: calcalcs.c:1367
int ccs_date2jday(calcalcs_cal *calendar, int year, int month, int day, int *jday)
Definition: calcalcs.c:441
int ccs_dpm(calcalcs_cal *calendar, int year, int month, int *dpm)
Definition: calcalcs.c:361
#define CCS_ERROR_MESSAGE_LEN
Definition: calcalcs.c:54
static int ccs_n_country_codes
Definition: calcalcs.c:84
#define CCS_VALID_SIG
Definition: calcalcs.c:78
static int c_dpm_gregorian_y0(int year, int month, int *dpm)
Definition: calcalcs.c:1527
static void ccs_gxd_add_country(char *code, char *longname, int year, int month, int day)
Definition: calcalcs.c:726
static int c_date2jday_julian(int year, int month, int day, int *jday)
Definition: calcalcs.c:1228
static int spm_idx1[]
Definition: calcalcs.c:65
int ccs_set_xition_date(calcalcs_cal *calendar, int year, int month, int day)
Definition: calcalcs.c:874
static int c_date2jday_gregorian(int year, int month, int day, int *jday)
Definition: calcalcs.c:1007
static int c_isleap_never(int year, int *leap)
Definition: calcalcs.c:1417
static int c_isleap_gregorian(int year, int *leap)
Definition: calcalcs.c:954
static char error_message[CCS_ERROR_MESSAGE_LEN]
Definition: calcalcs.c:55
static int dpm_leap_idx1[]
Definition: calcalcs.c:62
static int dpm_idx1[]
Definition: calcalcs.c:61
static int c_isleap_gregorian_y0(int year, int *leap)
Definition: calcalcs.c:984
static int date_gt(int year, int month, int day, int y2, int m2, int d2)
Definition: calcalcs.c:1361
static int c_dpm_julian(int year, int month, int *dpm)
Definition: calcalcs.c:1548
static int c_dpm_gregorian(int year, int month, int *dpm)
Definition: calcalcs.c:1505
int ccs_get_xition_date(const char *country_code, int *year, int *month, int *day)
Definition: calcalcs.c:807
#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
std::string printf(const char *format,...)
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
Definition: Time.cc:146
const double d2
Definition: ssafd_code.cc:1
int(* c_jday2date)(int, int *, int *, int *)
Definition: calcalcs.h:36
struct cccalendar * late_cal
Definition: calcalcs.h:45
int sig
Definition: calcalcs.h:30
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