/* ioreadrec_routines WJS Jul 96 */ /* Contains externally referenced routines: */ /* void calc_latlon(valarr,vallen,level,outlon,inlon,outlat,inlat) */ /* void calc_timedate(valarr,vallen,level,in,out,fragbuflist) */ /* both called by ioreadrec_ (& only by ioreadrec_) */ /* */ /* This routine logically uses the tabvalues array from defgb */ /* This is an array of character strings. Since the size of each */ /* string is method-dependent, if we used that constant here we */ /* would need to produce a version of this per method. Therefore, */ /* we do our own array arithmetic here. Better soln would be to */ /* replace tabvalues with an array of pointers... */ #define IOREADREC_ROUTINES_VERSION "ioreadrec_routines version 1.2 26 Apr 1997" /* 26 Apr 97. 1.2 */ /* Time zone conversion */ /* Bug fix: must set max DAY_FRAG to smallest illegal value */ /* instead of largest legal one. Symptom: Aug 0 is after Jul 30 */ /* Also, must redo carryover logic since some minimum values are */ /* 1 (month; day) instead of 0 (hour; minute) */ /* 27 Mar 97. 1.2 */ /* Change type of check_ampm to signed char from char. Not a */ /* bug since only use was a, p or neither, but make the change... */ /* [Begin 1.2] */ /* 18 Mar 97. 1.1a */ /* Bug fix: must set FRACTION_FRAG to missing when appropriate */ /* Bug fix: range check for days had "or" instead of "and" */ /* Bug fix: don't print fraction if # digits negative. */ /* Add version variable so version string appears in executable */ /* Modification to carryover algorithm in problem cases-all */ /* except time fraction remain illegal if carryover field missing */ /* or on next level */ /* [Begin 1.1a] */ /* 25 Feb 97. 1.1 */ /* Allow processing of input times that were not requested as */ /* output times. Allows "small" time to affect roundoff of larger */ /* times; use of year for Julian calculation; etc w/o asking for */ /* output of those variables */ /* Bug fix: Time roundoff did not carry over. Do the best */ /* we can considering that variables into which we might need to */ /* carry, might not be available at level we're on. In such */ /* cases, produce "illegal" stuff (such as hour 24 or day 32) per */ /* Bob. */ /* 7 Feb 97. 1.1 */ /* Remove pointers input to & use by calc_latlon & */ /* calc_timedate per new way of storing data in tabvalues */ /* [Begin 1.1] */ /* 4 Dec 96. 1.0b */ /* Pointer to array causes warning diagnostic. Remove */ /* 26 Oct 96. 1.0a */ /* Add routine name and "version" to *_VERSION so it will show up */ /* in strings command */ /* 1 Aug 1996 1.0a */ /* Bug fix in lat/lon conversion w/missing values. Bug not in */ /* defgb 2.4 */ /* 1 Aug 1996 [version 1.0 in use w/test ver of defgb 2.5] */ /* */ #include "defgb_nonconfigurable.h" /* Uses no global variables. Globals defined here only used here */ /* Needs following functions */ void err(),errn(); Logical ok_to_calc(),ok_to_use(),missing(),get_logical_value(); char *buildstring(),*lookup_wjstbl(); /* Pick next flag carefully. For example, -1 cannot be used since */ /* is a valid time offset. Fragments can have this flag for far */ /* more reasons than their input is missing */ #define MISSING_TIME_FLAG -9999 /* Save latest time/date info that passes through this routine. It */ /* may save calculations. Code must check that this info is */ /* applicable to the calculations, however ... mostly done while */ /* setting up in get_timedateparams */ /* I suspect that as of v 1.1, these static variables (except */ /* leapyear) could be replaced with outtime[x_FRAG].val, but this */ /* seems to work... */ /* Leap year info is all we want. However, need to know if saved */ /* leap year is valid; hence save year as flag. While we're at */ /* it, we use saved year to avoid leap year recalculation */ static int year = -1; static Logical leapyear; /* Process Julian day exactly once and save results in month & day */ /* Whichever output (day or month) occurs first does calculation; */ /* the other output just copies the saved value */ /* Ditto for 24-hour time */ static int month,day; static int hour,minute; /* Save pieces of HHMM time displacement for time zone conversion */ static int hours_offset,minutes_offset; static int cumdays[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; static char *monthnames[12] = {"january","february","march","april","may","june", "july","august","september","october","november","december"}; char *version = IOREADREC_ROUTINES_VERSION; /*************** */ void reformat_latlon (outstr,maxlenoutstr,instr,outformat,informat) char *instr,*outstr,*informat,*outformat; int maxlenoutstr; { char *ptr,*ptr2; char deg[5]; /* Sign; up to 3 digs; null */ int inlen,deglen; int sig_min_digs; /* Significant digits in minute portion of inp arg */ double f,g; if ( (strcmp(informat,"degdecmin") == 0) && (strcmp(outformat,"decdeg") == 0) ) { /* degdecmin format assumed to be opt sign, up to 3 digits of */ /* degrees, up to 2 digits of whole minutes, optional decimal pt */ /* w/fractional minute. Scheme: separate degrees from minutes, */ /* turn both into floating pt, add, and reconvert to string. If */ /* any characters found in either portion of string, return nd */ inlen = strlen(instr); if ( (ptr = strchr(instr,'.')) == NULL ) { ptr = instr + inlen; sig_min_digs = 0; } else sig_min_digs = -1; if ( (ptr = ptr - 2) <= instr ) ptr = instr; deglen = ptr-instr; if ( deglen > maxlenoutstr) err ("Output lat/lon not big enough to accept input lat/lon degrees\ \n Input lat/lon = ",instr); if (deglen == 0) f = strtod(ptr,&ptr2) / 60.; else { /* Copy to temp buffer and strtod instead of sscanf w/width */ /* to simplify figuring out if there are characters in string */ /* (requiring an nd return). Don't use outstr because of po- */ /* tential overlap */ strncpy (deg,instr,deglen); deg[deglen] = '\0'; f = strtod(deg,&ptr2); if (*ptr2 == '\0') { g = strtod(ptr,&ptr2) / 60.; (f >= 0) ? (f += g) : (f -= g); } } if (*ptr2 == '\0') { /* Generally, number of significant digits is same in input & */ /* and output (60ths of a degree vs 100ths of a degree */ /* Assume that if no decimal minutes, there are whole minutes */ /* Decimal pt or lack thereof already taken care of */ sig_min_digs += (instr + inlen - ptr); if (sig_min_digs < 2) sig_min_digs = 2; /* +1, -1 for decimal point */ if ((deglen + sig_min_digs + 1) > maxlenoutstr) sig_min_digs = maxlenoutstr - deglen - 1; sprintf (outstr,"%.*f",sig_min_digs,f); } else strncpy(outstr,MISSING_VALUE_STRING,maxlenoutstr); } else err ( buildstring("No code to do ",informat," to ", "building reformat_latlon msg"), buildstring(outformat," latlon conversion", "building reformat_latlon msg") ); return; } void negate_string (outstr,maxlenoutstr,instr) char *outstr,*instr; int maxlenoutstr; /* Negate a (presumably) numeric string w/string operations */ /* Drop digits if input string too long */ { char *out_ptr,*in_ptr; int leninstr; if (*instr == '-') strncpy(outstr,instr+1,maxlenoutstr); else { if (*instr == '+') strncpy (outstr+1,instr+1,maxlenoutstr-1); else { /* Must insert - */ /* May need to copy string backwards in case source & dest */ /* overlap. Leery if strcpy on Sun works this way (weird */ /* intermittent problems). K & R says that operation is */ /* undefined if addresses overlap, so... */ leninstr = strlen(instr); if (leninstr >= maxlenoutstr) { /* >= allows for - sign */ out_ptr = outstr + maxlenoutstr; *(out_ptr--) = '\0'; in_ptr = instr + maxlenoutstr - 2; } else { out_ptr = outstr + leninstr + 1; in_ptr = instr + leninstr; /* Points to terminating '\0' */ } while (out_ptr > outstr) *(out_ptr--) = *(in_ptr--); } /* Do next statement last in case instr & outstr overlap */ *outstr = '-'; } return; } void calc_latlon(valarr,vallen,level,outlon,inlon,outlat,inlat) int level,vallen; char *valarr; struct latlonformat *inlon,*outlon,*inlat,*outlat; { int valstrlen; char *inbuf,*outbuf; valstrlen = vallen + 1; if (strcmp(outlat->format,inlat->format) != 0) if (ok_to_calc(outlat->var,level)) { outbuf = valarr + valstrlen*outlat->var; if (ok_to_use(inlat->var,level)) { inbuf = valarr + valstrlen*inlat->var; reformat_latlon (outbuf,vallen,inbuf,outlat->format,inlat->format); } else strcpy (outbuf,MISSING_VALUE_STRING); } if (inlat->convention != outlat->convention) if (ok_to_calc(outlat->var,level)) { outbuf = valarr + valstrlen*outlat->var; if (ok_to_use(inlat->var,level)) { inbuf = valarr + valstrlen*inlat->var; negate_string (outbuf,vallen,inbuf); } else strcpy (outbuf,MISSING_VALUE_STRING); } if (strcmp(outlon->format,inlon->format) != 0) if (ok_to_calc(outlon->var,level)) { outbuf = valarr + valstrlen*outlon->var; if (ok_to_use(inlon->var,level)) { inbuf = valarr + valstrlen*inlon->var; reformat_latlon (outbuf,vallen,inbuf,outlon->format,inlon->format); } else strcpy (outbuf,MISSING_VALUE_STRING); } if (inlon->convention != outlon->convention) if (ok_to_calc(outlon->var,level)) { outbuf = valarr + valstrlen*outlon->var; if (ok_to_use(inlon->var,level)) { inbuf = valarr + valstrlen*inlon->var; negate_string (outbuf,vallen,inbuf); } else strcpy (outbuf,MISSING_VALUE_STRING); } return; } void get_timedate_frag (vec,buffer,format,index,expected_items) char *vec[],*buffer,*format; int expected_items,index; /* Do a sscanf from buffer to vec according to format # items in */ /* format assumed to match length of vec, but we don't know how many. */ /* Hence, set up to decode potentially beyond length of vec, relying */ /* on sscanf to stop usin vec once it's decoded everything (and */ /* relying on format to be correct!). Entries in vec are pointers */ /* 11 is number of items in decode list below. In includes an */ /* "extra" item, hence the -1 */ #if NUM_TIMEDATE_FRAGS != 11 - 1 #error "Incorrect number of decode items in get_timedate_frag" #endif { int i; char *tmp,*errtmp; char scratch[2]; if (strcmp(buffer,MISSING_VALUE_STRING) == 0) for (i=0; i 59) julday--; else if (julday == 59) { month = 2; day = 29; return TRUE; } if ( (0 <= julday) && (julday < 365) ) { for (month = 1; month <= 12; month++) if (julday < cumdays[month]) break; day = julday - cumdays[month-1] + 1; return TRUE; } else { month = -1; day = -1; return FALSE; } } signed char check_ampm(s) char *s; /* Check that string is either a or p, am or pm (ignoring case) */ /* Return a, p or -1 */ { char c; c = tolower(*s); if ( (c == 'a') || (c == 'p') ) switch (*(s+1)) { case 'm': case 'M': if (*(s+2) == '\0') return c; break; case '\0': return c; default: ; } return -1; } Logical ok_to_redefine (outtime,level) struct outtime *outtime; int level; { return ( (outtime->lev == level) && (outtime->val != MISSING_TIME_FLAG) ); } void td_carryover(units_struct,min,max,borrowval,tens_struct,level) struct outtime *units_struct,*tens_struct; int min,max,borrowval,level; /* Do carryovers from a time/date field to the next bigger field */ /* if both fields are on this level and not missing. (Deliber- */ /* ately leave illegal fields if carryover not possible) */ /* Analogy to addition/subtraction, hence "units", "tens", etc */ /* min and max are permissible values. negative borrowval is */ /* special case flag for days->months, asking this routine to calc */ /* number of days last month if necessary. This to avoid the calc */ /* except when needed... */ { int last_month; if (ok_to_redefine(units_struct,level) && ok_to_redefine(tens_struct,level)) if (units_struct->val > max) { units_struct->val -= (max+1); tens_struct->val++; } else if (units_struct->val < min) { if (borrowval < 0) { /* Set up maximum # days for last month */ last_month = (tens_struct->val == 1) ? 12 : tens_struct->val - 1; if ( (last_month == 2) && leapyear && (year != MISSING_TIME_FLAG) ) borrowval = 29; else borrowval = cumdays[last_month] - cumdays[last_month-1]; } units_struct->val += borrowval; tens_struct->val--; } return; } void calc_timedate(valarr,vallen,level,in,out,fragbuflist) int level,vallen; char *valarr; struct outtime out[]; struct intime in[]; char *fragbuflist[]; /* Do required time calculations. First get any fragments that have */ /* become valid at this level. Then compute any output fragments */ /* that have become valid. Then output any output variables */ { double pow(),modf(); char *ptr,*ptr2,*in_ptr,*out_ptr; char fractype; static char *casebuf = NULL; /* Buffer for caseblind compares */ int i,j,k; int fracdigits,valstrlen; Logical tzerr = FALSE; double frac,ftmp; div_t digs; valstrlen = vallen + 1; if (casebuf == NULL) casebuf = (char *)malloc(valstrlen); /* Get any time/date fragments that became available because input */ /* variables specifying info are present on this level */ for (i = 0; i < NUM_TIMEDATE_FRAGS; i++) /* Test below is NOT ok_to_use because a) we don't want to recalc */ /* at levels after variable appears & b) get_timedate_frag needs */ /* missing data input so it can mark all dependent frags missing */ if (ok_to_calc(in[i].var,level)) get_timedate_frag(&fragbuflist[in[i].fragbuf_index], valarr + valstrlen*in[i].var, in[i].format, i, in[i].nfrags); /* Try to calculate any output fragments that are present on this */ /* level. Possible fractional and/or Julian inputs require */ /* processing in "size order". 2nd pass is then done to do rounding */ /* (& zone change?) 2nd pass is needed (and done in reverse "size */ /* order") because roundoff of smallest piece might alter values of */ /* bigger pieces. 3rd pass is then done to do any output. */ /* In general, i &/or j are temp values until i becomes the */ /* integer value of a fragment and j becomes the integer value */ /* of the output. j is also used as "output ok" flag */ /* i is occasionally calculated when not needed & therefore its */ /* source buffer could contain garbage. Lazily relying on atoi not */ /* to bomb in this case (as well as relying on our code not to use */ /* the value!) */ /* Report errors when they occur; if errors are not fatal, set */ /* output string to missing */ /* Much validity work is done in get_timedataparams. In */ /* particular, we assume */ /* 1) formats for each fragment were correctly set; eg, if year */ /* fragment is not format y, it must be Y. Sometimes we check */ /* here anyway, if convenient */ /* 2) integer data consists only of integers & sign (done by */ /* extracting strings with [0-9,+,-] format specifier instead */ /* of s - bad strings should produce "format mismatch" errors */ /* in get_timedatefrag) */ /* 3) needed input variables exist in the dataset and have been */ /* read in by the time they're needed. Hence, instead of using */ /* ok_to_use, we can get away with just checking for missing */ /* Calculate year. Y = year w/century; y = w or w/o. */ j = MISSING_TIME_FLAG; /* Not using ok_to_calc allows us to process fragments that have not */ /* been requested for output */ if (out[YEAR_FRAG].lev == level) { i = out[YEAR_FRAG].intime_index; /* Which input gave frag? */ if ( ! missing(in[i].var) ) { in_ptr = out[YEAR_FRAG].fragbuf; /* Fragment data */ i = atoi(in_ptr); /* ... as integer */ if (*out[YEAR_FRAG].source_field_type == 'y') if ( (0 <= i) && (i < 50) ) i += 2000; else if ( (50 <= i) && (i < 99) ) i += 1900; if ( (1800 <= i) && (i <= 2100) ) { j = i; if (j != year) /* Test is just to avoid recalc */ leapyear = (j == 2000) || ( (j%4 == 0) && (j%400 != 0) ); } else err ("Illegal year value ",in_ptr); } out[YEAR_FRAG].val = j; year = j; } /* Calculate month. b, B = month name; m = #; j, J, v, V = Julian day */ j = MISSING_TIME_FLAG; if (out[MONTH_FRAG].lev == level) { i = out[MONTH_FRAG].intime_index; /* Which input gave frag? */ if ( ! missing(in[i].var) ) { in_ptr = out[MONTH_FRAG].fragbuf; /* Fragment data */ i = atoi(in_ptr); /* ... as integer */ switch (*out[MONTH_FRAG].source_field_type) { case 'm': if ( (1 <= i) && (i <= 12) ) j = i; else err ("Illegal month value ",in_ptr); break; case 'j': /* Starts from day 1 */ i --; case 'v': /* Starts from day 0 */ /* Missing year = missing Julian day */ if (year != MISSING_TIME_FLAG) { set_monthday_from_julian(i,leapyear); if ( (j = month) == MISSING_TIME_FLAG ) err ("Illegal Julian value ",in_ptr); } break; case 'J': /* 365 day yr (no 29 Feb); starts from day 1 */ i--; case 'V': /* 365 day yr (no 29 Feb); starts from day 0 */ set_monthday_from_julian(i,FALSE); if ( (j = month) == MISSING_TIME_FLAG ) err ("Illegal Julian value ",in_ptr);; case '*': /* Julian work already done for day */ j = month; break; case 'b': /* 3 letter abbrev */ /* Do caseblind compare of input. Tables are in lower case */ ptr = casebuf; ptr2 = in_ptr; while (*ptr2 != '\0') *(ptr++) = tolower(*(ptr2++)); *ptr = '\0'; if (ptr == (casebuf + 3)) { /* Length check */ for (i = 0; i < 12; i++) if (strncmp(monthnames[i],casebuf,3) == 0) break; if (i < 12) j = i + 1; } if (j == MISSING_TIME_FLAG) err ("Illegal month abbreviation ",in_ptr); break; case 'B': /* Full name */ /* Do caseblind compare of input. Tables are in lower case */ ptr = casebuf; ptr2 = in_ptr; while (*ptr2 != '\0') *(ptr++) = tolower(*(ptr2++)); *ptr = '\0'; for (i = 0; i < 12; i++) if (strcmp(monthnames[i],casebuf) == 0) break; if (i < 12) j = i + 1; if (j == MISSING_TIME_FLAG) err ("Illegal month name ",in_ptr); break; default: err ("Illegal month fragment type ", out[MONTH_FRAG].source_field_type); j = MISSING_TIME_FLAG; break; } } out[MONTH_FRAG].val = j; /* Set up maximum # days for this month */ if (j == MISSING_TIME_FLAG) out[DAY_FRAG].max = LONG_MAX; else if ( (j == 2) && leapyear && (year != MISSING_TIME_FLAG) ) out[DAY_FRAG].max = 29; else out[DAY_FRAG].max = cumdays[j] - cumdays[j-1]; } /* Calculate day d = #; j, J, v, V = Julian day */ j = MISSING_TIME_FLAG; if (out[DAY_FRAG].lev == level) { i = out[DAY_FRAG].intime_index; /* Which input gave frag? */ if ( ! missing(in[i].var) ) { in_ptr = out[DAY_FRAG].fragbuf; /* Fragment data */ i = atoi(in_ptr); /* ... as integer */ switch (*out[DAY_FRAG].source_field_type) { case 'd': if ( (1 <= i) && (i <= out[DAY_FRAG].max) ) j = i; else err ("Illegal day value ",in_ptr); break; case 'j': /* 366 day yr; starts from day 1 */ i --; case 'v': /* 366 day yr; starts from day 0 */ /* Missing year = missing Julian day */ if (year != MISSING_TIME_FLAG) { set_monthday_from_julian(i,leapyear); if ( (j = day) == MISSING_TIME_FLAG ) err ("Illegal Julian value ",in_ptr);; } break; case 'J': /* 365 day yr (no 29 Feb); starts from day 1 */ i--; case 'V': /* 365 day yr (no 29 Feb); starts from day 0 */ set_monthday_from_julian(i,FALSE); if ( (j = day) == MISSING_TIME_FLAG ) err ("Illegal Julian value ",in_ptr);; case '*': /* Julian work already done for month */ j = day; break; default: err ("Illegal day fragment type ",out[DAY_FRAG].source_field_type); break; } } out[DAY_FRAG].val = j; } /* Calculate time */ /* Hour from: H (24 hr hour), I & p (12 hour hr & AM/PM flag), */ /* N (24 hr time) or o (fraction of day) */ /* Minute from: M, N, o, or O (fraction of hour) */ /* Fraction of minute from: q, o, O, or S (seconds) & Q */ /* (fraction of second) */ j = MISSING_TIME_FLAG; if (out[HOUR_FRAG].lev == level) { /* See if we have a valid fraction fragment. Since it is not a */ /* valid output variable, looking at the .var piece of the struc- */ /* ture gives us nothing. Look at its source_field_type instead. */ /* If we have any kind of fraction, get it back to a real # < 1, */ /* and compute the precision of any final fractions of a minute. */ /* This precision can be negative, we use it later to round if */ /* necessary. Turns out that the negative precision can be used */ /* from the present calculations, but if precision is changed */ /* here, review the other places it's used */ /* Bob wants minutes output no matter what the precision is, so */ /* that's why we "overlook" this issue */ if ( (fractype = *out[FRACTION_FRAG].source_field_type) != '\0' ) { i = out[FRACTION_FRAG].intime_index; /* Whence this frag? */ if (missing(in[i].var)) frac = MISSING_TIME_FLAG; else { in_ptr = out[FRACTION_FRAG].fragbuf; /* Fragment data */ i = atoi(in_ptr); /* ... as integer */ /* Fraction is string w/o explicit decimal pt; leading decimal */ /* decimal pt assumed. */ fracdigits = strlen(in_ptr); ftmp = -fracdigits; frac = i * pow(10.,ftmp); if (fractype == 'o') fracdigits -= 3; /* .001 day = 1.4 min */ else if (fractype == 'O') fracdigits -= 2; /* .01 hr = .6 min */ else if (fractype == 'Q') fracdigits += 2; /* 100 sec = 1.7 min */ } } /* Hour */ if (fractype == 'o') { fractype = 'O'; /* What's left will supply fraction of hour */ if (frac >= 0.) { frac = modf(24.*frac,&ftmp); /* Hours to j; */ j = ftmp + .1; /* rest back to frac */ } } else { i = out[HOUR_FRAG].intime_index; /* Whence this frag? */ if ( ! missing(in[i].var) ) { in_ptr = out[HOUR_FRAG].fragbuf; /* Fragment data */ i = atoi(in_ptr); /* ... as integer */ switch (*out[HOUR_FRAG].source_field_type) { case 'H': /* 24 hr clock */ if ( (0 <= i) && (i <= 23) ) j = i; else err ("Illegal hour value ",in_ptr); break; case 'I': /* 'I' format - 12 hr clock */ if ( (i < 1) || (i > 12) ) err ("Illegal hour value ",in_ptr); else { /* Checked that AMPM_FRAG data was specified while */ /* setting up in get_timedateparams */ if ( ! missing(in[out[AMPM_FRAG].intime_index].var) ) { in_ptr = out[AMPM_FRAG].fragbuf; /* Fragment data */ switch (check_ampm(in_ptr)) { case 'a': j = i; if (i == 12) j = 0; break; case 'p': j = i; if (i < 12) j = i + 12; break; default: err ("Illegal AM/PM flag ",in_ptr); break; } } } break; case 'N': /* 24 hour time */ digs = div(i,100); if ( (0 <= digs.quot) && (digs.quot <= 23) && (0 <= digs.rem) && (digs.rem <= 59) ) { j = digs.quot; minute = digs.rem; /* Save in case minute requested */ } else { err ("Illegal 24 hour time value ",in_ptr); minute = MISSING_TIME_FLAG; } break; case '*': /* HHMM work already done for minute */ if (hour >= 0) j = hour; break; default: err ("Illegal hour fragment type ",out[HOUR_FRAG].source_field_type); break; } } } /* Do time zone conversion. Offset is 0 if conversion not */ /* requested, not possible, etc. */ if (j != MISSING_TIME_FLAG) j += hours_offset; out[HOUR_FRAG].val = j; if (j != MISSING_TIME_FLAG) { /* Can't have min w/o hour */ /* Minute */ /* Anticipate ng minute-can't set to nd, so use 0, not */ /* MISSING_TIME_FLAG */ j = 0; if (fractype == 'O') { fractype = 'q'; /* What's left will supply fraction of minute */ if (frac > 0.) { /* fracdigits + 2 is number of digits precision for minutes */ /* For 0 digits, provide 0 minutes. For 1 digit, */ /* round to nearest 10 min. For 2 digits, round */ /* to the nearest min. For >2 digits don't round-just */ /* pass on remainder for output as fraction-of-minute. */ /* This is all true, but Bob wants minutes no matter what, */ /* so we are in the <=2, >2 choice only */ frac = modf(60.*frac,&ftmp); /* Minutes to j; */ j = ftmp + .1; /* rest back to frac */ if ( (fracdigits <= 0) && (frac >= .5) ) j++; /* Round */ } } else { if (*out[MINUTE_FRAG].source_field_type != '\0') { i = out[MINUTE_FRAG].intime_index; /* Whence this frag? */ if ( ! missing(in[i].var) ) { in_ptr = out[MINUTE_FRAG].fragbuf; /* Fragment data */ i = atoi(in_ptr); /* ... as integer */ switch (*out[MINUTE_FRAG].source_field_type) { case 'N': /* 24 hour time */ digs = div(i,100); if ( (0 <= digs.quot) && (digs.quot <= 23) && (0 <= digs.rem) && (digs.rem <= 59) ) { j = digs.rem; hour = digs.quot; /* Save in case hour requested */ } else { err ("Illegal 24 hour time value ",in_ptr); hour = MISSING_TIME_FLAG; } break; case '*': /* HHMM work already done for hour */ if (minute >= 0) j = minute; break; case 'M': /* Minute (!too simple) */ if ( (0 <= i) && (i <= 59) ) j = i; else err ("Illegal minute value ",in_ptr); break; default: err ("Illegal minute fragment type ", out[MINUTE_FRAG].source_field_type); break; } } } } /* Do time zone conversion. Offset is 0 if conversion not */ /* requested, not possible, etc. */ if (j != MISSING_TIME_FLAG) j += minutes_offset; out[MINUTE_FRAG].val = j; /* Fraction of minute (if found) */ /* Should really check for seconds found w/o minutes & */ /* fractions-of-seconds found w/o seconds, but we don't */ /* Use frac instead of j for validity flag/value */ /* Fractype could be q or Q at this point. If q, frac */ /* already is carrying "the" validity flag for this datum */ /* If Q, it isn't-validity depends on whole seconds only */ /* (so I guess we really are checking "fractions-of-seconds */ /* found w/o seconds"). Since flag & value functions conflict */ /* in the Q case, use ftmp in non-q situation */ if (fractype != 'q') { ftmp = frac; frac = -1.; if (*out[SECOND_FRAG].source_field_type != '\0') { i = out[SECOND_FRAG].intime_index; /* Whence this frag? */ if ( ! missing(in[i].var) ) { in_ptr = out[SECOND_FRAG].fragbuf; /* Fragment data */ i = atoi(in_ptr); /* ... as integer */ if ( (0 <= i) && (i <= 59) ) { /* If we have valid fractions of seconds, add whole */ /* seconds. Otherwise, set up whole seconds and */ /* corresponding precision */ if ( (fractype == 'Q') && (ftmp > 0.) ) frac = ftmp + i; else { frac = i; fracdigits = 2; } frac /= 60.; /* Convert secs to mins */ } else err ("Illegal second value ",in_ptr); } } } if ( (frac >= 0.) && (fracdigits > 0) ) { if (fracdigits > MAX_OUTTIME_PRECISION) { errn ("Excessive output time precision ignored. Max digits = ", MAX_OUTTIME_PRECISION); fracdigits = MAX_OUTTIME_PRECISION; } /* Do our own roundoff-I don't understand the exact sprintf */ /* rules. Also don't understand c's real to integer rules... */ ftmp = fracdigits; ftmp = pow(10.,ftmp); /* Save next value for carryover purposes. If rounded */ /* value >= this, we have carryover... */ out[FRACTION_FRAG].max = ftmp + .1; frac = modf(frac*ftmp,&ftmp); j = ftmp + .1; if (frac >= .5) j++; out[FRACTION_FRAG].val = j; } else out[FRACTION_FRAG].val = MISSING_TIME_FLAG; } } /* Time displacement */ j = MISSING_TIME_FLAG; if (out[TIME_OFFSET_FRAG].lev == level) { i = out[TIME_OFFSET_FRAG].intime_index; /* Which input gave frag? */ if ( ! missing(in[i].var) ) { in_ptr = out[TIME_OFFSET_FRAG].fragbuf; /* Fragment data */ if (*out[TIME_OFFSET_FRAG].source_field_type == 'Z') { /* Time zone abbrev must be on regular or DST list. If on */ /* DST list, other DST input is a conflict */ /* Do caseblind compare of input. Tables are in upper case */ ptr = casebuf; ptr2 = in_ptr; while (*ptr2 != '\0') *(ptr++) = toupper(*(ptr2++)); *ptr = '\0'; if ( (ptr = lookup_wjstbl(casebuf,TZTBL)) == NULL ) if ( (ptr = lookup_wjstbl(casebuf,TZDSTTBL)) == NULL ) { err ("Time zone abbrev not on defgb's list. Zone = ",in_ptr); tzerr=TRUE; } else if (out[DST_FRAG].var != MISSING_TIME_FLAG) { err("Cannot spec DST input if time zone abbrev spec's DST\n \ Zone = ",in_ptr); tzerr=TRUE; } if (ptr != NULL) i = atoi(ptr); } else { i = atoi(in_ptr); if (*out[TIME_OFFSET_FRAG].source_field_type == 'z') i == -i; } if ( ! tzerr ) { if (abs(i) < 100) { /* HH -> HHMM */ /* Save pieces in case time conversion was requested */ hours_offset = (out[HOUR_FRAG].conv >= 0) ? i : -i; minutes_offset = 0; i *= 100; } if (abs(i) <= 1200) { if (*out[DST_FRAG].source_field_type == '\0') j = i; else { /* DST processing. Displacement is in minutes */ in_ptr=out[DST_FRAG].fragbuf; if (*out[DST_FRAG].source_field_type == 'f') k=atoi(in_ptr); else { k = get_logical_value(in_ptr); if (k == 1) k = 60; } if ( (0 <= k) && (k <= 120) ) { /* Do everything in minutes. Seems simpler */ /* Remainder has sign of quotient, simplifying things */ digs = div(i,100); j = 60*digs.quot + digs.rem + k; /* Minutes displacement */ if (out[HOUR_FRAG].conv >= 0) { /* Save pieces */ hours_offset = digs.quot; /* in case */ minutes_offset = digs.rem; /* time */ } else { /* conversion */ hours_offset = -digs.quot; /* was */ minutes_offset = -digs.rem; /* requested */ } digs = div(j,60); /* Rebuild */ j = 100*digs.quot + digs.rem; /* 4 dig time */ } else err ("Illegal DST flag or displacement ",in_ptr); } } else err ("Illegal UTC displacement ",in_ptr); } } if (j == MISSING_TIME_FLAG) { hours_offset = 0; minutes_offset = 0; } out[TIME_OFFSET_FRAG].val = j; } /* Output necessary frags after rounding. Since gmt<->local quan- */ /* tities have been added in, rounding finishes conversion, too. */ /* We assume that output vars appear in */ /* order (eg, can't have month at level 0 & year at level 1). If */ /* we cannot carry over because of variable level stuff, we output */ /* illegal field value (eg, 2359.999 becomes 2400 if day is not at */ /* same level as time; 2359.999 Oct 31 becomes 0000 Oct 32 if day */ /* is at time level but month isn't; etc) */ /* Carryovers must be done in order of increasing size... */ /* Do fractional time first. Different in that we must regularize */ /* fraction whether or not we can carry to minutes */ /* Note that .max is an impermissible value in this case... */ if (ok_to_redefine(&out[FRACTION_FRAG]),level) if (out[FRACTION_FRAG].val >= out[FRACTION_FRAG].max) { out[FRACTION_FRAG].val -= out[FRACTION_FRAG].max; if (ok_to_redefine(&out[MINUTE_FRAG]),level) out[MINUTE_FRAG].val++; } else if (out[FRACTION_FRAG].val < 0) { out[FRACTION_FRAG].val += out[FRACTION_FRAG].max; if (ok_to_redefine(&out[MINUTE_FRAG]),level) out[MINUTE_FRAG].val--; } td_carryover(&out[MINUTE_FRAG],0,59,60,&out[HOUR_FRAG],level); td_carryover(&out[HOUR_FRAG],0,23,24,&out[DAY_FRAG],level); td_carryover(&out[DAY_FRAG],1,out[DAY_FRAG].max,-1,&out[MONTH_FRAG],level); td_carryover(&out[MONTH_FRAG],1,12,12,&out[YEAR_FRAG],level); /* Finally, output! */ for (j=0; j 0) ) sprintf(out_ptr+4,".%0*d",fracdigits,out[FRACTION_FRAG].val); } else sprintf(out_ptr,out[j].fmt,out[j].val); } } return; }