diff options
Diffstat (limited to 'contrib/ntp/libntp/prettydate.c')
-rw-r--r-- | contrib/ntp/libntp/prettydate.c | 273 |
1 files changed, 186 insertions, 87 deletions
diff --git a/contrib/ntp/libntp/prettydate.c b/contrib/ntp/libntp/prettydate.c index 55adf64..5da5ecc 100644 --- a/contrib/ntp/libntp/prettydate.c +++ b/contrib/ntp/libntp/prettydate.c @@ -1,132 +1,231 @@ /* * prettydate - convert a time stamp to something readable */ +#include <config.h> #include <stdio.h> #include "ntp_fp.h" #include "ntp_unixtime.h" /* includes <sys/time.h> */ #include "lib_strbuf.h" #include "ntp_stdlib.h" +#include "ntp_assert.h" +#include "ntp_calendar.h" -static const char *months[] = { +#if SIZEOF_TIME_T < 4 +# error sizeof(time_t) < 4 -- this will not work! +#endif + +static char *common_prettydate(l_fp *, int); + +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 assuming that the localtime/gmtime library functions - have been updated so that they work -*/ - -#define MAX_EPOCH_NR 1000 - -struct tm * -ntp2unix_tm( - u_long ntp, int local - ) +/* + * 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 + +static struct tm * +get_struct_tm( + const vint64 *stamp, + int local) { - time_t t, curr; - struct tm *tm; - int curr_year, epoch_nr; - - /* First get the current year: */ - curr = time(NULL); - tm = local ? localtime(&curr) : gmtime(&curr); - if (!tm) return NULL; + struct tm *tm = NULL; + int32 folds = 0; + time_t ts; + +#ifdef HAVE_INT64 + + int64 tl; + ts = tl = stamp->q_s; + + /* + * 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 + + /* + * 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; + + hi = stamp->D_s.hi; + lo = stamp->D_s.lo; + + 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; - curr_year = 1900 + tm->tm_year; +#endif - /* Convert the ntp timestamp to a unix utc seconds count: */ - t = (time_t) ntp - JAN_1970; + /* + * '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; + ts += SOLAR_CYCLE_SECS; + } else if (ts >= (time_t)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! */ + } - /* Check that the ntp timestamp is not before a 136 year window centered - around the current year: + return tm; +} - Failsafe in case of an infinite loop: - Allow up to 1000 epochs of 136 years each! - */ - for (epoch_nr = 0; epoch_nr < MAX_EPOCH_NR; epoch_nr++) { - tm = local ? localtime(&t) : gmtime(&t); +static char * +common_prettydate( + l_fp *ts, + int local + ) +{ + static const char pfmt0[] = + "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03u"; + static const char pfmt1[] = + "%08lx.%08lx [%s, %s %2d %4d %2d:%02d:%02d.%03u UTC]"; -#if SIZEOF_TIME_T < 4 -# include "Bletch: sizeof(time_t) < 4!" -#endif + char *bp; + struct tm *tm; + u_int msec; + u_int32 ntps; + vint64 sec; -#if SIZEOF_TIME_T == 4 - /* If 32 bits, then year is 1970-2038, so no sense looking */ - epoch_nr = MAX_EPOCH_NR; -#else /* SIZEOF_TIME_T > 4 */ - /* Check that the resulting year is in the correct epoch: */ - if (1900 + tm->tm_year > curr_year - 68) break; + LIB_GETBUF(bp); - /* Epoch wraparound: Add 2^32 seconds! */ - t += (time_t) 65536 << 16; -#endif /* SIZEOF_TIME_T > 4 */ + /* get & fix milliseconds */ + ntps = ts->l_ui; + msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ + if (msec >= 1000u) { + msec -= 1000u; + ntps++; } - return tm; + 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, local ? pfmt1 : pfmt0, + (u_long)ts->l_ui, (u_long)ts->l_uf, + daynames[jd.weekday], months[jd.month-1], + jd.monthday, jd.year, jd.hour, + jd.minute, jd.second, msec); + } else + snprintf(bp, LIB_BUFLENGTH, pfmt0, + (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; } + char * prettydate( l_fp *ts ) { - char *bp; - struct tm *tm; - time_t sec; - u_long msec; - - LIB_GETBUF(bp); - - sec = ts->l_ui; - msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ - - tm = ntp2unix_tm(sec, 1); - if (!tm) { - (void) sprintf(bp, "%08lx.%08lx --- --- -- ---- --:--:--", - (u_long)ts->l_ui, (u_long)ts->l_uf); - } - else { - (void) sprintf(bp, "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu", - (u_long)ts->l_ui, (u_long)ts->l_uf, days[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; + return common_prettydate(ts, 1); } + char * gmprettydate( l_fp *ts ) { - char *bp; - struct tm *tm; - time_t sec; - u_long msec; - - LIB_GETBUF(bp); - - sec = ts->l_ui; - msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ + return common_prettydate(ts, 0); +} - tm = ntp2unix_tm(sec, 0); - if (!tm) { - (void) sprintf(bp, "%08lx.%08lx --- --- -- ---- --:--:--", - (u_long)ts->l_ui, (u_long)ts->l_uf); - } - else { - (void) sprintf(bp, "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu", - (u_long)ts->l_ui, (u_long)ts->l_uf, days[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; +struct tm * +ntp2unix_tm( + u_int32 ntp, int local + ) +{ + vint64 vl; + vl = ntpcal_ntp_to_time(ntp, NULL); + return get_struct_tm(&vl, local); } + |