diff options
Diffstat (limited to 'libntp/prettydate.c')
-rw-r--r-- | libntp/prettydate.c | 277 |
1 files changed, 152 insertions, 125 deletions
diff --git a/libntp/prettydate.c b/libntp/prettydate.c index 1503a2ce87e3..f12129716199 100644 --- a/libntp/prettydate.c +++ b/libntp/prettydate.c @@ -1,6 +1,7 @@ /* * prettydate - convert a time stamp to something readable */ +#include <config.h> #include <stdio.h> #include "ntp_fp.h" @@ -8,180 +9,194 @@ #include "lib_strbuf.h" #include "ntp_stdlib.h" #include "ntp_assert.h" +#include "ntp_calendar.h" + +#if SIZEOF_TIME_T < 4 +# error sizeof(time_t) < 4 -- this will not work! +#endif static char *common_prettydate(l_fp *, int); -const char *months[] = { +const char * const months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -static const char *days[] = { +const char * const daynames[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; /* Helper function to handle possible wraparound of the ntp epoch. + * + * Works by periodic extension of the ntp time stamp in the UN*X epoch. + * If the 'time_t' is 32 bit, use solar cycle warping to get the value + * in a suitable range. Also uses solar cycle warping to work around + * really buggy implementations of 'gmtime()' / 'localtime()' that + * cannot work with a negative time value, that is, times before + * 1970-01-01. (MSVCRT...) + * + * Apart from that we're assuming that the localtime/gmtime library + * functions have been updated so that they work... + * + * An explanation: The julian calendar repeats ever 28 years, because + * it's the LCM of 7 and 1461, the week and leap year cycles. This is + * called a 'solar cycle'. The gregorian calendar does the same as + * long as no centennial year (divisible by 100, but not 400) goes in + * the way. So between 1901 and 2099 (inclusive) we can warp time + * stamps by 28 years to make them suitable for localtime() and + * gmtime() if we have trouble. Of course this will play hubbubb with + * the DST zone switches, so we should do it only if necessary; but as + * we NEED a proper conversion to dates via gmtime() we should try to + * cope with as many idiosyncrasies as possible. + * + */ - Works by periodic extension of the ntp time stamp in the NTP epoch. If the - 'time_t' is 32 bit, use solar cycle warping to get the value in a suitable - range. Also uses solar cycle warping to work around really buggy - implementations of 'gmtime()' / 'localtime()' that cannot work with a - negative time value, that is, times before 1970-01-01. (MSVCRT...) - - Apart from that we're assuming that the localtime/gmtime library functions - have been updated so that they work... -*/ - - -/* solar cycle in secs, unsigned secs and years. And the cycle limits. -** -** And an explanation. The julian calendar repeats ever 28 years, because it's -** the LCM of 7 and 4, the week and leap year cycles. This is called a 'solar -** cycle'. The gregorian calendar does the same as long as no centennial year -** (divisible by 100, but not 400) goes in the way. So between 1901 and 2099 -** (inclusive) we can warp time stamps by 28 years to make them suitable for -** localtime() and gmtime() if we have trouble. Of course this will play -** hubbubb with the DST zone switches, so we should do it only if necessary; -** but as we NEED a proper conversion to dates via gmtime() we should try to -** cope with as many idiosyncrasies as possible. -*/ +/* + * solar cycle in unsigned secs and years, and the cycle limits. + */ #define SOLAR_CYCLE_SECS 0x34AADC80UL /* 7*1461*86400*/ #define SOLAR_CYCLE_YEARS 28 #define MINFOLD -3 -#define MAXFOLD 3 +#define MAXFOLD 3 -struct tm * -ntp2unix_tm( - u_long ntp, int local - ) +static struct tm * +get_struct_tm( + const vint64 *stamp, + int local) { - struct tm *tm; - int32 folds = 0; - time_t t = time(NULL); - u_int32 dwlo = (int32)t; /* might expand for SIZEOF_TIME_T < 4 */ -#if ( SIZEOF_TIME_T > 4 ) - int32 dwhi = (int32)(t >> 16 >> 16);/* double shift: avoid warnings */ -#else - /* - * Get the correct sign extension in the high part. - * (now >> 32) may not work correctly on every 32 bit - * system, e.g. it yields garbage under Win32/VC6. - */ - int32 dwhi = (int32)(t >> 31); -#endif - - /* Shift NTP to UN*X epoch, then unfold around currrent time. It's - * important to use a 32 bit max signed value -- LONG_MAX is 64 bit on - * a 64-bit system, and it will give wrong results. - */ - M_ADD(dwhi, dwlo, 0, ((1UL << 31)-1)); /* 32-bit max signed */ - if ((ntp -= JAN_1970) > dwlo) - --dwhi; - dwlo = ntp; - -# if SIZEOF_TIME_T < 4 -# error sizeof(time_t) < 4 -- this will not work! -# elif SIZEOF_TIME_T == 4 + struct tm *tm = NULL; + int32 folds = 0; + time_t ts; + +#ifdef HAVE_INT64 + + int64 tl; + ts = tl = stamp->q_s; /* - ** If the result will not fit into a 'time_t' we have to warp solar - ** cycles. That's implemented by looped addition / subtraction with - ** M_ADD and M_SUB to avoid implicit 64 bit operations, especially - ** division. As he number of warps is rather limited there's no big - ** performance loss here. - ** - ** note: unless the high word doesn't match the sign-extended low word, - ** the combination will not fit into time_t. That's what we use for - ** loop control here... - */ - while (dwhi != ((int32)dwlo >> 31)) { - if (dwhi < 0 && --folds >= MINFOLD) - M_ADD(dwhi, dwlo, 0, SOLAR_CYCLE_SECS); - else if (dwhi >= 0 && ++folds <= MAXFOLD) - M_SUB(dwhi, dwlo, 0, SOLAR_CYCLE_SECS); - else - return NULL; + * If there is chance of truncation, try to fix it. Let the + * compiler find out if this can happen at all. + */ + while (ts != tl) { /* truncation? */ + if (tl < 0) { + if (--folds < MINFOLD) + return NULL; + tl += SOLAR_CYCLE_SECS; + } else { + if (++folds > MAXFOLD) + return NULL; + tl -= SOLAR_CYCLE_SECS; + } + ts = tl; /* next try... */ } +#else -# else - - /* everything fine -- no reduction needed for the next thousand years */ + /* + * since we do not have 64-bit scalars, it's not likely we have + * 64-bit time_t. Assume 32 bits and properly reduce the value. + */ + u_int32 hi, lo; -# endif + hi = stamp->D_s.hi; + lo = stamp->D_s.lo; - /* combine hi/lo to make time stamp */ - t = ((time_t)dwhi << 16 << 16) | dwlo; /* double shift: avoid warnings */ + while ((hi && ~hi) || ((hi ^ lo) & 0x80000000u)) { + if (M_ISNEG(hi, lo)) { + if (--folds < MINFOLD) + return NULL; + M_ADD(hi, lo, 0, SOLAR_CYCLE_SECS); + } else { + if (++folds > MAXFOLD) + return NULL; + M_SUB(hi, lo, 0, SOLAR_CYCLE_SECS); + } + } + ts = (int32)lo; -# ifdef _MSC_VER /* make this an autoconf option? */ +#endif /* - ** The MSDN says that the (Microsoft) Windoze versions of 'gmtime()' - ** and 'localtime()' will bark on time stamps < 0. Better to fix it - ** immediately. - */ - while (t < 0) { - if (--folds < MINFOLD) - return NULL; - t += SOLAR_CYCLE_SECS; - } - -# endif /* Microsoft specific */ - - /* 't' should be a suitable value by now. Just go ahead. */ - while ( (tm = (*(local ? localtime : gmtime))(&t)) == 0) - /* seems there are some other pathological implementations of - ** 'gmtime()' and 'localtime()' somewhere out there. No matter - ** if we have 32-bit or 64-bit 'time_t', try to fix this by - ** solar cycle warping again... - */ - if (t < 0) { + * 'ts' should be a suitable value by now. Just go ahead, but + * with care: + * + * There are some pathological implementations of 'gmtime()' + * and 'localtime()' out there. No matter if we have 32-bit or + * 64-bit 'time_t', try to fix this by solar cycle warping + * again... + * + * At least the MSDN says that the (Microsoft) Windoze + * versions of 'gmtime()' and 'localtime()' will bark on time + * stamps < 0. + */ + while ((tm = (*(local ? localtime : gmtime))(&ts)) == NULL) + if (ts < 0) { if (--folds < MINFOLD) return NULL; - t += SOLAR_CYCLE_SECS; - } else { - if ((++folds > MAXFOLD) || ((t -= SOLAR_CYCLE_SECS) < 0)) - return NULL; /* That's truely pathological! */ - } - /* 'tm' surely not NULL here... */ + ts += SOLAR_CYCLE_SECS; + } else if (ts >= SOLAR_CYCLE_SECS) { + if (++folds > MAXFOLD) + return NULL; + ts -= SOLAR_CYCLE_SECS; + } else + return NULL; /* That's truly pathological! */ + + /* 'tm' surely not NULL here! */ NTP_INSIST(tm != NULL); if (folds != 0) { tm->tm_year += folds * SOLAR_CYCLE_YEARS; if (tm->tm_year <= 0 || tm->tm_year >= 200) return NULL; /* left warp range... can't help here! */ } + return tm; } - static char * common_prettydate( l_fp *ts, int local ) { - char *bp; - struct tm *tm; - u_long sec; - u_long msec; + static const char* pfmt[2] = { + "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03u", + "%08lx.%08lx [%s, %s %2d %4d %2d:%02d:%02d.%03u UTC]" + }; + + char *bp; + struct tm *tm; + u_int msec; + u_int32 ntps; + vint64 sec; LIB_GETBUF(bp); - - sec = ts->l_ui; - msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ - tm = ntp2unix_tm(sec, local); - if (!tm) - snprintf(bp, LIB_BUFLENGTH, - "%08lx.%08lx --- --- -- ---- --:--:--", - (u_long)ts->l_ui, (u_long)ts->l_uf); - else - snprintf(bp, LIB_BUFLENGTH, - "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu", + /* get & fix milliseconds */ + ntps = ts->l_ui; + msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ + if (msec >= 1000u) { + msec -= 1000u; + ntps++; + } + sec = ntpcal_ntp_to_time(ntps, NULL); + tm = get_struct_tm(&sec, local); + if (!tm) { + /* + * get a replacement, but always in UTC, using + * ntpcal_time_to_date() + */ + struct calendar jd; + ntpcal_time_to_date(&jd, &sec); + snprintf(bp, LIB_BUFLENGTH, pfmt[local != 0], (u_long)ts->l_ui, (u_long)ts->l_uf, - days[tm->tm_wday], months[tm->tm_mon], + daynames[jd.weekday], months[jd.month-1], + jd.monthday, jd.year, jd.hour, + jd.minute, jd.second, msec); + } else + snprintf(bp, LIB_BUFLENGTH, pfmt[0], + (u_long)ts->l_ui, (u_long)ts->l_uf, + daynames[tm->tm_wday], months[tm->tm_mon], tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec, msec); - return bp; } @@ -202,3 +217,15 @@ gmprettydate( { return common_prettydate(ts, 0); } + + +struct tm * +ntp2unix_tm( + u_int32 ntp, int local + ) +{ + vint64 vl; + vl = ntpcal_ntp_to_time(ntp, NULL); + return get_struct_tm(&vl, local); +} + |