summaryrefslogtreecommitdiff
path: root/libntp/prettydate.c
diff options
context:
space:
mode:
Diffstat (limited to 'libntp/prettydate.c')
-rw-r--r--libntp/prettydate.c277
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);
+}
+