TECA
The Toolkit for Extreme Climate Analysis
teca_calcalcs.h
Go to the documentation of this file.
1 /*
2 A threadsafe port of the calcalcs library
3 Burlen Loring Thu Apr 22 06:22:16 PM PDT 2021
4 
5 The CalCalcs routines, a set of C-language routines to perform
6 calendar calculations.
7 
8 Version 1.0, released 7 January 2010
9 
10 Copyright (C) 2010 David W. Pierce, dpierce@ucsd.edu
11 
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21 
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 #ifndef calcalcs_h
26 #define calcalcs_h
27 
28 /// @file
29 
30 #include "teca_config.h"
31 
32 #define CALCALCS_VERSION_NUMBER 1.0
33 
34 
35 /// A threadsafe port of the calcalcs library.
36 namespace teca_calcalcs
37 {
38 
39 /// @cond
40 
41 struct cccalendar {
42  int sig;
43  char *name;
44  int ndays_reg, ndays_leap;
45 
46  int (*c_isleap) ( int, int * );
47  int (*c_date2jday) ( int, int, int, int * );
48  int (*c_jday2date) ( int, int *, int *, int * );
49  int (*c_dpm) ( int, int, int * );
50 
51  /* The following implement "mixed" calendars, for example, our standard
52  * civil calendar, which converts from Julian to Gregorian, with the
53  * last day of the Julian calendar being 4 Oct 1582 and the first day
54  * of the Gregorian calendar being 15 Oct 1582
55  */
56  int mixed;
57  struct cccalendar *early_cal, *late_cal;
58  int year_x, month_x, day_x; /* These are the transition Y,M,D, i.e., the FIRST DATE the LATER CAL is used */
59  int year_px, month_px, day_px; /* These are the DAY BEFORE the transition Y,M,D, i.e., the last date the earlier cal is used */
60  int jday_x; /* Julian day of the transition date */
61 };
62 
63 /* A "country code", which holds the 2-letter code (abbreviation) for the country or region,
64  * its long name, and the Y/M/D date that it transitioned from the Julian to Gregorian calendar
65  */
66 struct ccs_ccode {
67  char *code, *longname;
68  int year, month, day;
69 };
70 
71 typedef struct cccalendar calcalcs_cal;
72 typedef struct ccs_ccode ccs_country_code;
73 
74 /* =====================================================================================
75  * Here are all the services this library supplies
76  *
77  * -------------------------------------------------------------------------
78  * ccs_init_calendar : initialize a calendar. Valid passed arguments are
79  * one of the following strings:
80  * Standard, proleptic_Gregorian, proleptic_Julian, noleap (aka 365_day and no_leap), 360_day
81  *
82  * The "Standard" calendar transitions from the Julian to the Gregorian calendar,
83  * with the last day of the Julian calender being 1582-10-04 and the next day being
84  * the first day of the Gregorian calendar, with the date 1582-10-15. This "transition date"
85  * can be set to be the value used by various countries, or set to any arbitrary
86  * date, using routine "set_cal_xition_date", below.
87  */
88 calcalcs_cal *ccs_init_calendar( const char *calname );
89 
90 /*------------------------------------------------------------------------------
91  * Frees the storage previously allocated by ccs_init_calendar()
92  */
93 void ccs_free_calendar( calcalcs_cal *calendar );
94 
95 /*--------------------------------------------------------------------------
96  * ccs_date2jday: turn a Y/M/D date into a (true) Julian day number. Note that
97  * a Julian day is not the day number of the year, but rather
98  * the integer day number starting from 1 on the day that would
99  * have been 1 Jan 4713 BC if the Julian calendar went back
100  * to that time.
101  */
102 int ccs_date2jday( calcalcs_cal *calendar, int year, int month, int day, int *jday );
103 
104 /*--------------------------------------------------------------------------
105  * ccs_jday2date: turn a (true) Julian day number into a calendar date.
106  */
107 int ccs_jday2date( calcalcs_cal *calendar, int jday, int *year, int *month, int *day );
108 
109 /*--------------------------------------------------------------------------
110  * ccs_isleap: determine if the specified year is a leap year in
111  * the specified calendar. return 0 if successful.
112  */
113 int ccs_isleap( calcalcs_cal *calendar, int year, int *leap );
114 
115 /*--------------------------------------------------------------------------
116  * ccs_dpm: returns the days per month for the given year/month.
117  * Note that during the month that transitions from a Julian to a
118  * Gregorian calendar, this might be a strange number of days.
119  */
120 int ccs_dpm( calcalcs_cal *calendar, int year, int month, int *dpm );
121 
122 /*--------------------------------------------------------------------------
123  * ccs_date2doy: given a Y/M/D date, calculates the day number of the year, starting at 1 for
124  * January 1st.
125  */
126 int ccs_date2doy( calcalcs_cal *calendar, int year, int month, int day, int *doy );
127 
128 /*--------------------------------------------------------------------------
129  * ccs_doy2date: given a year and a day number in that year (with counting starting at 1 for
130  * Jan 1st), this returns the month and day of the month that the doy refers to.
131  */
132 int ccs_doy2date( calcalcs_cal *calendar, int year, int doy, int *month, int *day );
133 
134 /*--------------------------------------------------------------------------
135  * ccs_dayssince: Given a Y/M/D date in a specified calendar, and the number of days since
136  * that date, this returns the new Y/M/D date in a (possibly different) calendar.
137  *
138  * Note that specifying "zero" days since, and giving different calendars as the original
139  * and new calendars, essentially converts dates between calendars.
140  */
141 int ccs_dayssince( calcalcs_cal *calendar_orig, int year_orig, int month_orig,
142  int day_orig, int ndays_since, calcalcs_cal *calendar_new,
143  int *year_new, int *month_new, int *day_new );
144 
145 /*--------------------------------------------------------------------------
146  * get/set_cal_xition_date: these routines set the transition date for a Standard
147  * calendar, which is the date that the Gregorian calendar was first used.
148  * Before that, it is assumed that the Julian calendar was used.
149  *
150  * Historically, this transition date varies by country and region. The
151  * variation can be extreme, and over the centuries country boundaries have
152  * changed, so this should be considered only the grossest approximation. For
153  * that matter, for several countries, different districts/regions converted
154  * at different times anyway, so actually doing a good job at this task is
155  * far beyond this library's capability. Nevertheless, this does give some
156  * basic simplified capabilities in this regard. And you can always set
157  * the routines to use an arbitrary transition date of your own calculation.
158  *
159  * How to use these routines:
160  *
161  * If you know the transition date you want to impose, simply call
162  * set_cal_xition_date with the specified transition date. The date
163  * specified is the FIRST date that the new (Gregorian) calendar was
164  * used. For exaple, in Italy, the Gregorian calendar was first used
165  * on 15 October 1582.
166  *
167  * If you don't know what transition date to use, there is a brief
168  * database with some APPROXIMATE dates of transition that can be accessed
169  * by calling get_cal_xition_date with a two-letter country code, corresponding
170  * to the internet suffix for the country. (As a special case, "US" is used
171  * for the United States of America.) If the routine returns 0, then the
172  * country code is recognized and the approximate transition date is returned
173  * in the passed parameters year, month, day. These should then be given to
174  * routine set_cal_xition_date to make that calendar use the specified
175  * transition date. If the get_cal_xition_date routine does not return 0,
176  * then there is no information on that country in the database.
177  *
178  * routine set_cal_xition_date returns 0 on success, and something other than
179  * 0 on an error. Errors include trying to set the transition date to an
180  * invalid date, or trying to set the transition date for any calendar
181  * other than the "Standard" calendar.
182  *
183  * The following country/region codes are recognized: AK (Alaska) 1867/10/18;
184  * AL (Albania) 1912/12/1; AT (Austria) 1583/10/16; BE (Belgium) 1582/12/25;
185  * BG (Bulgaria) 1916/4/1; CN (China) 1929/1/1; CZ (Czechoslovakia) 1584/1/17;
186  * DK (Denmark) 1700/3/1; NO (Norway) 1700/3/1; EG (Egypt) 1875/1/1;
187  * EE (Estonia) 1918/1/1; FI (Finland) 1753/3/1; FR (France) 1582/12/20;
188  * DE (Germany, note different states actually used different dates between
189  * 1583 and 1700!) 1583/11/22; UK (Great Britain and Dominions) 1752/9/14;
190  * GR (Greece) 1924/3/23; HU (Hungary) 1587/11/1; IT (Italy) 1582/10/15;
191  * JP (Japan) 1918/1/1; LV (Latvia) 1915/1/1; LT (Lithuania) 1915/1/1;
192  * LU (Luxemburg) 1582/12/15; NL (Netherlands, note Catholic regions
193  * transitioned in various dates of 1582/83, while Protestant regions
194  * transitioned in various dates of 1700/01) 1582/10/15; NO (Norway) 1700/3/1;
195  * PL (Poland) 1582/10/15; PT (Portugal) 1582/10/15; RO (Romania) 1919/4/14;
196  * ES (Spain) 1582/10/15; SE (Sweden) 1753/3/1; CH (Switzerland, note,
197  * varied bewteen 1584 and 1701 by Canton) 1584/1/22; TR (Turkey)
198  * 1927/1/1; YU (Yugoslavia) 1919/1/1; UK (Great Britain and Dominions) 1752/9/14;
199  * US (United States) 1752/9/14; SU (former Soviet Union) 1918/2/1;
200  * RU (Russia) 1918/2/1.
201  */
202 int ccs_set_xition_date( calcalcs_cal *calendar, int year, int month, int day );
203 int ccs_get_xition_date( const char *country_code, int *year, int *month, int *day );
204 
205 /*--------------------------------------------------------------------------
206  * calcalcs_err_str: return a static char * to an error string, given the error nmmber
207  */
208 char *ccs_err_str(int ccs_errno);
209 
210 #define CALCALCS_ERR_NO_YEAR_ZERO -10
211 #define CALCALCS_ERR_DATE_NOT_IN_CALENDAR -11
212 #define CALCALCS_ERR_INVALID_DAY_OF_YEAR -12
213 #define CALCALCS_ERR_NOT_A_MIXED_CALENDAR -13
214 #define CALCALCS_ERR_UNKNOWN_COUNTRY_CODE -14
215 #define CALCALCS_ERR_OUT_OF_RANGE -15
216 #define CALCALCS_ERR_NULL_CALENDAR -16
217 #define CALCALCS_ERR_INVALID_CALENDAR -17
218 
219 
220 /* The high level API is defined below. This API is thread safe and
221  * has implementations optimized for use with calendar and units strings.
222  */
223 #define UT_ENOINIT -10
224 #define UT_EINVALID -11
225 
226 /// @endcond
227 
228 /** high level thread safe initialize the library and select a calendar
229  * to use in subsequent calls.
230  * return 0 upon success
231  */
233 int set_current_calendar( const char *calendar, const char *units );
234 
235 /** Determine if the specified year is a leap year in the specified calendar.
236  * this wraps ccs_isleap such that initialization is automatically handled and
237  * optimizes for repeat calls. @returns 0 if successful.
238  */
240 int is_leap_year( const char *calendar, const char *units,
241  int year, int &leap );
242 
243 /** Returns the days per month for the given year/month.
244  * Note that during the month that transitions from a Julian to a
245  * Gregorian calendar, this might be a strange number of days. this
246  * wraps ccs_dpm such that initialization is automatically handled and
247  * optimizes for repeat calls. returns 0 on success.
248  */
250 int days_in_month( const char *calendar, const char *units,
251  int year, int month, int &dpm );
252 
253 /** Given a floating point offset in the given calendar return
254  * year, month, day, hour, minute, seconds. returns 0 upon success.
255  */
257 int date( double val, int *year, int *month, int *day, int *hour,
258  int *minute, double *second, const char *dataunits,
259  const char *calendar_name );
260 
261 
262 /** given a year, month, day, hour, minute, second and calendar find
263  * the floating point offset. returns 0 upon success.
264  */
266 int coordinate( int year, int month, int day, int hour, int minute,
267  double second, const char *user_unit, const char *calendar_name,
268  double *value );
269 
270 };
271 
272 #endif
teca_calcalcs::date
TECA_EXPORT int date(double val, int *year, int *month, int *day, int *hour, int *minute, double *second, const char *dataunits, const char *calendar_name)
teca_calcalcs::is_leap_year
TECA_EXPORT int is_leap_year(const char *calendar, const char *units, int year, int &leap)
teca_calcalcs::set_current_calendar
TECA_EXPORT int set_current_calendar(const char *calendar, const char *units)
teca_calcalcs::days_in_month
TECA_EXPORT int days_in_month(const char *calendar, const char *units, int year, int month, int &dpm)
teca_calcalcs
A threadsafe port of the calcalcs library.
Definition: teca_calcalcs.h:36
teca_calcalcs::coordinate
TECA_EXPORT int coordinate(int year, int month, int day, int hour, int minute, double second, const char *user_unit, const char *calendar_name, double *value)
teca_error::TECA_EXPORT
p_teca_error_handler error_handler TECA_EXPORT
The global error handler instance.