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 *
get_struct_tm(const vint64 * stamp,int local)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 *
common_prettydate(l_fp * ts,int local)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 *
prettydate(l_fp * ts)209 prettydate(
210 l_fp *ts
211 )
212 {
213 return common_prettydate(ts, 1);
214 }
215
216
217 char *
gmprettydate(l_fp * ts)218 gmprettydate(
219 l_fp *ts
220 )
221 {
222 return common_prettydate(ts, 0);
223 }
224
225
226 struct tm *
ntp2unix_tm(u_int32 ntp,int local)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