Coverage Report

Created: 2023-05-19 06:16

/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