#include "math.h" #include "dbhost.h" /* PROTOTYPE_ALLOWED */ #include "dbext.h" /* YMDHMS_TIME_TYPE, ULONG, etc., round */ #include "time_.h" #include "ioserv.h" /* yr4digit() */ /***************************************************************************** * * * COMMON OCEANOGRAPHIC DATA ACCESS SYSTEM (CODAS) * * * * WRITTEN BY: RAMON CABRERA, ERIC FIRING, and JULIE RANADA * * JOINT INSTITUTE FOR MARINE AND ATMOSPHERIC RESEARCH * * 1000 POPE ROAD MSB 404 * * HONOLULU, HI 96822 * * * * VERSION: 3.00 * * * * DATE: APRIL 1989 * * * *****************************************************************************/ /*----------------------------------------------------------------------------- FILE: time_.c TIME CONVERSION & PRINTING FUNCTIONS */ int CUM_MONTH_DAYS[] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; /*----------------------------------------------------------------------------- FUNCTION: invalid_time It checks a YMDHMS_TIME_TYPE structure for valid ranges. PARAMETER: t = pointer to YMDHMS_TIME_TYPE structure RETURNS: 0 if time is okay 1 otherwise CALLED FROM: C */ int invalid_time(YMDHMS_TIME_TYPE *t) { if ((t->year < 1) || (t->year > 4095) || (t->month < 1) || (t->month > 12) || (t->day < 1) || (t->day > 31) || (t->hour > 23) || (t->minute > 59) || (to_hundredths_sec_s(t->second) > 5999)) return(INVALID_TIME); return(0); } /*----------------------------------------------------------------------------- FUNCTION: TIMMIN Given a pointer to a ymdhms_time structure it returns the time in minutes since "JANUARY 1, 1 at 00:00" Note: no validity check is done on t PARAMETER: t = pointer to struct/array of type YMDHMS_TIME_TYPE RETURNS: minutes from "JAN 1, 1 at 00:00" CALLED FROM: C, FORTRAN */ ULONG TIMMIN(YMDHMS_TIME_TYPE *t) { ULONG l_1440 = 1440, l_365 = 365; ULONG m, y; m = l_1440 * (CUM_MONTH_DAYS[t->month] + t->day - 1) + 60 * t->hour + t->minute; if ((y = t->year - 1) > 0) m += l_1440 * (l_365 * y + y / 4 - y / 100 + y / 400); if (!(t->year % 4) && ((t->year % 100) || !(t->year % 400)) && t->month > 2) m += l_1440; return(m); } /*----------------------------------------------------------------------------- FUNCTION: MINTIM Given time in minutes since Jan 1, 1 at 00:00, it calculates the YMDHMS_TIME_TYPE equivalent. PARAMETERS: t = pointer to YMDHMS_TIME_TYPE struct/array m = pointer to LONG variable where the minutes are stored RETURNS: VOID CALLED FROM: C, FORTRAN */ void MINTIM(YMDHMS_TIME_TYPE *t, LONG *m) { LONG d, y, y1, im, d1, l_1440 = 1440, l_365 = 365; t->second = 0; t->minute = *m % 60; t->hour = (*m / 60) % 24; d = *m / l_1440; y = d / 365.2425; d1 = y * l_365 + y / 4 - y / 100 + y / 400; if (d > d1) { d -= (d1 - 1); y++; } else { y1 = y - 1; d -= (y1 * l_365 + y1 / 4 - y / 100 + y / 400 - 1); } if (d > l_365) { if ((y % 4) || (!(y % 100) && (y % 400))) { d -= l_365; y++; } else if (d > 366) { d -= 366; y++; } } if (!(y % 4) && ((y % 100) || !(y % 400))) if (d <= 60) { if (d > 31) { im = 2; d -= 31; } else im = 1; } else { im = (--d) / 29; if (d > CUM_MONTH_DAYS[im + 1]) im++; d -= CUM_MONTH_DAYS[im]; } else { im = d / 29; if (d > CUM_MONTH_DAYS[im + 1]) im++; d -= CUM_MONTH_DAYS[im]; } t->year = y; t->month = im; t->day = d; return; } /*----------------------------------------------------------------------------- FUNCTION: TIMDIF Given pointers to two YMDHMS_TIME_TYPE structures, it returns the difference in seconds between the second and the first times. Note: no validity check is done on t1 and t2 PARAMETERS: t1 = pointer to first time t2 = pointer to second time RETURNS: (t2 - t1) in seconds CALLED FROM: C, FORTRAN */ LONG TIMDIF(YMDHMS_TIME_TYPE *t1, YMDHMS_TIME_TYPE *t2) { return((TIMMIN(t2) - TIMMIN(t1)) * 60 + ((long)t2->second - (long)t1->second)); } /*----------------------------------------------------------------------------- FUNCTION: DIFTIM Given a reference YMDHMS_TIME_TYPE t1 and a number of seconds s from that time, this function calculates the YMDHMS_TIME_TYPE t2 as t1 + s. Note: no validity check is done on t1 and t2 PARAMETERS: t1 = pointer to reference YMDHMS_TIME_TYPE t2 = pointer to resulting YMDHMS_TIM_TYPE s = pointer to number of seconds from reference time t1 RETURNS: VOID CALLED FROM: C, FORTRAN */ void DIFTIM(YMDHMS_TIME_TYPE *t1, YMDHMS_TIME_TYPE *t2, LONG *s) { LONG m, ss; ss = to_hundredths_sec_s(t1->second) / 100 + (*s); m = TIMMIN(t1) + div_pr(ss,60); MINTIM(t2, &m); t2->second = mod_pr(ss,60); } /*----------------------------------------------------------------------------- FUNCTION: HTIMDIF Given pointers to two YMDHMS_TIME_TYPE structures, it returns the difference in hundredths of seconds between the second and the first times. Note: no validity check is done on t1 and t2 MAXLONG can accommodate time differences of up to +/-68 years if expressed in seconds, 0.68 year or 248 days if in hundredths of seconds. Since TIMDIF is used in the core database routines to perform calculations on profile times with respect to a block base time, this switch from returning a difference in seconds to one in hundredths of seconds restricts a block to contain a time range no more than 248 days. - JR 95/09 PARAMETERS: t1 = pointer to first time t2 = pointer to second time RETURNS: (t2 - t1) in hundredths of seconds CALLED FROM: C, FORTRAN */ LONG HTIMDIF(YMDHMS_TIME_TYPE *t1, YMDHMS_TIME_TYPE *t2) { return((TIMMIN(t2) - TIMMIN(t1)) * 6000 + ((long)to_hundredths_sec_s(t2->second) - (long)to_hundredths_sec_s(t1->second))); } /*----------------------------------------------------------------------------- FUNCTION: HDIFTIM Given a reference YMDHMS_TIME_TYPE t1 and a number of hundredths of seconds hs from that time, this function calculates the YMDHMS_TIME_TYPE t2 as t1 + hs. Note: no validity check is done on t1 and t2 Since hs is a signed value, we have to just set it to be expressed in hundredths of seconds (rather than do the usual sign bit test to determine if it is in seconds or hundredths of seconds). - JR 95/09 PARAMETERS: t1 = pointer to reference YMDHMS_TIME_TYPE t2 = pointer to resulting YMDHMS_TIME_TYPE hs = pointer to number of hundredths of seconds from reference time t1 RETURNS: VOID CALLED FROM: C, FORTRAN */ void HDIFTIM(YMDHMS_TIME_TYPE *t1, YMDHMS_TIME_TYPE *t2, LONG *hs) { LONG m, hss; hss = to_hundredths_sec_s(t1->second) + (*hs); m = TIMMIN(t1) + div_pr(hss,6000); MINTIM(t2, &m); t2->second = mod_pr(hss,6000); t2->second |= 0x8000; } /*----------------------------------------------------------------------------- FUNCTION: TIMCMP It compares two YMDHMS_TIME_TYPE structures/arrays. Note: no validity check is done on t1 and t2 PARAMETERS: t1 = pointer to first time t2 = pointer to second time RETURNS: 0 if t1 = t2 1 if t1 < t2 2 if t2 < t1 CALLED FROM: C, FORTRAN */ LONG TIMCMP(YMDHMS_TIME_TYPE *t1, YMDHMS_TIME_TYPE *t2) { USHORT t1_hs, t2_hs; if (t1->year < t2->year) return(1); if (t1->year > t2->year) return(2); if (t1->month < t2->month) return(1); if (t1->month > t2->month) return(2); if (t1->day < t2->day) return(1); if (t1->day > t2->day) return(2); if (t1->hour < t2->hour) return(1); if (t1->hour > t2->hour) return(2); if (t1->minute < t2->minute) return(1); if (t1->minute > t2->minute) return(2); t1_hs = to_hundredths_sec_s(t1->second); t2_hs = to_hundredths_sec_s(t2->second); if (t1_hs < t2_hs) return(1); if (t1_hs > t2_hs) return(2); return(0); } /*----------------------------------------------------------------------------- FUNCTION: PCKTIM Given a YMDHMS_TIME_TYPE structure, it returns a packed block time. PARAMETER: t = pointer to YMDHMS_TIME_TYPE structure RETURNS: packed block time CALLED FROM: C, FORTRAN */ ULONG PCKTIM(YMDHMS_TIME_TYPE *t) { return(t->minute + (BIT6 * t->hour) + (BIT11 * t->day) + (BIT16 * t->month) + (BIT20 * t->year)); } /*----------------------------------------------------------------------------- FUNCTION: UPCKTIM It unpacks a packed block time into a YMDHMS_TIME_TYPE structure. PARAMETERS: t = pointer to YMDHMS_TIME_TYPE structure block_time = pointer to packed block time RETURNS: VOID CALLED FROM: C, FORTRAN */ void UPCKTIM(YMDHMS_TIME_TYPE *t, ULONG *block_time) { t->second = 0; t->minute = *block_time % BIT6; t->hour = (*block_time / BIT6) % BIT5; t->day = (*block_time / BIT11) % BIT5; t->month = (*block_time / BIT16) % BIT4; t->year = (*block_time / BIT20); return; } /*----------------------------------------------------------------------------- FUNCTION: year_day It converts a YMDHMS_TIME_TYPE into a double-precision number of days from the beginning of a base year. NOTE: If this number is given with seven digits after the decimal, there is no loss of accuracy. Therefore a double precision number is adequate for a period of many years from the base time. PARAMETERS: ymdhms_time = time to be converted year_base = year to use RETURNS: time in decimal days */ double year_day(YMDHMS_TIME_TYPE *time, int year_base) #define hundredths_seconds_per_minute 6000.0 #define hundredths_seconds_per_day 8640000.0 { YMDHMS_TIME_TYPE base; base.year = year_base; base.month = base.day = 1; /* January 1 */ base.hour = base.minute = base.second = 0; return( ((LONG) (TIMMIN(time) - TIMMIN(&base))) * hundredths_seconds_per_minute + to_hundredths_sec_s(time->second)) / hundredths_seconds_per_day; } #define minutes_per_day 1440.0 #define seconds_per_day 86400.0 /*----------------------------------------------------------------------------- FUNCTION: yd_to_ymdhmh_time It converts time expressed in decimal days to a YMDHMH_TIME_TYPE structure (using sign bit in seconds field to specify precision to hundredths of a second). PARAMETERS: yd = time to be converted (double-precision decimal days) year_base = year to use time_ptr = ptr to YMDHMS_TIME_TYPE result RETURNS: void */ void yd_to_ymdhmh_time(double yd, int year_base, YMDHMS_TIME_TYPE *time_ptr) { YMDHMS_TIME_TYPE base; LONG yd_min, y; base.year = year_base; base.month = base.day = 1; /* January 1 */ base.hour = base.minute = base.second = 0; yd_min = (long)(yd * minutes_per_day); y = yd_min + TIMMIN(&base); MINTIM(time_ptr, &y); time_ptr->second = ((yd * seconds_per_day - yd_min * 60.0) * 100.0); /* An automatic cast, hence truncation, is used above to avoid the problem of rounding 59.999 seconds up to 60.00. */ time_ptr->second |= 0x8000; return; } /*----------------------------------------------------------------------------- FUNCTION: yd_to_ymdhms_time It converts time expressed in decimal days to a YMDHMS_TIME_TYPE structure. PARAMETERS: yd = time to be converted (double-precision decimal days) year_base = year to use time_ptr = ptr to YMDHMS_TIME_TYPE result RETURNS: void */ void yd_to_ymdhms_time(double yd, int year_base, YMDHMS_TIME_TYPE *time_ptr) { YMDHMS_TIME_TYPE base, base_min; double dsec; LONG sec; LONG yd_min, y; base.year = year_base; base.month = base.day = 1; base.hour = base.minute = base.second = 0; yd_min = (long)(yd * minutes_per_day); y = yd_min + TIMMIN(&base); MINTIM(&base_min, &y); dsec = (yd * minutes_per_day - yd_min) * 60.0; sec = (long) round_val(dsec); DIFTIM(&base_min, time_ptr, &sec); return; } int get_ymdhms_time(FILE *fp, YMDHMS_TIME_TYPE *t) { char buff[80]; double hs = -1.0; /* for user input in real seconds */ double dd; int year; int d1, d2; t->hour = t->minute = t->second = 0; fgets(buff, 80, fp); if (sscanf(buff, " %hd %hd %hd %hd %hd %lf", &(t->year), &(t->month), &(t->day), &(t->hour), &(t->minute), &hs) < 3) { if (sscanf(buff, " %d.%d", &d1, &d2) == 2) return(1); /* year is missing */ if (sscanf(buff, " %d %lf", &year, &dd) != 2) return(1); else { year = yr4digit(year); yd_to_ymdhms_time(dd, year, t); return(0); } } t->year = yr4digit(t->year); t->second = set_hs(hs); return(0); } /* set_hs is a very special-purpose function: it takes a POSITIVE double precision number of seconds, which must be LESS THAN 59.995 (so that multiplied by 100 and rounded it will be less than 6000), and returns a USHORT suitable for the seconds field of a YMDHMS or YMDHMH structure. There is NO error checking. */ USHORT set_hs(double hs) { USHORT s, hun; hun = round_val(hs*100); /* hundredths */ s = round_val(hs); if (100*s != hun) { /* store as YMDHMH-type */ s = hun; s |= 0x8000; /* set sign bit flag */ } return(s); } /* When working with a database with time in seconds, not hundredths, it is important to search using time in seconds. If a time was read in as a decimal day and converted to YMDHMS, it will typically be in hundredths, so it should be rounded to the nearest second. */ void round_sec(YMDHMS_TIME_TYPE *t) { if (t->second & 0x8000) { unsigned int hs, h, s; LONG one = 1L; hs = t->second ^ 0x8000; s = hs/100; h = hs - s * 100; t->second = s; if ( h > 49 ) { DIFTIM(t, t, &one); /* Add one second. */ } } }