/src/ntp-dev/libntp/prettydate.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #define SOLAR_CYCLE_SECS 0x34AADC80UL /* 7*1461*86400*/ |
58 | 0 | #define SOLAR_CYCLE_YEARS 28 |
59 | 0 | #define MINFOLD -3 |
60 | 0 | #define MAXFOLD 3 |
61 | | |
62 | | static struct tm * |
63 | | get_struct_tm( |
64 | | const vint64 *stamp, |
65 | | int local) |
66 | 0 | { |
67 | 0 | struct tm *tm = NULL; |
68 | 0 | int32 folds = 0; |
69 | 0 | time_t ts; |
70 | |
|
71 | 0 | #ifdef HAVE_INT64 |
72 | |
|
73 | 0 | int64 tl; |
74 | 0 | 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 | 0 | while (ts != tl) { /* truncation? */ |
81 | 0 | if (tl < 0) { |
82 | 0 | if (--folds < MINFOLD) |
83 | 0 | return NULL; |
84 | 0 | tl += SOLAR_CYCLE_SECS; |
85 | 0 | } else { |
86 | 0 | if (++folds > MAXFOLD) |
87 | 0 | return NULL; |
88 | 0 | tl -= SOLAR_CYCLE_SECS; |
89 | 0 | } |
90 | 0 | ts = tl; /* next try... */ |
91 | 0 | } |
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 | 0 | while ((tm = (*(local ? localtime : gmtime))(&ts)) == NULL) |
132 | 0 | if (ts < 0) { |
133 | 0 | if (--folds < MINFOLD) |
134 | 0 | return NULL; |
135 | 0 | ts += SOLAR_CYCLE_SECS; |
136 | 0 | } else if (ts >= (time_t)SOLAR_CYCLE_SECS) { |
137 | 0 | if (++folds > MAXFOLD) |
138 | 0 | return NULL; |
139 | 0 | ts -= SOLAR_CYCLE_SECS; |
140 | 0 | } else |
141 | 0 | return NULL; /* That's truly pathological! */ |
142 | | |
143 | | /* 'tm' surely not NULL here! */ |
144 | 0 | INSIST(tm != NULL); |
145 | 0 | if (folds != 0) { |
146 | 0 | tm->tm_year += folds * SOLAR_CYCLE_YEARS; |
147 | 0 | if (tm->tm_year <= 0 || tm->tm_year >= 200) |
148 | 0 | return NULL; /* left warp range... can't help here! */ |
149 | 0 | } |
150 | | |
151 | 0 | return tm; |
152 | 0 | } |
153 | | |
154 | | static char * |
155 | | common_prettydate( |
156 | | l_fp *ts, |
157 | | int local |
158 | | ) |
159 | 0 | { |
160 | 0 | static const char pfmt0[] = |
161 | 0 | "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03u"; |
162 | 0 | static const char pfmt1[] = |
163 | 0 | "%08lx.%08lx [%s, %s %2d %4d %2d:%02d:%02d.%03u UTC]"; |
164 | |
|
165 | 0 | char *bp; |
166 | 0 | struct tm *tm; |
167 | 0 | u_int msec; |
168 | 0 | u_int32 ntps; |
169 | 0 | vint64 sec; |
170 | |
|
171 | 0 | LIB_GETBUF(bp); |
172 | |
|
173 | 0 | if (ts->l_ui == 0 && ts->l_uf == 0) { |
174 | 0 | strlcpy (bp, "(no time)", LIB_BUFLENGTH); |
175 | 0 | return (bp); |
176 | 0 | } |
177 | | |
178 | | /* get & fix milliseconds */ |
179 | 0 | ntps = ts->l_ui; |
180 | 0 | msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ |
181 | 0 | if (msec >= 1000u) { |
182 | 0 | msec -= 1000u; |
183 | 0 | ntps++; |
184 | 0 | } |
185 | 0 | sec = ntpcal_ntp_to_time(ntps, NULL); |
186 | 0 | tm = get_struct_tm(&sec, local); |
187 | 0 | if (!tm) { |
188 | | /* |
189 | | * get a replacement, but always in UTC, using |
190 | | * ntpcal_time_to_date() |
191 | | */ |
192 | 0 | struct calendar jd; |
193 | 0 | ntpcal_time_to_date(&jd, &sec); |
194 | 0 | snprintf(bp, LIB_BUFLENGTH, local ? pfmt1 : pfmt0, |
195 | 0 | (u_long)ts->l_ui, (u_long)ts->l_uf, |
196 | 0 | daynames[jd.weekday], months[jd.month-1], |
197 | 0 | jd.monthday, jd.year, jd.hour, |
198 | 0 | jd.minute, jd.second, msec); |
199 | 0 | } else |
200 | 0 | snprintf(bp, LIB_BUFLENGTH, pfmt0, |
201 | 0 | (u_long)ts->l_ui, (u_long)ts->l_uf, |
202 | 0 | daynames[tm->tm_wday], months[tm->tm_mon], |
203 | 0 | tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour, |
204 | 0 | tm->tm_min, tm->tm_sec, msec); |
205 | 0 | return bp; |
206 | 0 | } |
207 | | |
208 | | |
209 | | char * |
210 | | prettydate( |
211 | | l_fp *ts |
212 | | ) |
213 | 0 | { |
214 | 0 | return common_prettydate(ts, 1); |
215 | 0 | } |
216 | | |
217 | | |
218 | | char * |
219 | | gmprettydate( |
220 | | l_fp *ts |
221 | | ) |
222 | 0 | { |
223 | 0 | return common_prettydate(ts, 0); |
224 | 0 | } |
225 | | |
226 | | |
227 | | struct tm * |
228 | | ntp2unix_tm( |
229 | | u_int32 ntp, int local |
230 | | ) |
231 | 0 | { |
232 | 0 | vint64 vl; |
233 | 0 | vl = ntpcal_ntp_to_time(ntp, NULL); |
234 | 0 | return get_struct_tm(&vl, local); |
235 | 0 | } |
236 | | |