Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / libntp / prettydate.c
blob290bc8835e4153354b941ca7fe6b53bc65f3a6f1
1 /* $NetBSD$ */
3 /*
4 * prettydate - convert a time stamp to something readable
5 */
6 #include <stdio.h>
8 #include "ntp_fp.h"
9 #include "ntp_unixtime.h" /* includes <sys/time.h> */
10 #include "lib_strbuf.h"
11 #include "ntp_stdlib.h"
12 #include "ntp_assert.h"
14 static char *common_prettydate(l_fp *, int);
16 const char *months[] = {
17 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
18 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
21 static const char *days[] = {
22 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
25 /* Helper function to handle possible wraparound of the ntp epoch.
27 Works by periodic extension of the ntp time stamp in the NTP epoch. If the
28 'time_t' is 32 bit, use solar cycle warping to get the value in a suitable
29 range. Also uses solar cycle warping to work around really buggy
30 implementations of 'gmtime()' / 'localtime()' that cannot work with a
31 negative time value, that is, times before 1970-01-01. (MSVCRT...)
33 Apart from that we're assuming that the localtime/gmtime library functions
34 have been updated so that they work...
38 /* solar cycle in secs, unsigned secs and years. And the cycle limits.
40 ** And an explanation. The julian calendar repeats ever 28 years, because it's
41 ** the LCM of 7 and 4, the week and leap year cycles. This is called a 'solar
42 ** cycle'. The gregorian calendar does the same as long as no centennial year
43 ** (divisible by 100, but not 400) goes in the way. So between 1901 and 2099
44 ** (inclusive) we can warp time stamps by 28 years to make them suitable for
45 ** localtime() and gmtime() if we have trouble. Of course this will play
46 ** hubbubb with the DST zone switches, so we should do it only if necessary;
47 ** but as we NEED a proper conversion to dates via gmtime() we should try to
48 ** cope with as many idiosyncrasies as possible.
50 #define SOLAR_CYCLE_SECS 0x34AADC80UL /* 7*1461*86400*/
51 #define SOLAR_CYCLE_YEARS 28
52 #define MINFOLD -3
53 #define MAXFOLD 3
55 struct tm *
56 ntp2unix_tm(
57 u_long ntp, int local
60 struct tm *tm;
61 int32 folds = 0;
62 time_t t = time(NULL);
63 u_int32 dwlo = (int32)t; /* might expand for SIZEOF_TIME_T < 4 */
64 #if ( SIZEOF_TIME_T > 4 )
65 int32 dwhi = (int32)(t >> 16 >> 16);/* double shift: avoid warnings */
66 #else
68 * Get the correct sign extension in the high part.
69 * (now >> 32) may not work correctly on every 32 bit
70 * system, e.g. it yields garbage under Win32/VC6.
72 int32 dwhi = (int32)(t >> 31);
73 #endif
75 /* Shift NTP to UN*X epoch, then unfold around currrent time. It's
76 * important to use a 32 bit max signed value -- LONG_MAX is 64 bit on
77 * a 64-bit system, and it will give wrong results.
79 M_ADD(dwhi, dwlo, 0, ((1UL << 31)-1)); /* 32-bit max signed */
80 if ((ntp -= JAN_1970) > dwlo)
81 --dwhi;
82 dwlo = ntp;
84 # if SIZEOF_TIME_T < 4
85 # error sizeof(time_t) < 4 -- this will not work!
86 # elif SIZEOF_TIME_T == 4
89 ** If the result will not fit into a 'time_t' we have to warp solar
90 ** cycles. That's implemented by looped addition / subtraction with
91 ** M_ADD and M_SUB to avoid implicit 64 bit operations, especially
92 ** division. As he number of warps is rather limited there's no big
93 ** performance loss here.
95 ** note: unless the high word doesn't match the sign-extended low word,
96 ** the combination will not fit into time_t. That's what we use for
97 ** loop control here...
99 while (dwhi != ((int32)dwlo >> 31)) {
100 if (dwhi < 0 && --folds >= MINFOLD)
101 M_ADD(dwhi, dwlo, 0, SOLAR_CYCLE_SECS);
102 else if (dwhi >= 0 && ++folds <= MAXFOLD)
103 M_SUB(dwhi, dwlo, 0, SOLAR_CYCLE_SECS);
104 else
105 return NULL;
108 # else
110 /* everything fine -- no reduction needed for the next thousand years */
112 # endif
114 /* combine hi/lo to make time stamp */
115 t = ((time_t)dwhi << 16 << 16) | dwlo; /* double shift: avoid warnings */
117 # ifdef _MSC_VER /* make this an autoconf option? */
120 ** The MSDN says that the (Microsoft) Windoze versions of 'gmtime()'
121 ** and 'localtime()' will bark on time stamps < 0. Better to fix it
122 ** immediately.
124 while (t < 0) {
125 if (--folds < MINFOLD)
126 return NULL;
127 t += SOLAR_CYCLE_SECS;
130 # endif /* Microsoft specific */
132 /* 't' should be a suitable value by now. Just go ahead. */
133 while ( (tm = (*(local ? localtime : gmtime))(&t)) == 0)
134 /* seems there are some other pathological implementations of
135 ** 'gmtime()' and 'localtime()' somewhere out there. No matter
136 ** if we have 32-bit or 64-bit 'time_t', try to fix this by
137 ** solar cycle warping again...
139 if (t < 0) {
140 if (--folds < MINFOLD)
141 return NULL;
142 t += SOLAR_CYCLE_SECS;
143 } else {
144 if ((++folds > MAXFOLD) || ((t -= SOLAR_CYCLE_SECS) < 0))
145 return NULL; /* That's truely pathological! */
147 /* 'tm' surely not NULL here... */
148 NTP_INSIST(tm != NULL);
149 if (folds != 0) {
150 tm->tm_year += folds * SOLAR_CYCLE_YEARS;
151 if (tm->tm_year <= 0 || tm->tm_year >= 200)
152 return NULL; /* left warp range... can't help here! */
154 return tm;
158 static char *
159 common_prettydate(
160 l_fp *ts,
161 int local
164 char *bp;
165 struct tm *tm;
166 u_long sec;
167 u_long msec;
169 LIB_GETBUF(bp);
171 sec = ts->l_ui;
172 msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */
174 tm = ntp2unix_tm(sec, local);
175 if (!tm) {
176 (void) sprintf(bp, "%08lx.%08lx --- --- -- ---- --:--:--",
177 (u_long)ts->l_ui, (u_long)ts->l_uf);
179 else {
180 (void) sprintf(bp, "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu",
181 (u_long)ts->l_ui, (u_long)ts->l_uf, days[tm->tm_wday],
182 months[tm->tm_mon], tm->tm_mday, 1900 + tm->tm_year,
183 tm->tm_hour,tm->tm_min, tm->tm_sec, msec);
186 return bp;
190 char *
191 prettydate(
192 l_fp *ts
195 return common_prettydate(ts, 1);
199 char *
200 gmprettydate(
201 l_fp *ts
204 return common_prettydate(ts, 0);