1/*********************************************************************
2 *   Copyright 2008, University Corporation for Atmospheric Research
3 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 *   $Id: nctime.c,v 1.9 2010/05/05 22:15:39 dmh Exp $
5 *********************************************************************/
6
7/*
8 * This code was extracted with permission from the CDMS time
9 * conversion and arithmetic routines developed by Bob Drach, Lawrence
10 * Livermore National Laboratory as part of the cdtime library.  Russ
11 * Rew of the UCAR Unidata Program made changes and additions to
12 * support the "-t" option of the netCDF ncdump utility, including a
13 * 366-day climate calendar.
14 *
15 * For the complete time conversion and climate calendar facilities of
16 * the CDMS library, get the original sources from LLNL.
17 */
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <ctype.h>
22#include <math.h>
23#include <string.h>
24#include <stdarg.h>
25#include <assert.h>
26#include "nctime.h"
27
28static int cuErrOpts;      /* Error options */
29static int cuErrorOccurred = 0;      /* True iff cdError was called */
30
31#define CU_FATAL 1      /* Exit immediately on fatal error */
32#define CU_VERBOSE 2      /* Report errors */
33
34#define CD_DEFAULT_BASEYEAR "1979"      /* Default base year for relative time (no 'since' clause) */
35#define VALCMP(a,b) ((a)<(b)?-1:(b)<(a)?1:0)
36
37/* forward declarations */
38static void CdMonthDay(int *doyCdTime *date);
39static void CdDayOfYear(CdTime *date, int *doy);
40static void cdComp2Rel(cdCalenType timetypecdCompTime comptime, char* relunits, double* reltime);
41static void cdRel2CompMixed(double reltimecdUnitTime unitcdCompTime basetimecdCompTime *comptime);
42static void cdRel2Comp(cdCalenType timetype, char* relunits, double reltimecdCompTimecomptime);
43
44/* Trim trailing whitespace, up to n characters. */
45/* If no whitespace up to the last character, set */
46/* the last character to null, else set the first */
47/* whitespace character to null. */
48static void
49cdTrim(char* s, int n)
50{
51 char* c;
52
53 if(s==NULL)
54 return;
55 for(c=s; *c && c<s+n-1 && !isspace((int)*c); c++);
56 *c='\0';
57 return;
58}
59
60static
61void cdError(char *fmt, ...){
62 va_list args;
63
64 cuErrorOccurred = 1;
65 if(cuErrOpts & CU_VERBOSE){
66 va_start(args,fmt);
67 fprintf(stderr, "CDMS error: ");
68 vfprintf(stderrfmtargs);
69 fprintf(stderr, "\n");
70 va_end(args);
71 }
72 if(cuErrOpts & CU_FATAL)
73 exit(1);
74 return;
75}
76
77#define ISLEAP(year,timeType) ((timeType & Cd366) || (((timeType) & CdHasLeap) && (!((year) % 4) && (((timeType) & CdJulianType) || (((year) % 100) || !((year) % 400))))))
78static int mon_day_cnt[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
79static int days_sum[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
80
81/* Compute month and day from year and day-of-year.
82 *
83 * Input:
84 * doy      (int)  (day-of-year)
85 * date->year   (long)  (year since 0 BC)
86 *              date->timeType (CdTimetype) (time type)
87 *              date->baseYear   base year for relative times
88 * Output:
89 * date->month  (short)  (month in year)
90 * date->day    (short)  (day in month)
91 *
92 *
93 * Derived from NRL NEONS V3.6.
94 */
95
96static void
97CdMonthDay(int *doyCdTime *date)
98{
99 int i; /* month counter */
100 int idoy; /* day of year counter */
101 long year;
102
103 if ((idoy = *doy) < 1) {
104 date->month = 0;
105 date->day   = 0;
106 return;
107 }
108
109 if(!(date->timeType & CdChronCal))   /* Ignore year for Clim calendar */
110 year = 0;
111 else if(!(date->timeType & CdBase1970)) /* year is offset from base for relative time */
112 year = date->baseYear + date->year;
113 else
114 year = date->year;
115
116 if (ISLEAP(year,date->timeType)) {
117 mon_day_cnt[1] = 29;
118 } else {
119 mon_day_cnt[1] = 28;
120 }
121 date->month = 0;
122 for (i = 0; i < 12; i++) {
123 (date->month)++;
124 date->day = (short)idoy;
125 if ((idoy -= ((date->timeType & Cd365) ? (mon_day_cnt[date->month-1]) : 30)) <= 0) {
126 return;
127 }
128 }
129 return;
130}
131
132/* Compute day-of-year from year, month and day
133 *
134 * Input:
135 * date->year  (long)  (year since 0 BC)
136 * date->month (short)  (month in year)
137 * date->day   (short)  (day in month)
138 *              date->baseYear   base year for relative times
139 * Output: doy         (int)  (day-of-year)
140 *
141 * Derived from NRL NEONS V3.6
142 */
143
144static void
145CdDayOfYear(CdTime *date, int *doy)
146{
147 int leap_add = 0; /* add 1 day if leap year */
148 int month; /* month */
149 long year;
150
151    monthdate->month;
152 if (month < 1 || month > 12) {
153 cdError( "Day-of-year error; month: %d\n", month);
154 month = 1;
155 }
156
157 if(!(date->timeType & CdChronCal))   /* Ignore year for Clim calendar */
158 year = 0;
159 else if(!(date->timeType & CdBase1970)) /* year is offset from base for relative time */
160 year = date->baseYear + date->year;
161 else
162 year = date->year;
163
164 if (ISLEAP(year,date->timeType) && month > 2) leap_add = 1;
165 if( ((date->timeType) & Cd365) || ((date->timeType) & Cd366) ) {
166     *doy = days_sum[month-1] + date->day + leap_add ;
167 } else { /* date->timeType & Cd360 */
168     *doy = 30*(month-1) + date->day + leap_add ;
169 }
170 return;
171}
172
173/* Convert epochal time (hours since 00 jan 1, 1970)
174 *   to human time (structured)
175 *
176 * Input:
177 *   etime = epochal time representation
178 *   timeType = time type (e.g., CdChron, CdClim, etc.) as defined in cdms.h
179 *   baseYear = base real, used for relative time types only
180 *
181 * Output: htime = human (structured) time representation
182 *
183 * Derived from NRL Neons V3.6
184 */
185void
186Cde2h(double etimeCdTimeType timeType, long baseYearCdTime *htime)
187{
188 long  ytemp; /* temporary year holder */
189 int  yr_day_cnt; /* count of days in year */
190 int  doy; /* day of year */
191 int     daysInLeapYear;      /* number of days in a leap year */
192 int     daysInYear;      /* days in non-leap year */
193
194 doy = (int) floor(etime / 24.) + 1;
195 htime->houretime - (double) (doy - 1) * 24.;
196
197      /* Correct for goofy floor func on J90 */
198 if(htime->hour >= 24.){
199 doy += 1;
200 htime->hour -= 24.;
201 }
202
203 htime->baseYear = (timeType & CdBase1970) ? 1970 : baseYear;
204 if(!(timeType & CdChronCal)) htime->baseYear = 0; /* Set base year to 0 for Clim */
205 if(timeType & Cd366) {
206     daysInLeapYear = 366;
207     daysInYear = 366;
208 } else {
209     daysInLeapYear = (timeType & Cd365) ? 366 : 360;
210     daysInYear = (timeType & Cd365) ? 365 : 360;
211 }
212
213 if (doy > 0) {
214 for (ytemp = htime->baseYear; ; ytemp++) {
215 yr_day_cnt = ISLEAP(ytemp,timeType) ? daysInLeapYear : daysInYear;
216 if (doy <= yr_day_cnt) break;
217 doy -= yr_day_cnt;
218 }
219 } else {
220 for (ytemp = htime->baseYear-1; ; ytemp--) {
221 yr_day_cnt = ISLEAP(ytemp,timeType) ? daysInLeapYear : daysInYear;
222 doy += yr_day_cnt;
223 if (doy > 0) break;
224 }
225 }
226        htime->year = (timeType & CdBase1970) ? ytemp : (ytemp - htime->baseYear);
227 if(!(timeType & CdChronCal)) htime->year = 0; /* Set year to 0 for Clim */
228 htime->timeType = timeType;
229 CdMonthDay(&doy,htime);
230
231        return;
232}
233
234/* Add 'nDel' times 'delTime' to epochal time 'begEtm',
235 * return the result in epochal time 'endEtm'.
236 */
237static void
238CdAddDelTime(double begEtm, long nDelCdDeltaTime delTimeCdTimeType timeType,
239      long baseYear, double *endEtm)
240{
241 double delHours;
242 long delMonthsdelYears;
243 CdTime bhtimeehtime;
244
245 extern void Cde2h(double etimeCdTimeType timeType, long baseYearCdTime *htime);
246 extern void Cdh2e(CdTime *htime, double *etime);
247
248 switch(delTime.units){
249   case CdYear:
250 delMonths = 12;
251 break;
252   case CdSeason:
253 delMonths = 3;
254 break;
255   case CdMonth:
256 delMonths = 1;
257 break;
258   case CdWeek:
259 delHours = 168.0;
260 break;
261   case CdDay:
262 delHours = 24.0;
263 break;
264   case CdHour:
265 delHours = 1.0;
266 break;
267   case CdMinute:
268 delHours = 1./60.;
269 break;
270   case CdSecond:
271 delHours = 1./3600.;
272 break;
273   default:
274 cdError("Invalid delta time units: %d\n",delTime.units);
275 return;
276 }
277
278 switch(delTime.units){
279   case CdYear: case CdSeason: case CdMonth:
280 Cde2h(begEtm,timeType,baseYear,&bhtime);
281 delMonths = delMonths * nDel * delTime.count + bhtime.month - 1;
282 delYears = (delMonths >= 0 ? (delMonths/12) : (delMonths+1)/12 - 1);
283 ehtime.year = bhtime.year + delYears;
284 ehtime.month = (short)(delMonths - (12 * delYears) + 1);
285 ehtime.day = 1;
286 ehtime.hour = 0.0;
287 ehtime.timeType = timeType;
288 ehtime.baseYear = !(timeType & CdChronCal) ? 0 :
289 (timeType & CdBase1970) ? 1970 : baseYear; /* base year is 0 for Clim, */
290    /* 1970 for Chron, */
291    /* or input base year for Rel */
292 Cdh2e(&ehtime,endEtm);
293 break;
294   case CdWeek: case CdDay: case CdHour: case CdMinute: case CdSecond:
295 delHours = delHours * (double)(nDel * delTime.count);
296 *endEtm = begEtm + delHours;
297 break;
298   default: break;
299 }
300 return;
301}
302
303/* Parse relative units, returning the unit and base component time. */
304/* Function returns 1 if error, 0 on success */
305int
306cdParseRelunits(cdCalenType timetype, char* relunitscdUnitTimeunitcdCompTimebase_comptime)
307{
308 char charunits[CD_MAX_RELUNITS];
309 char basetime_1[CD_MAX_CHARTIME];
310 char basetime_2[CD_MAX_CHARTIME];
311 char basetime[CD_MAX_CHARTIME];
312 int nconv1nconv2nconv;
313
314      /* Parse the relunits */
315 /* Allow ISO-8601 "T" date-time separator as well as blank separator */
316 nconv1 = sscanf(relunits,"%s since %[^T]T%s",charunits,basetime_1,basetime_2);
317 if(nconv1==EOF || nconv1==0){
318 cdError("Error on relative units conversion, string = %s\n",relunits);
319 return 1;
320 }
321 nconv2 = sscanf(relunits,"%s since %s %s",charunits,basetime_1,basetime_2);
322 if(nconv2==EOF || nconv2==0){
323 cdError("Error on relative units conversion, string = %s\n",relunits);
324 return 1;
325 }
326 if(nconv1 < nconv2) {
327     nconv = nconv2;
328 } else {
329     nconv = sscanf(relunits,"%s since %[^T]T%s",charunits,basetime_1,basetime_2);
330 }
331      /* Get the units */
332 cdTrim(charunits,CD_MAX_RELUNITS);
333 if(!strncmp(charunits,"sec",3) || !strcmp(charunits,"s")){
334 *unit = cdSecond;
335 }
336 else if(!strncmp(charunits,"min",3) || !strcmp(charunits,"mn")){
337 *unit = cdMinute;
338 }
339 else if(!strncmp(charunits,"hour",4) || !strcmp(charunits,"hr")){
340 *unit = cdHour;
341 }
342 else if(!strncmp(charunits,"day",3) || !strcmp(charunits,"dy")){
343 *unit = cdDay;
344 }
345 else if(!strncmp(charunits,"week",4) || !strcmp(charunits,"wk")){
346 *unit = cdWeek;
347 }
348 else if(!strncmp(charunits,"month",5) || !strcmp(charunits,"mo")){
349 *unit = cdMonth;
350 }
351 else if(!strncmp(charunits,"season",6)){
352 *unit = cdSeason;
353 }
354 else if(!strncmp(charunits,"year",4) || !strcmp(charunits,"yr")){
355 if(!(timetype & cdStandardCal)){
356 cdError("Error on relative units conversion: climatological units cannot be 'years'.\n");
357 return 1;
358 }
359 *unit = cdYear;
360 }
361 else {
362 cdError("Error on relative units conversion: invalid units = %s\n",charunits);
363 return 1;
364 }
365
366      /* Build the basetime, if any (default is 1979), */
367      /* or month 1 for climatological time. */
368 if(nconv == 1){
369 if(timetype & cdStandardCal)
370 strcpy(basetime,CD_DEFAULT_BASEYEAR);
371 else
372 strcpy(basetime,"1");
373 }
374      /* Convert the basetime to component, then epochal (hours since 1970) */
375 else{
376 if(nconv == 2){
377 cdTrim(basetime_1,CD_MAX_CHARTIME);
378 strcpy(basetime,basetime_1);
379 }
380 else{
381 cdTrim(basetime_1,CD_MAX_CHARTIME);
382 cdTrim(basetime_2,CD_MAX_CHARTIME);
383 sprintf(basetime,"%s %s",basetime_1,basetime_2);
384 }
385 }
386
387 cdChar2Comp(timetypebasetimebase_comptime);
388
389 return 0;
390}
391
392/* ca - cb in Gregorian calendar */
393/* Result is in hours. */
394static double
395cdDiffGregorian(cdCompTime cacdCompTime cb){
396
397 double relarelb;
398
399 cdComp2Rel(cdStandardca, "hours", &rela);
400 cdComp2Rel(cdStandardcb, "hours", &relb);
401 return (rela - relb);
402}
403
404/* Return -1, 0, 1 as ca is less than, equal to, */
405/* or greater than cb, respectively. */
406static int
407cdCompCompare(cdCompTime cacdCompTime cb){
408
409 int test;
410
411 if ((test = VALCMP(ca.yearcb.year)))
412 return test;
413 else if ((test = VALCMP(ca.monthcb.month)))
414 return test;
415 else if ((test = VALCMP(ca.daycb.day)))
416 return test;
417 else
418 return (VALCMP(ca.hourcb.hour));
419}
420
421/* ca - cb in Julian calendar.  Result is in hours. */
422static double
423cdDiffJulian(cdCompTime cacdCompTime cb){
424
425 double relarelb;
426
427 cdComp2Rel(cdJulianca, "hours", &rela);
428 cdComp2Rel(cdJuliancb, "hours", &relb);
429 return (rela - relb);
430}
431
432/* ca - cb in mixed Julian/Gregorian calendar. */
433/* Result is in hours. */
434static double
435cdDiffMixed(cdCompTime cacdCompTime cb){
436
437 static cdCompTime ZA = {1582, 10, 5, 0.0};
438 static cdCompTime ZB = {1582, 10, 15, 0.0};
439 double result;
440
441 if (cdCompCompare(cbZB) == -1){
442 if (cdCompCompare(caZB) == -1) {
443 result = cdDiffJulian(cacb);
444 }
445 else {
446 result = cdDiffGregorian(caZB) + cdDiffJulian(ZAcb);
447 }
448 }
449 else {
450 if (cdCompCompare(caZB) == -1){
451 result = cdDiffJulian(caZA) + cdDiffGregorian(ZBcb);
452 }
453 else {
454 result = cdDiffGregorian(cacb);
455 }
456 }
457 return result;
458}
459
460/* Divide ('endEtm' - 'begEtm') by 'delTime',
461 * return the integer portion of the result in 'nDel'.
462 */
463static void
464CdDivDelTime(double begEtm, double endEtmCdDeltaTime delTimeCdTimeType timeType,
465      long baseYear, long *nDel)
466{
467 double delHoursfrange;
468 long delMonthsrange;
469 CdTime bhtimeehtime;
470 int hoursInYear;
471
472 extern void Cde2h(double etimeCdTimeType timeType, long baseYearCdTime *htime);
473
474 switch(delTime.units){
475   case CdYear:
476 delMonths = 12;
477 break;
478   case CdSeason:
479 delMonths = 3;
480 break;
481   case CdMonth:
482 delMonths = 1;
483 break;
484   case CdWeek:
485 delHours = 168.0;
486 break;
487   case CdDay:
488 delHours = 24.0;
489 break;
490   case CdHour:
491 delHours = 1.0;
492 break;
493   case CdMinute:
494 delHours = 1./60.;
495 break;
496   case CdSecond:
497 delHours = 1./3600.;
498 break;
499   default:
500 cdError("Invalid delta time units: %d\n",delTime.units);
501 return;
502 }
503
504 switch(delTime.units){
505   case CdYear: case CdSeason: case CdMonth:
506 delMonths *= delTime.count;
507 Cde2h(begEtm,timeType,baseYear,&bhtime);
508 Cde2h(endEtm,timeType,baseYear,&ehtime);
509 if(timeType & CdChronCal){   /* Chron and Rel time */
510 range = 12*(ehtime.year - bhtime.year)
511 + (ehtime.month - bhtime.month);
512 }
513 else{      /* Clim time, ignore year */
514 range = (ehtime.month - bhtime.month);
515 if(range < 0) range += 12;
516 }
517 *nDel = abs((int)range)/delMonths;
518 break;
519   case CdWeek: case CdDay: case CdHour: case CdMinute: case CdSecond:
520 delHours *= (double)delTime.count;
521 if(timeType & CdChronCal){   /* Chron and Rel time */
522 frange = fabs(endEtm - begEtm);
523 }
524 else{      /* Clim time, ignore year, but */
525      /* wraparound relative to hours-in-year*/
526 frange = endEtm - begEtm;
527 if(timeType & Cd366) {
528     hoursInYear = 8784;
529 } else {
530     hoursInYear = (timeType & Cd365) ? 8760. : 8640.;
531 }
532      /* Normalize frange to interval [0,hoursInYear) */
533 if(frange < 0.0 || frange >= hoursInYear)
534 frange -= hoursInYear * floor(frange/hoursInYear);
535 }
536 *nDel = (long)((frange + 1.e-10*delHours)/delHours);
537 break;
538     default: break;
539 }
540 return;
541}
542
543/* Value is in hours. Translate to units. */
544static double
545cdFromHours(double valuecdUnitTime unit){
546 double result;
547
548 switch(unit){
549 case cdSecond:
550 result = value * 3600.0;
551 break;
552 case cdMinute:
553 result = value * 60.0;
554 break;
555 case cdHour:
556 result = value;
557 break;
558 case cdDay:
559 result = value/24.0;
560 break;
561 case cdWeek:
562 result = value/168.0;
563 break;
564 case cdMonth:
565 case cdSeason:
566 case cdYear:
567 case cdFraction:
568 default:
569     cdError("Error on conversion from hours to vague unit");
570 result = 0;
571 break;
572 }
573 return result;
574}
575      /* Map to old timetypes */
576static int
577cdToOldTimetype(cdCalenType newtypeCdTimeTypeoldtype)
578{
579 switch(newtype){
580   case cdStandard:
581 *oldtype = CdChron;
582 break;
583   case cdJulian:
584 *oldtype = CdJulianCal;
585 break;
586   case cdNoLeap:
587 *oldtype = CdChronNoLeap;
588 break;
589   case cd360:
590 *oldtype = CdChron360;
591 break;
592   case cd366:
593 *oldtype = CdChron366;
594 break;
595   case cdClim:
596 *oldtype = CdClim;
597 break;
598   case cdClimLeap:
599 *oldtype = CdClimLeap;
600 break;
601   case cdClim360:
602 *oldtype = CdClim360;
603 break;
604   default:
605 cdError("Error on relative units conversion, invalid timetype = %d",newtype);
606 return 1;
607 }
608 return 0;
609}
610
611/* Convert human time to epochal time (hours since 00 jan 1, 1970)
612 *
613 * Input: htime = human time representation
614 *
615 * Output: etime = epochal time representation
616 *
617 * Derived from NRL Neons V3.6
618 */
619void
620Cdh2e(CdTime *htime, double *etime)
621{
622 long  ytempyear; /* temporary year holder */
623 int day_cnt; /* count of days */
624 int  doy; /* day of year */
625 long    baseYear;      /* base year for epochal time */
626 int     daysInLeapYear;      /* number of days in a leap year */
627 int     daysInYear;      /* days in non-leap year */
628
629 CdDayOfYear(htime,&doy);
630
631 day_cnt = 0;
632
633 baseYear = ((htime->timeType) & CdBase1970) ? 1970 : htime->baseYear;
634 year = ((htime->timeType) & CdBase1970) ? htime->year : (htime->year + htime->baseYear);
635 if(!((htime->timeType) & CdChronCal)) baseYear = year = 0; /* set year and baseYear to 0 for Clim */
636 if((htime->timeType) & Cd366) {
637     daysInLeapYear = 366;
638     daysInYear = 366;
639 } else {
640     daysInLeapYear = ((htime->timeType) & Cd365) ? 366 : 360;
641     daysInYear = ((htime->timeType) & Cd365) ? 365 : 360;
642 }
643
644 if (year > baseYear) {
645 for (ytemp = year - 1; ytemp >= baseYearytemp--) {
646 day_cnt += ISLEAP(ytemp,htime->timeType) ? daysInLeapYear : daysInYear;
647 }
648 } else if (year < baseYear) {
649 for (ytemp = yearytemp < baseYearytemp++) {
650 day_cnt -= ISLEAP(ytemp,htime->timeType) ? daysInLeapYear : daysInYear;
651 }
652 }
653 *etime = (double) (day_cnt + doy - 1) * 24. + htime->hour;
654        return;
655}
656
657/* Validate the component time, return 0 if valid, 1 if not */
658static int
659cdValidateTime(cdCalenType timetypecdCompTime comptime)
660{
661 if(comptime.month<1 || comptime.month>12){
662 cdError("Error on time conversion: invalid month = %hd\n",comptime.month);
663 return 1;
664 }
665 if(comptime.day<1 || comptime.day>31){
666 cdError("Error on time conversion: invalid day = %hd\n",comptime.day);
667 return 1;
668 }
669 if(comptime.hour<0.0 || comptime.hour>24.0){
670 cdError("Error on time conversion: invalid hour = %lf\n",comptime.hour);
671 return 1;
672 }
673 return 0;
674}
675
676void
677cdChar2Comp(cdCalenType timetype, char* chartimecdCompTimecomptime)
678{
679 double sec;
680 int ihriminnconv;
681 long year;
682 short day;
683 short month;
684
685 comptime->year = CD_NULL_YEAR;
686 comptime->month = CD_NULL_MONTH;
687 comptime->day = CD_NULL_DAY;
688 comptime->hour = CD_NULL_HOUR;
689
690 if(timetype & cdStandardCal){
691 nconv = sscanf(chartime,"%ld-%hd-%hd %d:%d:%lf",&year,&month,&day,&ihr,&imin,&sec);
692 if(nconv==EOF || nconv==0){
693 cdError("Error on character time conversion, string = %s\n",chartime);
694 return;
695 }
696 if(nconv >= 1){
697 comptime->year = year;
698 }
699 if(nconv >= 2){
700 comptime->month = month;
701 }
702 if(nconv >= 3){
703 comptime->day = day;
704 }
705 if(nconv >= 4){
706 if(ihr<0 || ihr>23){
707 cdError("Error on character time conversion: invalid hour = %d\n",ihr);
708 return;
709 }
710 comptime->hour = (double)ihr;
711 }
712 if(nconv >= 5){
713 if(imin<0 || imin>59){
714 cdError("Error on character time conversion: invalid minute = %d\n",imin);
715 return;
716 }
717 comptime->hour += (double)imin/60.;
718 }
719 if(nconv >= 6){
720 if(sec<0.0 || sec>60.0){
721 cdError("Error on character time conversion: invalid second = %lf\n",sec);
722 return;
723 }
724 comptime->hour += sec/3600.;
725 }
726 }
727 else{      /* Climatological */
728 nconv = sscanf(chartime,"%hd-%hd %d:%d:%lf",&month,&day,&ihr,&imin,&sec);
729 if(nconv==EOF || nconv==0){
730 cdError("Error on character time conversion, string = %s",chartime);
731 return;
732 }
733 if(nconv >= 1){
734 comptime->month = month;
735 }
736 if(nconv >= 2){
737 comptime->day = day;
738 }
739 if(nconv >= 3){
740 if(ihr<0 || ihr>23){
741 cdError("Error on character time conversion: invalid hour = %d\n",ihr);
742 return;
743 }
744 comptime->hour = (double)ihr;
745 }
746 if(nconv >= 4){
747 if(imin<0 || imin>59){
748 cdError("Error on character time conversion: invalid minute = %d\n",imin);
749 return;
750 }
751 comptime->hour += (double)imin/60.;
752 }
753 if(nconv >= 5){
754 if(sec<0.0 || sec>60.0){
755 cdError("Error on character time conversion: invalid second = %lf\n",sec);
756 return;
757 }
758 comptime->hour += sec/3600.;
759 }
760 }
761 (void)cdValidateTime(timetype,*comptime);
762 return;
763}
764
765/* Convert ct to relunits (unit, basetime) */
766/* in the mixed Julian/Gregorian calendar. */
767/* unit is anything but year, season, month. unit and basetime are */
768/* from the parsed relunits. Return result in reltime. */
769static void
770cdComp2RelMixed(cdCompTime ctcdUnitTime unitcdCompTime basetime, double *reltime){
771
772 double hourdiff;
773
774 hourdiff = cdDiffMixed(ctbasetime);
775 *reltime = cdFromHours(hourdiffunit);
776 return;
777}
778
779static void
780cdComp2Rel(cdCalenType timetypecdCompTime comptime, char* relunits, double* reltime)
781{
782 cdCompTime base_comptime;
783 CdDeltaTime deltime;
784 CdTime humantime;
785 CdTimeType old_timetype;
786 cdUnitTime unit;
787 double base_etmetmdelta;
788 long ndelhoursInYear;
789
790      /* Parse the relunits */
791 if(cdParseRelunits(timetyperelunits, &unit, &base_comptime))
792 return;
793
794      /* Handle mixed Julian/Gregorian calendar */
795 if (timetype == cdMixed){
796 switch(unit){
797 case cdWeek: case cdDay: case cdHour: case cdMinute: case cdSecond:
798 cdComp2RelMixed(comptimeunitbase_comptimereltime);
799 return;
800 case cdYear: case cdSeason: case cdMonth:
801 timetype = cdStandard;
802 break;
803 case cdFraction:
804         cdError("invalid unit in conversion");
805         break;
806 default: break;
807 }
808 }
809
810      /* Convert basetime to epochal */
811 humantime.year = base_comptime.year;
812 humantime.month = base_comptime.month;
813 humantime.day = base_comptime.day;
814 humantime.hour = base_comptime.hour;
815 humantime.baseYear = 1970;
816      /* Map to old-style timetype */
817 if(cdToOldTimetype(timetype,&old_timetype))
818 return;
819 humantime.timeType = old_timetype;
820 Cdh2e(&humantime,&base_etm);
821
822      /* Map end time to epochal */
823 humantime.year = comptime.year;
824 humantime.month = comptime.month;
825 humantime.day = comptime.day;
826 humantime.hour = comptime.hour;
827 Cdh2e(&humantime,&etm);
828      /* Calculate relative time value for months or hours */
829 deltime.count = 1;
830 /* Coverity[MIXED_ENUMS] */
831 deltime.units = (CdTimeUnit)unit;
832 switch(unit){
833   case cdWeek: case cdDay: case cdHour: case cdMinute: case cdSecond:
834 delta = etm - base_etm;
835 if(!(timetype & cdStandardCal)){ /* Climatological time */
836 hoursInYear = (timetype & cd365Days) ? 8760. : (timetype & cdHasLeap) ? 8784. : 8640.;
837      /* Normalize delta to interval [0,hoursInYear) */
838 if(delta < 0.0 || delta >= hoursInYear) {
839 double down = ((double)delta)/((double)hoursInYear);
840 down = floor(down);
841 down = down * (double)hoursInYear;
842 delta = delta - down;
843 }
844 }
845 break;
846   case cdYear: case cdSeason: case cdMonth:
847 CdDivDelTime(base_etmetmdeltimeold_timetype, 1970, &ndel);
848 break;
849   case cdFraction:
850         cdError("invalid unit in conversion");
851 break;
852   default: break;
853 }
854
855      /* Convert to output units */
856 switch(unit){
857   case cdSecond:
858 *reltime = 3600.0 * delta;
859 break;
860   case cdMinute:
861 *reltime = 60.0 * delta;
862 break;
863   case cdHour:
864 *reltime = delta;
865 break;
866   case cdDay:
867 *reltime = delta/24.0;
868 break;
869   case cdWeek:
870 *reltime = delta/168.0;
871 break;
872   case cdMonth: case cdSeason: case cdYear: /* Already in correct units */
873 if(timetype & cdStandardCal)
874 *reltime = (base_etm <= etm) ? (double)ndel : (double)(-ndel);
875 else      /* Climatological time is already normalized*/
876 *reltime = (double)ndel;
877 break;
878   default:
879 cdError("invalid unit in conversion");
880 break;
881 }
882
883 return;
884}
885
886/* Add (value,unit) to comptime. */
887/* value is in hours. */
888/* calendar is anything but cdMixed. */
889static void
890cdCompAdd(cdCompTime comptime, double valuecdCalenType calendarcdCompTime *result){
891
892 double reltime;
893
894 cdComp2Rel(calendarcomptime, "hours", &reltime);
895 reltime += value;
896 cdRel2Comp(calendar, "hours", reltimeresult);
897 return;
898}
899
900/* Add value in hours to ct, in the mixed Julian/Gregorian
901 * calendar. */
902static void
903cdCompAddMixed(cdCompTime ct, double valuecdCompTime *result){
904
905 static cdCompTime ZA = {1582, 10, 5, 0.0};
906 static cdCompTime ZB = {1582, 10, 15, 0.0};
907 double xjxg;
908
909 if (cdCompCompare(ctZB) == -1){
910 xj = cdDiffJulian(ZAct);
911 if (value <= xj){
912 cdCompAdd(ctvaluecdJulianresult);
913 }
914 else {
915 cdCompAdd(ZBvalue-xjcdStandardresult);
916 }
917 }
918 else {
919 xg = cdDiffGregorian(ZBct);
920 if (value > xg){
921 cdCompAdd(ctvaluecdStandardresult);
922 }
923 else {
924 cdCompAdd(ZAvalue-xgcdJulianresult);
925 }
926 }
927 return;
928}
929
930/* Return value expressed in hours. */
931static double
932cdToHours(double valuecdUnitTime unit){
933
934 double result = 0;
935
936 switch(unit){
937 case cdSecond:
938 result = value/3600.0;
939 break;
940 case cdMinute:
941 result = value/60.0;
942 break;
943 case cdHour:
944 result = value;
945 break;
946 case cdDay:
947 result = 24.0 * value;
948 break;
949 case cdWeek:
950 result = 168.0 * value;
951 break;
952 default:
953         cdError("invalid unit in conversion");
954 break;
955
956 }
957 return result;
958}
959
960/* Convert relative time (reltime, unit, basetime) to comptime in the
961 * mixed Julian/Gregorian calendar. unit is anything but year, season,
962 * month. unit and basetime are from the parsed relunits. Return
963 * result in comptime. */
964static void
965cdRel2CompMixed(double reltimecdUnitTime unitcdCompTime basetimecdCompTime *comptime){
966
967 reltime = cdToHours(reltimeunit);
968 cdCompAddMixed(basetimereltimecomptime);
969 return;
970}
971
972
973static void
974cdRel2Comp(cdCalenType timetype, char* relunits, double reltimecdCompTimecomptime)
975{
976 CdDeltaTime deltime;
977 CdTime humantime;
978 CdTimeType old_timetype;
979 cdCompTime base_comptime;
980 cdUnitTime unitbaseunits;
981 double base_etmresult_etm;
982 double delta;
983 long idelta;
984
985      /* Parse the relunits */
986 if(cdParseRelunits(timetyperelunits, &unit, &base_comptime))
987 return;
988
989 if (timetype == cdMixed){
990 switch(unit){
991 case cdWeek: case cdDay: case cdHour: case cdMinute: case cdSecond:
992 cdRel2CompMixed(reltimeunitbase_comptimecomptime);
993 return;
994 case cdYear: case cdSeason: case cdMonth:
995 timetype = cdStandard;
996 break;
997 case cdFraction:
998         cdError("invalid unit in conversion");
999         break;
1000 default: break;
1001 }
1002 }
1003
1004 baseunits =cdBadUnit;
1005 switch(unit){
1006   case cdSecond:
1007 delta = reltime/3600.0;
1008 baseunits = cdHour;
1009 break;
1010   case cdMinute:
1011 delta = reltime/60.0;
1012 baseunits = cdHour;
1013 break;
1014   case cdHour:
1015 delta = reltime;
1016 baseunits = cdHour;
1017 break;
1018   case cdDay:
1019 delta = 24.0 * reltime;
1020 baseunits = cdHour;
1021 break;
1022   case cdWeek:
1023 delta = 168.0 * reltime;
1024 baseunits = cdHour;
1025 break;
1026   case cdMonth:
1027 idelta = (long)(reltime + (reltime<0 ? -1.e-10 : 1.e-10));
1028 baseunits = cdMonth;
1029 break;
1030   case cdSeason:
1031 idelta = (long)(3.0 * reltime + (reltime<0 ? -1.e-10 : 1.e-10));
1032 baseunits = cdMonth;
1033 break;
1034   case cdYear:
1035 idelta = (long)(12 * reltime + (reltime<0 ? -1.e-10 : 1.e-10));
1036 baseunits = cdMonth;
1037 break;
1038   default:
1039         cdError("invalid unit in conversion");
1040 break;
1041 }
1042
1043 deltime.count = 1;
1044 /* Coverity[MIXED_ENUMS] */
1045 deltime.units = (CdTimeUnit)baseunits;
1046
1047 humantime.year = base_comptime.year;
1048 humantime.month = base_comptime.month;
1049 humantime.day = base_comptime.day;
1050 humantime.hour = base_comptime.hour;
1051 humantime.baseYear = 1970;
1052      /* Map to old-style timetype */
1053 if(cdToOldTimetype(timetype,&old_timetype))
1054 return;
1055 humantime.timeType = old_timetype;
1056
1057 Cdh2e(&humantime,&base_etm);
1058      /* If months, seasons, or years, */
1059 if(baseunits == cdMonth){
1060
1061      /* Calculate new epochal time from integer months. */
1062      /* Convert back to human, then comptime. */
1063      /* For zero reltime, just return the basetime*/
1064 if(reltime != 0.0){
1065 CdAddDelTime(base_etm,idelta,deltime,old_timetype,1970,&result_etm);
1066 Cde2h(result_etmold_timetype, 1970, &humantime);
1067 }
1068 }
1069      /* Calculate new epochal time. */
1070      /* Convert back to human, then comptime. */
1071 else if(baseunits == cdHour){
1072 Cde2h(base_etm+deltaold_timetype, 1970, &humantime);
1073
1074 }
1075 comptime->year = humantime.year;
1076 comptime->month = humantime.month;
1077 comptime->day = humantime.day;
1078 comptime->hour = humantime.hour;
1079
1080 return;
1081}
1082
1083/* rkr: output as ISO 8601 strings */
1084static void
1085cdComp2Iso(cdCalenType timetype, int separatorcdCompTime comptime, char* time)
1086{
1087 double dtmpsec;
1088 int ihriminisec;
1089 int nskip;
1090
1091 if(cdValidateTime(timetype,comptime))
1092 return;
1093
1094 ihr = (int)comptime.hour;
1095 dtmp = 60.0 * (comptime.hour - (double)ihr);
1096 imin = (int)dtmp;
1097 sec = 60.0 * (dtmp - (double)imin);
1098 isec = (int)sec;
1099
1100 if(sec == isec)
1101     if(isec == 0)
1102 if(imin == 0)
1103     if(ihr == 0)
1104 nskip = 4;
1105     else
1106 nskip = 3;
1107 else
1108     nskip = 2;
1109     else
1110 nskip = 1;
1111 else
1112     nskip = 0;
1113
1114 if(timetype & cdStandardCal){
1115     switch (nskip) {
1116     case 0: /* sec != 0 && (int)sec != sec */
1117 sprintf(time,"%4.4ld-%2.2hd-%2.2hd%c%2.2d:%2.2d:%lf",
1118 comptime.year,comptime.month,comptime.day,separator,ihr,imin,sec);
1119 break;
1120     case 1:
1121 sprintf(time,"%4.4ld-%2.2hd-%2.2hd%c%2.2d:%2.2d:%2.2d",
1122 comptime.year,comptime.month,comptime.day,separator,ihr,imin,isec);
1123 break;
1124     case 2:
1125 sprintf(time,"%4.4ld-%2.2hd-%2.2hd%c%2.2d:%2.2d",
1126 comptime.year,comptime.month,comptime.day,separator,ihr,imin);
1127 break;
1128     case 3:
1129 sprintf(time,"%4.4ld-%2.2hd-%2.2hd%c%2.2d",
1130 comptime.year,comptime.month,comptime.day,separator,ihr);
1131 break;
1132     case 4:
1133 sprintf(time,"%4.4ld-%2.2hd-%2.2hd",
1134 comptime.year,comptime.month,comptime.day);
1135 break;
1136     }
1137 }
1138 else {      /* Climatological */
1139     switch (nskip) {
1140     case 0: /* sec != 0 && (int)sec != sec */
1141 sprintf(time,"%2.2hd-%2.2hd%c%2.2d:%2.2d:%lf",
1142 comptime.month,comptime.day,separator,ihr,imin,sec);
1143 break;
1144     case 1:
1145 sprintf(time,"%2.2hd-%2.2hd%c%2.2d:%2.2d:%2.2d",
1146 comptime.month,comptime.day,separator,ihr,imin,isec);
1147 break;
1148     case 2:
1149 sprintf(time,"%2.2hd-%2.2hd%c%2.2d:%2.2d",
1150 comptime.month,comptime.day,separator,ihr,imin);
1151 break;
1152     case 3:
1153 sprintf(time,"%2.2hd-%2.2hd%c%2.2d",
1154 comptime.month,comptime.day,separator,ihr);
1155 break;
1156     case 4:
1157 sprintf(time,"%2.2hd-%2.2hd",
1158 comptime.month,comptime.day);
1159 break;
1160     }
1161 }
1162 return;
1163}
1164
1165/* rkr: added for output closer to ISO 8601 */
1166void
1167cdRel2Iso(cdCalenType timetype, char* relunits, int separator, double reltime, char* chartime)
1168{
1169 cdCompTime comptime;
1170
1171 cdRel2Comp(timetyperelunitsreltime, &comptime);
1172 cdComp2Iso(timetypeseparatorcomptimechartime);
1173
1174 return;
1175}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed