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