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 12static char *common_prettydate(l_fp *, int); 13 14const char *months[] = { 15 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 16 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 17}; 18 19static 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 53struct tm * 54ntp2unix_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 int32 dwhi = (SIZEOF_TIME_T > 4) ? (int32)(t >> 16 >> 16) : (int32)(t >> 31); /* double shift: avoid warnings */ 63 64 65 /* Shift NTP to UN*X epoch, then unfold around currrent time. It's 66 * important to use a 32 bit max signed value -- LONG_MAX is 64 bit on 67 * a 64-bit system, and it will give wrong results. 68 */ 69 M_ADD(dwhi, dwlo, 0, ((1UL << 31)-1)); /* 32-bit max signed */ 70 if ((ntp -= JAN_1970) > dwlo) 71 --dwhi; 72 dwlo = ntp; 73 74 if (SIZEOF_TIME_T == 4) { 75 /* 76 ** If the result will not fit into a 'time_t' we have to warp solar 77 ** cycles. That's implemented by looped addition / subtraction with 78 ** M_ADD and M_SUB to avoid implicit 64 bit operations, especially 79 ** division. As he number of warps is rather limited there's no big 80 ** performance loss here. 81 ** 82 ** note: unless the high word doesn't match the sign-extended low word, 83 ** the combination will not fit into time_t. That's what we use for 84 ** loop control here... 85 */ 86 while (dwhi != ((int32)dwlo >> 31)) { 87 if (dwhi < 0 && --folds >= MINFOLD) 88 M_ADD(dwhi, dwlo, 0, SOLAR_CYCLE_SECS); 89 else if (dwhi >= 0 && ++folds <= MAXFOLD) 90 M_SUB(dwhi, dwlo, 0, SOLAR_CYCLE_SECS); 91 else 92 return NULL; 93 } 94 95 } 96 /* combine hi/lo to make time stamp */ 97 t = ((time_t)dwhi << 16 << 16) | dwlo; /* double shift: avoid warnings */ 98 99# ifdef _MSC_VER /* make this an autoconf option? */ 100 101 /* 102 ** The MSDN says that the (Microsoft) Windoze versions of 'gmtime()' 103 ** and 'localtime()' will bark on time stamps < 0. Better to fix it 104 ** immediately. 105 */ 106 while (t < 0) { 107 if (--folds < MINFOLD) 108 return NULL; 109 t += SOLAR_CYCLE_SECS; 110 } 111 112# endif /* Microsoft specific */ 113 114 /* 't' should be a suitable value by now. Just go ahead. */ 115 while ( (tm = (*(local ? localtime : gmtime))(&t)) == 0) 116 /* seems there are some other pathological implementations of 117 ** 'gmtime()' and 'localtime()' somewhere out there. No matter 118 ** if we have 32-bit or 64-bit 'time_t', try to fix this by 119 ** solar cycle warping again... 120 */ 121 if (t < 0) { 122 if (--folds < MINFOLD) 123 return NULL; 124 t += SOLAR_CYCLE_SECS; 125 } else { 126 if ((++folds > MAXFOLD) || ((t -= SOLAR_CYCLE_SECS) < 0)) 127 return NULL; /* That's truely pathological! */ 128 } 129 /* 'tm' surely not NULL here... */ 130 NTP_INSIST(tm != NULL); 131 if (folds != 0) { 132 tm->tm_year += folds * SOLAR_CYCLE_YEARS; 133 if (tm->tm_year <= 0 || tm->tm_year >= 200) 134 return NULL; /* left warp range... can't help here! */ 135 } 136 return tm; 137} 138 139 140static char * 141common_prettydate( 142 l_fp *ts, 143 int local 144 ) 145{ 146 char *bp; 147 struct tm *tm; 148 u_long sec; 149 u_long msec; 150 151 LIB_GETBUF(bp); 152 153 sec = ts->l_ui; 154 msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ 155 156 tm = ntp2unix_tm(sec, local); 157 if (!tm) { 158 (void) sprintf(bp, "%08lx.%08lx --- --- -- ---- --:--:--", 159 (u_long)ts->l_ui, (u_long)ts->l_uf); 160 } 161 else { 162 (void) sprintf(bp, "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu", 163 (u_long)ts->l_ui, (u_long)ts->l_uf, days[tm->tm_wday], 164 months[tm->tm_mon], tm->tm_mday, 1900 + tm->tm_year, 165 tm->tm_hour,tm->tm_min, tm->tm_sec, msec); 166 } 167 168 return bp; 169} 170 171 172char * 173prettydate( 174 l_fp *ts 175 ) 176{ 177 return common_prettydate(ts, 1); 178} 179 180 181char * 182gmprettydate( 183 l_fp *ts 184 ) 185{ 186 return common_prettydate(ts, 0); 187} 188