1 /* 2 * prettydate - convert a time stamp to something readable 3 */ 4 #include <config.h> 5 #include <stdio.h> 6 7 #include "ntp_fp.h" 8 #include "ntp_unixtime.h" /* includes <sys/time.h> */ 9 #include "lib_strbuf.h" 10 #include "ntp_stdlib.h" 11 #include "ntp_assert.h" 12 #include "ntp_calendar.h" 13 14 #if SIZEOF_TIME_T < 4 15 # error sizeof(time_t) < 4 -- this will not work! 16 #endif 17 18 static char *common_prettydate(l_fp *, int); 19 20 const char * const months[12] = { 21 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 22 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 23 }; 24 25 const char * const daynames[7] = { 26 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 27 }; 28 29 /* Helper function to handle possible wraparound of the ntp epoch. 30 * 31 * Works by periodic extension of the ntp time stamp in the UN*X epoch. 32 * If the 'time_t' is 32 bit, use solar cycle warping to get the value 33 * in a suitable range. Also uses solar cycle warping to work around 34 * really buggy implementations of 'gmtime()' / 'localtime()' that 35 * cannot work with a negative time value, that is, times before 36 * 1970-01-01. (MSVCRT...) 37 * 38 * Apart from that we're assuming that the localtime/gmtime library 39 * functions have been updated so that they work... 40 * 41 * An explanation: The julian calendar repeats ever 28 years, because 42 * it's the LCM of 7 and 1461, the week and leap year cycles. This is 43 * called a 'solar cycle'. The gregorian calendar does the same as 44 * long as no centennial year (divisible by 100, but not 400) goes in 45 * the way. So between 1901 and 2099 (inclusive) we can warp time 46 * stamps by 28 years to make them suitable for localtime() and 47 * gmtime() if we have trouble. Of course this will play hubbubb with 48 * the DST zone switches, so we should do it only if necessary; but as 49 * we NEED a proper conversion to dates via gmtime() we should try to 50 * cope with as many idiosyncrasies as possible. 51 * 52 */ 53 54 /* 55 * solar cycle in unsigned secs and years, and the cycle limits. 56 */ 57 #define SOLAR_CYCLE_SECS 0x34AADC80UL /* 7*1461*86400*/ 58 #define SOLAR_CYCLE_YEARS 28 59 #define MINFOLD -3 60 #define MAXFOLD 3 61 62 static struct tm * 63 get_struct_tm( 64 const vint64 *stamp, 65 int local) 66 { 67 struct tm *tm = NULL; 68 int32 folds = 0; 69 time_t ts; 70 71 #ifdef HAVE_INT64 72 73 int64 tl; 74 ts = tl = stamp->q_s; 75 76 /* 77 * If there is chance of truncation, try to fix it. Let the 78 * compiler find out if this can happen at all. 79 */ 80 while (ts != tl) { /* truncation? */ 81 if (tl < 0) { 82 if (--folds < MINFOLD) 83 return NULL; 84 tl += SOLAR_CYCLE_SECS; 85 } else { 86 if (++folds > MAXFOLD) 87 return NULL; 88 tl -= SOLAR_CYCLE_SECS; 89 } 90 ts = tl; /* next try... */ 91 } 92 #else 93 94 /* 95 * since we do not have 64-bit scalars, it's not likely we have 96 * 64-bit time_t. Assume 32 bits and properly reduce the value. 97 */ 98 u_int32 hi, lo; 99 100 hi = stamp->D_s.hi; 101 lo = stamp->D_s.lo; 102 103 while ((hi && ~hi) || ((hi ^ lo) & 0x80000000u)) { 104 if (M_ISNEG(hi, lo)) { 105 if (--folds < MINFOLD) 106 return NULL; 107 M_ADD(hi, lo, 0, SOLAR_CYCLE_SECS); 108 } else { 109 if (++folds > MAXFOLD) 110 return NULL; 111 M_SUB(hi, lo, 0, SOLAR_CYCLE_SECS); 112 } 113 } 114 ts = (int32)lo; 115 116 #endif 117 118 /* 119 * 'ts' should be a suitable value by now. Just go ahead, but 120 * with care: 121 * 122 * There are some pathological implementations of 'gmtime()' 123 * and 'localtime()' out there. No matter if we have 32-bit or 124 * 64-bit 'time_t', try to fix this by solar cycle warping 125 * again... 126 * 127 * At least the MSDN says that the (Microsoft) Windoze 128 * versions of 'gmtime()' and 'localtime()' will bark on time 129 * stamps < 0. 130 */ 131 while ((tm = (*(local ? localtime : gmtime))(&ts)) == NULL) 132 if (ts < 0) { 133 if (--folds < MINFOLD) 134 return NULL; 135 ts += SOLAR_CYCLE_SECS; 136 } else if (ts >= (time_t)SOLAR_CYCLE_SECS) { 137 if (++folds > MAXFOLD) 138 return NULL; 139 ts -= SOLAR_CYCLE_SECS; 140 } else 141 return NULL; /* That's truly pathological! */ 142 143 /* 'tm' surely not NULL here! */ 144 NTP_INSIST(tm != NULL); 145 if (folds != 0) { 146 tm->tm_year += folds * SOLAR_CYCLE_YEARS; 147 if (tm->tm_year <= 0 || tm->tm_year >= 200) 148 return NULL; /* left warp range... can't help here! */ 149 } 150 151 return tm; 152 } 153 154 static char * 155 common_prettydate( 156 l_fp *ts, 157 int local 158 ) 159 { 160 static const char pfmt0[] = 161 "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03u"; 162 static const char pfmt1[] = 163 "%08lx.%08lx [%s, %s %2d %4d %2d:%02d:%02d.%03u UTC]"; 164 165 char *bp; 166 struct tm *tm; 167 u_int msec; 168 u_int32 ntps; 169 vint64 sec; 170 171 LIB_GETBUF(bp); 172 173 /* get & fix milliseconds */ 174 ntps = ts->l_ui; 175 msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ 176 if (msec >= 1000u) { 177 msec -= 1000u; 178 ntps++; 179 } 180 sec = ntpcal_ntp_to_time(ntps, NULL); 181 tm = get_struct_tm(&sec, local); 182 if (!tm) { 183 /* 184 * get a replacement, but always in UTC, using 185 * ntpcal_time_to_date() 186 */ 187 struct calendar jd; 188 ntpcal_time_to_date(&jd, &sec); 189 snprintf(bp, LIB_BUFLENGTH, local ? pfmt1 : pfmt0, 190 (u_long)ts->l_ui, (u_long)ts->l_uf, 191 daynames[jd.weekday], months[jd.month-1], 192 jd.monthday, jd.year, jd.hour, 193 jd.minute, jd.second, msec); 194 } else 195 snprintf(bp, LIB_BUFLENGTH, pfmt0, 196 (u_long)ts->l_ui, (u_long)ts->l_uf, 197 daynames[tm->tm_wday], months[tm->tm_mon], 198 tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour, 199 tm->tm_min, tm->tm_sec, msec); 200 return bp; 201 } 202 203 204 char * 205 prettydate( 206 l_fp *ts 207 ) 208 { 209 return common_prettydate(ts, 1); 210 } 211 212 213 char * 214 gmprettydate( 215 l_fp *ts 216 ) 217 { 218 return common_prettydate(ts, 0); 219 } 220 221 222 struct tm * 223 ntp2unix_tm( 224 u_int32 ntp, int local 225 ) 226 { 227 vint64 vl; 228 vl = ntpcal_ntp_to_time(ntp, NULL); 229 return get_struct_tm(&vl, local); 230 } 231 232