]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/libntp/prettydate.c
Upgrade NTP to 4.2.8p4.
[FreeBSD/releng/10.2.git] / contrib / ntp / libntp / prettydate.c
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         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