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 <netcdf.h>
27#include "utils.h"
28#include "nccomps.h"
29#include "dumplib.h" /* for sbuf_... prototypes */
30#include "ncdump.h" /* for fspec_t def */
31#include "nctime0.h"
32#include "vardata.h"
33
34
35static struct {
36    size_t nbnds; /* number of bounds variables */
37    bounds_node_t *first;
38bounds_list;
39
40extern fspec_t formatting_specs; /* set from command-line options */
41
42/* rkr: added bounds functions to detect bounds variables of time variables */
43void
44bounds_add(char *bounds_name, int ncid, int varid) {
45    bounds_node_t *bnode = emalloc(sizeof(bounds_node_t) + 1);
46    bounds_list.nbnds++;
47    bnode->ncid = ncid;
48    bnode->varid = varid;
49    bnode->bounds_name = strdup(bounds_name);
50    bnode->next = bounds_list.first;
51    bounds_list.first = bnode;
52}
53
54/* Check for optional "calendar" attribute and return specified
55 * calendar type, if present. */
56cdCalenType
57calendar_type(int ncid, int varid) {
58    int ctype;
59    int stat;
60    ncatt_t catt;
61    static struct {
62 char* attname;
63 int type;
64    } calmap[] = {
65 {"gregorian", cdMixed},
66 {"standard", cdMixed}, /* synonym */
67 {"proleptic_gregorian", cdStandard},
68 {"noleap", cdNoLeap},
69 {"no_leap", cdNoLeap},
70 {"365_day", cdNoLeap}, /* synonym */
71 {"allleap", cd366},
72 {"all_leap", cd366}, /* synonym */
73 {"366_day", cd366}, /* synonym */
74 {"360_day", cd360},
75 {"julian", cdJulian},
76 {"none", cdClim} /* TODO: test this */
77    };
78#define CF_CAL_ATT_NAME "calendar"
79    int ncals = (sizeof calmap)/(sizeof calmap[0]);
80    ctype = cdMixed;  /* default mixed Gregorian/Julian ala udunits */
81    stat = nc_inq_att(ncidvaridCF_CAL_ATT_NAME, &catt.type, &catt.len);
82    if(stat == NC_NOERR && catt.type == NC_CHAR && catt.len > 0) {
83 char *calstr = (char *)emalloc(catt.len + 1);
84 int itype;
85 NC_CHECK(nc_get_att(ncidvaridCF_CAL_ATT_NAMEcalstr));
86 calstr[catt.len] = '\0';
87 for(itype = 0; itype < ncalsitype++) {
88     if(strncmp(calstrcalmap[itype].attnamecatt.len) == 0) {
89 ctype = calmap[itype].type;
90 break;
91     }
92 }
93 free(calstr);
94    }
95    return ctype;
96}
97
98bool_t
99is_bounds_var(char *varname, int *pargrpidp, int *parvaridp) {
100    bounds_node_t *bp = bounds_list.first;
101    for(; bpbp = bp->next) {
102 if(STREQ(bp->bounds_namevarname)) {
103     *pargrpidp = bp->ncid;
104     *parvaridp = bp->varid;
105     return true;
106 }
107    }
108    return false;
109}
110
111/* Test if attribute is of form required by cdtime:
112 *     <time_unit> since <base_time>
113 * where
114 *     <time_unit>:
115 */
116bool_t
117is_valid_time_unit(const char *units) {
118 char charunits[CD_MAX_RELUNITS];
119 char basetime_1[CD_MAX_CHARTIME];
120 char basetime_2[CD_MAX_CHARTIME];
121 int nconv1nconv2;
122 bool_t okunit = false;
123
124 /* Allow ISO-8601 "T" date-time separator as well as blank separator */
125 nconv1 = sscanf(units,"%s since %[^T]T%s", charunitsbasetime_1basetime_2);
126 nconv2 = sscanf(units,"%s since %s %s", charunitsbasetime_1basetime_2);
127 if (!(nconv1 > 1 || nconv2 > 1))
128     return false;
129 /* Check for unit compatible with cdtime library, no attempt
130  * to enforce CF-compliance or udunits compliance here ... */
131 if(!strncmp(charunits,"sec",3) || !strcmp(charunits,"s")){
132     okunit = true;
133 }
134 else if(!strncmp(charunits,"min",3) || !strcmp(charunits,"mn")){
135     okunit = true;
136 }
137 else if(!strncmp(charunits,"hour",4) || !strcmp(charunits,"hr")){
138     okunit = true;
139 }
140 else if(!strncmp(charunits,"day",3) || !strcmp(charunits,"dy")){
141     okunit = true;
142 }
143 else if(!strncmp(charunits,"week",4) || !strcmp(charunits,"wk")){
144     okunit = true;
145 }
146 else if(!strncmp(charunits,"month",5) || !strcmp(charunits,"mo")){
147     okunit = true;
148 }
149 else if(!strncmp(charunits,"season",6)){
150     okunit = true;
151 }
152 else if(!strncmp(charunits,"year",4) || !strcmp(charunits,"yr")){
153     okunit = true;
154 }
155 if (!okunit)
156     return false;
157 return true;
158}
159
160/* Return true only if this is a "bounds" attribute */
161bool_t
162is_bounds_att(ncatt_t *attp) {
163    if(attp->type == NC_CHAR && attp->valgp && STREQ((char *)attp->name, "bounds")) {
164 return true;
165    }
166#ifdef USE_NETCDF4
167    if(attp->type == NC_STRING && attp->valgp && STREQ((char *)attp->name, "bounds")) {
168 return true;
169    }
170#endif /* USE_NETCDF4 */
171    return false;
172}
173
174/* Insert info about a bounds attribute into bounds list, so we can
175 * later determine which variables are bounds variables for which
176 * other variables.  att must be a variable "bounds" attribute.  */
177void
178insert_bounds_info(int ncid, int varidncatt_t *attp) {
179    static bool_t uninitialized = true;
180
181    if(uninitialized) {
182 bounds_list.nbnds = 0;
183 bounds_list.first = NULL;
184    }
185    assert(is_bounds_att(attp));
186    bounds_add(attp->valgpncidvarid);
187}
188
189void
190get_timeinfo(int ncid1, int varid1ncvar_t *vp) {
191    ncatt_t uatt; /* units attribute */
192    int nc_status; /* return from netcdf calls */
193    char *units;
194    int ncid = ncid1;
195    int varid = varid1;
196
197    vp->has_timeval = false; /* by default, turn on if criteria met */
198    vp->timeinfo = 0;
199    vp->is_bnds_var = false;
200    /* for timeinfo, treat a bounds variable like its "parent" time variable */
201    if(is_bounds_var(vp->name, &ncid, &varid)) {
202 vp->is_bnds_var = true;
203    }
204
205    /* time variables must have appropriate units attribute or be a bounds variable */
206    nc_status = nc_inq_att(ncidvarid, "units", &uatt.type, &uatt.len);
207    if(nc_status == NC_NOERR && uatt.type == NC_CHAR) { /* TODO: NC_STRING? */
208 units = emalloc(uatt.len + 1);
209 NC_CHECK(nc_get_att(ncidvarid, "units", units));
210 units[uatt.len] = '\0';
211 if(!is_valid_time_unit(units)) {
212     free(units);
213     return;
214 }
215 /* check for calendar attribute (not required even for time vars) */
216 vp->timeinfo = (timeinfo_t *)emalloc(sizeof(timeinfo_t));
217 memset((void*)vp->timeinfo,0,sizeof(timeinfo_t));
218 vp->timeinfo->calendar = calendar_type(ncidvarid);
219 /* Parse relative units, returning the unit and base component time. */
220  if(cdParseRelunits(vp->timeinfo->calendarunits,
221    &vp->timeinfo->unit, &vp->timeinfo->origin) != 0) {
222     /* error parsing units so just treat as not a time variable */
223     free(vp->timeinfo);
224     free(units);
225     vp->timeinfo = NULL;
226     return;
227 }
228 /* Currently this gets reparsed for every value, need function
229  * like cdRel2Comp that resuses parsed units? */
230 vp->timeinfo->units = strdup(units);
231 vp->has_timeval = true;
232 free(units);
233    }
234    return;
235}
236
237/* print_att_times
238 * by Dave Allured, NOAA/PSD/CIRES.
239 * This version supports only primitive attribute types; do not call
240 * for user defined types.  Print interpreted, human readable (ISO)
241 * time strings for an attribute of a CF-like time variable.
242 *
243 * Print strings as CDL comments, following the normal non-decoded
244 * numeric values, which were already printed by the calling function.
245 * In the following example, this function prints only the right hand
246 * side, starting at the two slashes:
247 *
248 *    time:actual_range = 51133., 76670. ; // "1940-01-01", "2009-12-01"
249 *
250 * This function may be called for ALL primitive attributes.
251 * This function qualifies the attribute for numeric type and
252 * inheriting valid time attributes (has_time).  If the attribute
253 * does not qualify, this function prints nothing and safely
254 * returns.
255 *
256 * This function interprets and formats time values with the SAME
257 * methods already used in ncdump -t for data variables.
258 *
259 * This version has special line wrapping rules:
260 *
261 * (1) If the attribute has one or two values, the time strings are
262 *     always printed on the same line.
263 *
264 * (2) If the attribute has three or more values, the time strings
265 *     are always printed on successive lines, with line wrapping
266 *     as needed.
267 *
268 * Assume: Preceding call to pr_att_valgs has already screened
269 * this attribute for valid primitive types for the current netcdf
270 * model (netcdf 3 or 4).
271 */
272
273void
274print_att_times(
275    int ncid,
276    int varid, /* parent var ID */
277    const ncatt_t *att /* attribute structure */
278    )
279{
280    nc_type type = att->type; /* local copy */
281    bool_t wrap;
282    bool_t first_item;
283
284    ncvar_t var; /* fake var structure for the att values; */
285 /* will add only the minimum necessary info */
286
287/* For common disqualifications, print nothing and return immediately. */
288
289    if (type == NC_CHAR || type == NC_STRING) /* must be numeric */
290 return;
291
292    if (varid == NC_GLOBAL) /* time units not defined for global atts */
293 return;
294
295    assert (att->len > 0); /* should already be eliminated by caller */
296
297#ifdef USE_NETCDF4
298    assert ( type == NC_BYTE   || type == NC_SHORT  || type == NC_INT
299          || type == NC_FLOAT  || type == NC_DOUBLE || type == NC_UBYTE
300          || type == NC_USHORT || type == NC_UINT   || type == NC_INT64
301          || type == NC_UINT64 );
302#else   /* NETCDF3 */
303    assert ( type == NC_BYTE   || type == NC_SHORT  || type == NC_INT
304          || type == NC_FLOAT  || type == NC_DOUBLE );
305#endif
306
307/* Get time info from parent variable, and qualify. */
308
309    memset((void*)&var,0,sizeof(var)); /* clear the fake var structure */
310    get_timeinfo(ncidvarid, &var); /* sets has_timeval, timeinfo members */
311
312    if (var.has_timeval) { /* no print unless time qualified */
313
314/* Convert each value to ISO date/time string, and print. */
315
316 size_t iel;      /* attrib index */
317 const char *valp = (const char *)att->valgp;  /* attrib value pointer */
318 safebuf_t *sb = sbuf_new(); /* allocate new string buffer */
319#ifdef NOTUSED
320        int func; /* line wrap control */
321 int separator = ' '; /* default between data and time */
322 if(formatting_specs.iso_separator)
323     separator = 'T';
324#endif
325
326 var.type = att->type; /* insert attrib type into fake var */
327
328 for (iel = 0; iel < att->leniel++) {
329     nctime_val_tostring(&varsb, (void *)valp);  /* convert to str. */
330     valp += att->tinfo->size; /* increment value pointer, by type */
331     if (iel < att->len - 1) /* add comma, except for final value */
332 sbuf_cat(sb, ",");
333
334            first_item = (iel == 0); /* identify start of list */
335
336            wrap = (att->len > 2); /* specify line wrap variations:     */
337 /* 1 or 2 values: keep on same line, */
338 /* more than 2: enable line wrap     */
339
340            lput2 (sbuf_str(sb), first_itemwrap);
341             /* print string in CDL comment, */
342             /* with auto newline            */
343 }
344
345        sbuf_free(sb); /* clean up */
346
347 if(var.timeinfo->units) /* clean up from get_timeinfo */
348     free(var.timeinfo->units);
349 free(var.timeinfo);
350    }
351}


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