Annotation of embedaddon/ntp/libntp/prettydate.c, revision 1.1
1.1 ! misho 1: /*
! 2: * prettydate - convert a time stamp to something readable
! 3: */
! 4: #include <stdio.h>
! 5:
! 6: #include "ntp_fp.h"
! 7: #include "ntp_unixtime.h" /* includes <sys/time.h> */
! 8: #include "lib_strbuf.h"
! 9: #include "ntp_stdlib.h"
! 10: #include "ntp_assert.h"
! 11:
! 12: static char *common_prettydate(l_fp *, int);
! 13:
! 14: const char *months[] = {
! 15: "Jan", "Feb", "Mar", "Apr", "May", "Jun",
! 16: "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
! 17: };
! 18:
! 19: static const char *days[] = {
! 20: "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
! 21: };
! 22:
! 23: /* Helper function to handle possible wraparound of the ntp epoch.
! 24:
! 25: Works by periodic extension of the ntp time stamp in the NTP epoch. If the
! 26: 'time_t' is 32 bit, use solar cycle warping to get the value in a suitable
! 27: range. Also uses solar cycle warping to work around really buggy
! 28: implementations of 'gmtime()' / 'localtime()' that cannot work with a
! 29: negative time value, that is, times before 1970-01-01. (MSVCRT...)
! 30:
! 31: Apart from that we're assuming that the localtime/gmtime library functions
! 32: have been updated so that they work...
! 33: */
! 34:
! 35:
! 36: /* solar cycle in secs, unsigned secs and years. And the cycle limits.
! 37: **
! 38: ** And an explanation. The julian calendar repeats ever 28 years, because it's
! 39: ** the LCM of 7 and 4, the week and leap year cycles. This is called a 'solar
! 40: ** cycle'. The gregorian calendar does the same as long as no centennial year
! 41: ** (divisible by 100, but not 400) goes in the way. So between 1901 and 2099
! 42: ** (inclusive) we can warp time stamps by 28 years to make them suitable for
! 43: ** localtime() and gmtime() if we have trouble. Of course this will play
! 44: ** hubbubb with the DST zone switches, so we should do it only if necessary;
! 45: ** but as we NEED a proper conversion to dates via gmtime() we should try to
! 46: ** cope with as many idiosyncrasies as possible.
! 47: */
! 48: #define SOLAR_CYCLE_SECS 0x34AADC80UL /* 7*1461*86400*/
! 49: #define SOLAR_CYCLE_YEARS 28
! 50: #define MINFOLD -3
! 51: #define MAXFOLD 3
! 52:
! 53: struct tm *
! 54: ntp2unix_tm(
! 55: u_long ntp, int local
! 56: )
! 57: {
! 58: struct tm *tm;
! 59: int32 folds = 0;
! 60: time_t t = time(NULL);
! 61: u_int32 dwlo = (int32)t; /* might expand for SIZEOF_TIME_T < 4 */
! 62: #if ( SIZEOF_TIME_T > 4 )
! 63: int32 dwhi = (int32)(t >> 16 >> 16);/* double shift: avoid warnings */
! 64: #else
! 65: /*
! 66: * Get the correct sign extension in the high part.
! 67: * (now >> 32) may not work correctly on every 32 bit
! 68: * system, e.g. it yields garbage under Win32/VC6.
! 69: */
! 70: int32 dwhi = (int32)(t >> 31);
! 71: #endif
! 72:
! 73: /* Shift NTP to UN*X epoch, then unfold around currrent time. It's
! 74: * important to use a 32 bit max signed value -- LONG_MAX is 64 bit on
! 75: * a 64-bit system, and it will give wrong results.
! 76: */
! 77: M_ADD(dwhi, dwlo, 0, ((1UL << 31)-1)); /* 32-bit max signed */
! 78: if ((ntp -= JAN_1970) > dwlo)
! 79: --dwhi;
! 80: dwlo = ntp;
! 81:
! 82: # if SIZEOF_TIME_T < 4
! 83: # error sizeof(time_t) < 4 -- this will not work!
! 84: # elif SIZEOF_TIME_T == 4
! 85:
! 86: /*
! 87: ** If the result will not fit into a 'time_t' we have to warp solar
! 88: ** cycles. That's implemented by looped addition / subtraction with
! 89: ** M_ADD and M_SUB to avoid implicit 64 bit operations, especially
! 90: ** division. As he number of warps is rather limited there's no big
! 91: ** performance loss here.
! 92: **
! 93: ** note: unless the high word doesn't match the sign-extended low word,
! 94: ** the combination will not fit into time_t. That's what we use for
! 95: ** loop control here...
! 96: */
! 97: while (dwhi != ((int32)dwlo >> 31)) {
! 98: if (dwhi < 0 && --folds >= MINFOLD)
! 99: M_ADD(dwhi, dwlo, 0, SOLAR_CYCLE_SECS);
! 100: else if (dwhi >= 0 && ++folds <= MAXFOLD)
! 101: M_SUB(dwhi, dwlo, 0, SOLAR_CYCLE_SECS);
! 102: else
! 103: return NULL;
! 104: }
! 105:
! 106: # else
! 107:
! 108: /* everything fine -- no reduction needed for the next thousand years */
! 109:
! 110: # endif
! 111:
! 112: /* combine hi/lo to make time stamp */
! 113: t = ((time_t)dwhi << 16 << 16) | dwlo; /* double shift: avoid warnings */
! 114:
! 115: # ifdef _MSC_VER /* make this an autoconf option? */
! 116:
! 117: /*
! 118: ** The MSDN says that the (Microsoft) Windoze versions of 'gmtime()'
! 119: ** and 'localtime()' will bark on time stamps < 0. Better to fix it
! 120: ** immediately.
! 121: */
! 122: while (t < 0) {
! 123: if (--folds < MINFOLD)
! 124: return NULL;
! 125: t += SOLAR_CYCLE_SECS;
! 126: }
! 127:
! 128: # endif /* Microsoft specific */
! 129:
! 130: /* 't' should be a suitable value by now. Just go ahead. */
! 131: while ( (tm = (*(local ? localtime : gmtime))(&t)) == 0)
! 132: /* seems there are some other pathological implementations of
! 133: ** 'gmtime()' and 'localtime()' somewhere out there. No matter
! 134: ** if we have 32-bit or 64-bit 'time_t', try to fix this by
! 135: ** solar cycle warping again...
! 136: */
! 137: if (t < 0) {
! 138: if (--folds < MINFOLD)
! 139: return NULL;
! 140: t += SOLAR_CYCLE_SECS;
! 141: } else {
! 142: if ((++folds > MAXFOLD) || ((t -= SOLAR_CYCLE_SECS) < 0))
! 143: return NULL; /* That's truely pathological! */
! 144: }
! 145: /* 'tm' surely not NULL here... */
! 146: NTP_INSIST(tm != NULL);
! 147: if (folds != 0) {
! 148: tm->tm_year += folds * SOLAR_CYCLE_YEARS;
! 149: if (tm->tm_year <= 0 || tm->tm_year >= 200)
! 150: return NULL; /* left warp range... can't help here! */
! 151: }
! 152: return tm;
! 153: }
! 154:
! 155:
! 156: static char *
! 157: common_prettydate(
! 158: l_fp *ts,
! 159: int local
! 160: )
! 161: {
! 162: char *bp;
! 163: struct tm *tm;
! 164: u_long sec;
! 165: u_long msec;
! 166:
! 167: LIB_GETBUF(bp);
! 168:
! 169: sec = ts->l_ui;
! 170: msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */
! 171:
! 172: tm = ntp2unix_tm(sec, local);
! 173: if (!tm)
! 174: snprintf(bp, LIB_BUFLENGTH,
! 175: "%08lx.%08lx --- --- -- ---- --:--:--",
! 176: (u_long)ts->l_ui, (u_long)ts->l_uf);
! 177: else
! 178: snprintf(bp, LIB_BUFLENGTH,
! 179: "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu",
! 180: (u_long)ts->l_ui, (u_long)ts->l_uf,
! 181: days[tm->tm_wday], months[tm->tm_mon],
! 182: tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour,
! 183: tm->tm_min, tm->tm_sec, msec);
! 184:
! 185: return bp;
! 186: }
! 187:
! 188:
! 189: char *
! 190: prettydate(
! 191: l_fp *ts
! 192: )
! 193: {
! 194: return common_prettydate(ts, 1);
! 195: }
! 196:
! 197:
! 198: char *
! 199: gmprettydate(
! 200: l_fp *ts
! 201: )
! 202: {
! 203: return common_prettydate(ts, 0);
! 204: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>