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 *
get_struct_tm(const vint64 * stamp,int local)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 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 *
common_prettydate(l_fp * ts,int local)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 if (ts->l_ui == 0 && ts->l_uf == 0) {
174 strlcpy (bp, "(no time)", LIB_BUFLENGTH);
175 return (bp);
176 }
177
178 /* get & fix milliseconds */
179 ntps = ts->l_ui;
180 msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */
181 if (msec >= 1000u) {
182 msec -= 1000u;
183 ntps++;
184 }
185 sec = ntpcal_ntp_to_time(ntps, NULL);
186 tm = get_struct_tm(&sec, local);
187 if (!tm) {
188 /*
189 * get a replacement, but always in UTC, using
190 * ntpcal_time_to_date()
191 */
192 struct calendar jd;
193 ntpcal_time_to_date(&jd, &sec);
194 snprintf(bp, LIB_BUFLENGTH, local ? pfmt1 : pfmt0,
195 (u_long)ts->l_ui, (u_long)ts->l_uf,
196 daynames[jd.weekday], months[jd.month-1],
197 jd.monthday, jd.year, jd.hour,
198 jd.minute, jd.second, msec);
199 } else
200 snprintf(bp, LIB_BUFLENGTH, pfmt0,
201 (u_long)ts->l_ui, (u_long)ts->l_uf,
202 daynames[tm->tm_wday], months[tm->tm_mon],
203 tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour,
204 tm->tm_min, tm->tm_sec, msec);
205 return bp;
206 }
207
208
209 char *
prettydate(l_fp * ts)210 prettydate(
211 l_fp *ts
212 )
213 {
214 return common_prettydate(ts, 1);
215 }
216
217
218 char *
gmprettydate(l_fp * ts)219 gmprettydate(
220 l_fp *ts
221 )
222 {
223 return common_prettydate(ts, 0);
224 }
225
226
227 struct tm *
ntp2unix_tm(u_int32 ntp,int local)228 ntp2unix_tm(
229 u_int32 ntp, int local
230 )
231 {
232 vint64 vl;
233 vl = ntpcal_ntp_to_time(ntp, NULL);
234 return get_struct_tm(&vl, local);
235 }
236
237