nctime.c 30 KB

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