Coverage Report

Created: 2025-07-18 06:53

/src/ntpsec/libntp/ntp_calendar.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * ntp_calendar.c - calendar and helper functions
3
 *
4
 * Copyright Juergen Perlinger <perlinger@ntp.org> for the NTP project.
5
 * Copyright the NTPsec project contributors
6
 * SPDX-License-Identifier: NTP
7
 *
8
 * There is more about these types and calculations in the internals tour
9
 * document distributed with the code.
10
 */
11
#include "config.h"
12
#include <sys/types.h>
13
14
#include "ntp_types.h"
15
#include "ntp_calendar.h"
16
#include "ntp_stdlib.h"
17
#include "ntp_fp.h"
18
#include "PIVOT.h"
19
20
/*
21
 *---------------------------------------------------------------------
22
 * replacing the 'time()' function
23
 * --------------------------------------------------------------------
24
 */
25
26
static ntpcal_split
27
ntpcal_days_in_months(int32_t /* months */);
28
29
static  int32_t
30
ntpcal_edate_to_yeardays(int32_t, int32_t, int32_t);
31
32
/*
33
 *---------------------------------------------------------------------
34
 * Get the build date & time
35
 *---------------------------------------------------------------------
36
 */
37
bool
38
ntpcal_get_build_date(
39
  struct calendar * jd
40
  )
41
0
{
42
0
        time_t epoch = RELEASE_DATE;
43
0
        struct tm epoch_tm;
44
45
0
  ZERO(*jd);
46
0
  jd->year     = 1970;
47
0
  jd->month    = 1;
48
0
  jd->monthday = 1;
49
50
0
        if (NULL == gmtime_r(&epoch, &epoch_tm)) {
51
            /* bad EPOCH */
52
0
      return false;
53
0
        }
54
  /* good EPOCH */
55
0
  jd->year     = epoch_tm.tm_year + 1900;
56
0
  jd->yearday  = epoch_tm.tm_yday + 1;
57
0
  jd->month    = epoch_tm.tm_mon + 1;
58
0
  jd->monthday = epoch_tm.tm_mday;
59
0
  jd->hour     = epoch_tm.tm_hour;
60
0
  jd->minute   = epoch_tm.tm_min;
61
0
  jd->second   = epoch_tm.tm_sec;
62
0
  jd->weekday  = epoch_tm.tm_wday;
63
64
#if 0
65
        fprintf(stderr, "Build: %d %d %d %d %d %d %d %d\n",
66
      (int)jd->year, (int)jd->yearday, (int)jd->month, (int)jd->monthday,
67
      (int)jd->hour, (int)jd->minute, (int)jd->second, (int)jd->weekday);
68
#endif
69
0
        return true;
70
0
}
71
72
73
/*
74
 *---------------------------------------------------------------------
75
 * basic calendar stuff
76
 * --------------------------------------------------------------------
77
 */
78
79
/* month table for a year starting with March,1st */
80
static const uint16_t shift_month_table[13] = {
81
  0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366
82
};
83
84
/* month tables for years starting with January,1st; regular & leap */
85
static const uint16_t real_month_table[2][13] = {
86
  /* -*- table for regular years -*- */
87
  { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
88
  /* -*- table for leap years -*- */
89
  { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
90
};
91
92
/*
93
 * Some notes on the terminology:
94
 *
95
 * We use the proleptic Gregorian calendar, which is the Gregorian
96
 * calendar extended in both directions ad infinitum. This totally
97
 * disregards the fact that this calendar was invented in 1582, and
98
 * was adopted at various dates over the world; sometimes even after
99
 * the start of the NTP epoch.
100
 *
101
 * Normally date parts are given as current cycles, while time parts
102
 * are given as elapsed cycles:
103
 *
104
 * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month,
105
 * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed.
106
 *
107
 * The basic calculations for this calendar implementation deal with
108
 * ELAPSED date units, which is the number of full years, full months
109
 * and full days before a date: 1970-01-01 would be (1969, 0, 0) in
110
 * that notation.
111
 *
112
 * To ease the numeric computations, month and day values outside the
113
 * normal range are acceptable: 2001-03-00 will be treated as the day
114
 * before 2001-03-01, 2000-13-32 will give the same result as
115
 * 2001-02-01 and so on.
116
 *
117
 * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die'
118
 * (day number).  This is the number of days elapsed since 0000-12-31
119
 * in the proleptic Gregorian calendar. The begin of the Christian Era
120
 * (0001-01-01) is RD(1).
121
 *
122
 *
123
 * Some notes on the implementation:
124
 *
125
 * Calendar algorithms thrive on the division operation, which is one of
126
 * the slowest numerical operations in any CPU. What saves us here from
127
 * abysmal performance is the fact that all divisions are divisions by
128
 * constant numbers, and most compilers can do this by a multiplication
129
 * operation.  But this might not work when using the div/ldiv/lldiv
130
 * function family, because many compilers are not able to do inline
131
 * expansion of the code with following optimisation for the
132
 * constant-divider case.
133
 *
134
 * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which
135
 * are inherently target dependent. Nothing that could not be cured with
136
 * autoconf, but still a mess...
137
 *
138
 * Furthermore, we need floor division while C demands truncation to
139
 * zero, so additional steps are required to make sure the algorithms
140
 * work.
141
 *
142
 * For all this, all divisions by constant are coded manually, even when
143
 * there is a joined div/mod operation: The optimiser should sort that
144
 * out, if possible.
145
 *
146
 * Finally, the functions do not check for overflow conditions. This
147
 * is a sacrifice made for execution speed; since a 32-bit day counter
148
 * covers +/- 5,879,610 years, this should not pose a problem here.
149
 */
150
151
152
/*
153
 * ==================================================================
154
 *
155
 * General algorithmic stuff
156
 *
157
 * ==================================================================
158
 */
159
160
/*
161
 *---------------------------------------------------------------------
162
 * Do a periodic extension of 'value' around 'pivot' with a period of
163
 * 'cycle'.
164
 *
165
 * The result 'res' is a number that holds to the following properties:
166
 *
167
 *   1)  res MOD cycle == value MOD cycle
168
 *   2)  pivot <= res < pivot + cycle
169
 *   (replace </<= with >/>= for negative cycles)
170
 *
171
 * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which
172
 * is not the same as the '%' operator in C: C requires division to be
173
 * a truncated division, where remainder and dividend have the same
174
 * sign if the remainder is not zero, whereas floor division requires
175
 * divider and modulus to have the same sign for a non-zero modulus.
176
 *
177
 * This function has some useful applications:
178
 *
179
 * + let Y be a calendar year and V a truncated 2-digit year: then
180
 *  periodic_extend(Y-50, V, 100)
181
 *   is the closest expansion of the truncated year with respect to
182
 *   the full year, that is a 4-digit year with a difference of less
183
 *   than 50 years to the year Y. ("century unfolding")
184
 *
185
 * + let T be a UN*X time stamp and V be seconds-of-day: then
186
 *  perodic_extend(T-43200, V, 86400)
187
 *   is a time stamp that has the same seconds-of-day as the input
188
 *   value, with an absolute difference to T of <= 12hrs.  ("day
189
 *   unfolding")
190
 *
191
 * + Wherever you have a truncated periodic value and a non-truncated
192
 *   base value and you want to match them somehow...
193
 *
194
 * Basically, the function delivers 'pivot + (value - pivot) % cycle',
195
 * but the implementation takes some pains to avoid internal signed
196
 * integer overflows in the '(value - pivot) % cycle' part and adheres
197
 * to the floor division convention.
198
 *
199
 * If 64bit scalars where available on all intended platforms, writing a
200
 * version that uses 64 bit ops would be easy; writing a general
201
 * division routine for 64bit ops on a platform that can only do
202
 * 32/16bit divisions and is still performant is a bit more
203
 * difficult. Since most usecases can be coded in a way that does only
204
 * require the 32-bit version a 64bit version is NOT provided here.
205
 * ---------------------------------------------------------------------
206
 */
207
int32_t
208
ntpcal_periodic_extend(
209
  int32_t pivot,
210
  int32_t value,
211
  int32_t cycle
212
  )
213
0
{
214
0
  uint32_t diff;
215
0
  bool   cpl = false; /* modulo complement flag */
216
0
  bool   neg = false; /* sign change flag     */
217
218
  /* make the cycle positive and adjust the flags */
219
0
  if (cycle < 0) {
220
0
    cycle = - cycle;
221
0
    neg = !neg;
222
0
    cpl = !cpl;
223
0
  }
224
  /* guard against div by zero or one */
225
0
  if (cycle > 1) {
226
    /*
227
     * Get absolute difference as unsigned quantity and
228
     * the complement flag. This is done by always
229
     * subtracting the smaller value from the bigger
230
     * one. This implementation works only on a two's
231
     * complement machine!
232
     */
233
0
    if (value >= pivot) {
234
0
      diff = (uint32_t)value - (uint32_t)pivot;
235
0
    } else {
236
0
      diff = (uint32_t)pivot - (uint32_t)value;
237
0
      cpl = !cpl;
238
0
    }
239
0
    diff %= (uint32_t)cycle;
240
0
    if (diff) {
241
0
      if (cpl)
242
0
        diff = (uint32_t)cycle - diff;
243
0
      if (neg)
244
0
        diff = ~diff + 1;
245
0
      pivot += (int32_t)diff;
246
0
    }
247
0
  }
248
0
  return pivot;
249
0
}
250
251
/*
252
 *-------------------------------------------------------------------
253
 * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X
254
 * scale with proper epoch unfolding around a given pivot or the current
255
 * system time. This function happily accepts negative pivot values as
256
 * timestamps before 1970-01-01, so be aware of possible trouble on
257
 * platforms with 32bit 'time_t'!
258
 *
259
 * This is also a periodic extension, but since the cycle is 2^32 and
260
 * the shift is 2^31, we can do some *very* fast math without explicit
261
 * divisions.
262
 *-------------------------------------------------------------------
263
 */
264
time64_t
265
ntpcal_ntp_to_time(
266
  uint32_t  ntp,
267
  time_t    pivot
268
  )
269
0
{
270
0
  time64_t res;
271
272
0
  settime64s(res, pivot);
273
0
  settime64u(res, time64u(res)-0x80000000); /* unshift of half range */
274
0
  ntp -= (uint32_t)JAN_1970;   /* warp into UN*X domain */
275
0
  ntp -= time64lo(res);    /* cycle difference  */
276
0
  settime64u(res, time64u(res)+(uint64_t)ntp);  /* get expanded time */
277
278
0
  return res;
279
0
}
280
281
/*
282
 *-------------------------------------------------------------------
283
 * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP
284
 * scale with proper epoch unfolding around a given pivot or the current
285
 * system time.
286
 *
287
 * Note: The pivot must be given in the UN*X time domain!
288
 *
289
 * This is also a periodic extension, but since the cycle is 2^32 and
290
 * the shift is 2^31, we can do some *very* fast math without explicit
291
 * divisions.
292
 *-------------------------------------------------------------------
293
 */
294
time64_t
295
ntpcal_ntp_to_ntp(
296
  uint32_t      ntp,
297
  time_t        pivot
298
  )
299
0
{
300
0
  time64_t res;
301
302
0
  settime64s(res, pivot);
303
0
  settime64u(res, time64u(res) - 0x80000000);   /* unshift of half range */
304
0
  settime64u(res, time64u(res) + (uint32_t)JAN_1970); /* warp into NTP domain  */
305
306
0
  ntp -= time64lo(res);        /* cycle difference  */
307
0
  settime64u(res, time64u(res) + (uint64_t)ntp);  /* get expanded time   */
308
309
0
  return res;
310
0
}
311
312
313
/*
314
 * ==================================================================
315
 *
316
 * Splitting values to composite entities
317
 *
318
 * ==================================================================
319
 */
320
321
/*
322
 *-------------------------------------------------------------------
323
 * Split a 64bit seconds value into elapsed days in 'res.hi' and
324
 * elapsed seconds since midnight in 'res.lo' using explicit floor
325
 * division. This function happily accepts negative time values as
326
 * timestamps before the respective epoch start.
327
 * -------------------------------------------------------------------
328
 */
329
ntpcal_split
330
ntpcal_daysplit(
331
  const time64_t ts
332
  )
333
0
{
334
0
  ntpcal_split res;
335
336
  /* manual floor division by SECSPERDAY */
337
0
  res.hi = (int32_t)(time64s(ts) / SECSPERDAY);
338
0
  res.lo = (int32_t)(time64s(ts) % SECSPERDAY);
339
0
  if (res.lo < 0) {
340
0
    res.hi -= 1;
341
0
    res.lo += SECSPERDAY;
342
0
  }
343
344
0
  return res;
345
0
}
346
347
/*
348
 *-------------------------------------------------------------------
349
 * Split a 32bit seconds value into h/m/s and excessive days.  This
350
 * function happily accepts negative time values as timestamps before
351
 * midnight.
352
 * -------------------------------------------------------------------
353
 */
354
static int32_t
355
priv_timesplit(
356
  int32_t split[3],
357
  int32_t ts
358
  )
359
0
{
360
0
  int32_t days = 0;
361
362
  /* make sure we have a positive offset into a day */
363
0
  if (ts < 0 || ts >= SECSPERDAY) {
364
0
    days = ts / SECSPERDAY;
365
0
    ts   = ts % SECSPERDAY;
366
0
    if (ts < 0) {
367
0
      days -= 1;
368
0
      ts   += SECSPERDAY;
369
0
    }
370
0
  }
371
372
  /* get secs, mins, hours */
373
0
  split[2] = (uint8_t)(ts % SECSPERMIN);
374
0
  ts /= SECSPERMIN;
375
0
  split[1] = (uint8_t)(ts % MINSPERHR);
376
0
  split[0] = (uint8_t)(ts / MINSPERHR);
377
378
0
  return days;
379
0
}
380
381
/*
382
 * ---------------------------------------------------------------------
383
 * Given the number of elapsed days in the calendar era, split this
384
 * number into the number of elapsed years in 'res.hi' and the number
385
 * of elapsed days of that year in 'res.lo'.
386
 *
387
 * if 'isleapyear' is not NULL, it will receive an integer that is 0 for
388
 * regular years and a non-zero value for leap years.
389
 *---------------------------------------------------------------------
390
 */
391
ntpcal_split
392
ntpcal_split_eradays(
393
  int32_t days,
394
  int32_t *isleapyear
395
  )
396
0
{
397
0
  ntpcal_split res;
398
0
  int32_t      n400, n100, n004, n001, yday; /* calendar year cycles */
399
400
  /*
401
   * Split off calendar cycles, using floor division in the first
402
   * step. After that first step, simple division does it because
403
   * all operands are positive; alas, we have to be aware of the
404
   * possible cycle overflows for 100 years and 1 year, caused by
405
   * the additional leap day.
406
   */
407
0
  n400 = days / GREGORIAN_CYCLE_DAYS;
408
0
  yday = days % GREGORIAN_CYCLE_DAYS;
409
0
  if (yday < 0) {
410
0
    n400 -= 1;
411
0
    yday += GREGORIAN_CYCLE_DAYS;
412
0
  }
413
0
  n100 = yday / GREGORIAN_NORMAL_CENTURY_DAYS;
414
0
  yday = yday % GREGORIAN_NORMAL_CENTURY_DAYS;
415
0
  n004 = yday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
416
0
  yday = yday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
417
0
  n001 = yday / DAYSPERYEAR;
418
0
  yday = yday % DAYSPERYEAR;
419
420
  /*
421
   * check for leap cycle overflows and calculate the leap flag
422
   * if needed
423
   */
424
0
  if ((n001 | n100) > 3) {
425
    /* hit last day of leap year */
426
0
    n001 -= 1;
427
0
    yday += DAYSPERYEAR;
428
0
    if (isleapyear) {
429
0
      *isleapyear = 1;
430
0
    }
431
0
  } else if (isleapyear) {
432
0
    *isleapyear = (n001 == 3) && ((n004 != 24) || (n100 == 3));
433
0
  }
434
435
  /* now merge the cycles to elapsed years, using horner scheme */
436
0
  res.hi = ((4*n400 + n100)*25 + n004)*4 + n001;
437
0
  res.lo = yday;
438
439
0
  return res;
440
0
}
441
442
/*
443
 *---------------------------------------------------------------------
444
 * Given a number of elapsed days in a year and a leap year indicator,
445
 * split the number of elapsed days into the number of elapsed months in
446
 * 'res.hi' and the number of elapsed days of that month in 'res.lo'.
447
 *
448
 * This function will fail and return {-1,-1} if the number of elapsed
449
 * days is not in the valid range!
450
 *---------------------------------------------------------------------
451
 */
452
ntpcal_split
453
ntpcal_split_yeardays(
454
  int32_t eyd,
455
  bool    isleapyear
456
  )
457
0
{
458
0
  ntpcal_split    res;
459
0
  const uint16_t *lt; /* month length table */
460
461
  /* check leap year flag and select proper table */
462
0
  lt = real_month_table[(isleapyear != 0)];
463
0
  if (0 <= eyd && eyd < lt[12]) {
464
    /* get zero-based month by approximation & correction step */
465
0
    res.hi = eyd >> 5;     /* approx month; might be 1 too low */
466
0
    if (lt[res.hi + 1] <= eyd) /* fixup approximative month value  */
467
0
      res.hi += 1;
468
0
    res.lo = eyd - lt[res.hi];
469
0
  } else {
470
0
    res.lo = res.hi = -1;
471
0
  }
472
473
0
  return res;
474
0
}
475
476
/*
477
 *---------------------------------------------------------------------
478
 * Convert a RD into the date part of a 'struct calendar'.
479
 * Returns -1 on calculation overflow.
480
 *---------------------------------------------------------------------
481
 */
482
int
483
ntpcal_rd_to_date(
484
  struct calendar *jd,
485
  int32_t    rd
486
  )
487
0
{
488
0
  ntpcal_split split;
489
0
  int32_t      leaps;
490
0
  int32_t      retv;
491
492
0
  leaps = 0;
493
0
  retv = 0;
494
  /* Get day-of-week first. Since rd is signed, the remainder can
495
   * be in the range [-6..+6], but the assignment to an unsigned
496
   * variable maps the negative values to positive values >=7.
497
   * This makes the sign correction look strange, but adding 7
498
   * causes the needed wrap-around into the desired value range of
499
   * zero to six, both inclusive.
500
   */
501
0
  jd->weekday = rd % 7;
502
0
  if (jd->weekday >= 7) /* unsigned! */
503
0
    jd->weekday += 7;
504
505
0
  split = ntpcal_split_eradays(rd - 1, &leaps);
506
0
  retv  = (int)leaps;
507
  /* get year and day-of-year */
508
0
  jd->year = (uint16_t)split.hi + 1;
509
0
  if (jd->year != split.hi + 1) {
510
0
    jd->year = 0;
511
0
    retv   = -1;  /* bletch. overflow trouble. */
512
0
  }
513
0
  jd->yearday = (uint16_t)split.lo + 1;
514
515
  /* convert to month and mday */
516
0
  split = ntpcal_split_yeardays(split.lo, leaps);
517
0
  jd->month    = (uint8_t)split.hi + 1;
518
0
  jd->monthday = (uint8_t)split.lo + 1;
519
520
0
  return retv ? retv : leaps;
521
0
}
522
523
/*
524
 *---------------------------------------------------------------------
525
 * Take a value of seconds since midnight and split it into hhmmss in a
526
 * 'struct calendar'.
527
 *---------------------------------------------------------------------
528
 */
529
int32_t
530
ntpcal_daysec_to_date(
531
  struct calendar *jd,
532
  int32_t   sec
533
  )
534
0
{
535
0
  int32_t days;
536
0
  int   ts[3];
537
538
0
  days = priv_timesplit(ts, sec);
539
0
  jd->hour   = (uint8_t)ts[0];
540
0
  jd->minute = (uint8_t)ts[1];
541
0
  jd->second = (uint8_t)ts[2];
542
543
0
  return days;
544
0
}
545
546
/*
547
 *---------------------------------------------------------------------
548
 * Take a UN*X time and convert to a calendar structure.
549
 *---------------------------------------------------------------------
550
 */
551
int
552
ntpcal_time_to_date(
553
  struct calendar *jd,
554
  const time64_t ts
555
  )
556
0
{
557
0
  ntpcal_split ds;
558
559
0
  ds = ntpcal_daysplit(ts);
560
0
  ds.hi += ntpcal_daysec_to_date(jd, ds.lo);
561
0
  ds.hi += DAY_UNIX_STARTS;
562
563
0
  return ntpcal_rd_to_date(jd, ds.hi);
564
0
}
565
566
567
/*
568
 * ==================================================================
569
 *
570
 * merging composite entities
571
 *
572
 * ==================================================================
573
 */
574
575
/*
576
 *---------------------------------------------------------------------
577
 * Merge a number of days and a number of seconds into seconds,
578
 * expressed in 64 bits to avoid overflow.
579
 *---------------------------------------------------------------------
580
 */
581
time64_t
582
ntpcal_dayjoin(
583
  int32_t days,
584
  int32_t secs
585
  )
586
0
{
587
0
  time64_t res;
588
589
0
  settime64s(res, days);
590
0
  settime64s(res, time64s(res) * SECSPERDAY);
591
0
  settime64s(res, time64s(res) + secs);
592
593
0
  return res;
594
0
}
595
596
/*
597
 *---------------------------------------------------------------------
598
 * Convert elapsed years in Era into elapsed days in Era.
599
 *
600
 * To accommodate for negative values of years, floor division would be
601
 * required for all division operations. This can be eased by first
602
 * splitting the years into full 400-year cycles and years in the
603
 * cycle. Only this operation must be coded as a full floor division; as
604
 * the years in the cycle is a non-negative number, all other divisions
605
 * can be regular truncated divisions.
606
 *---------------------------------------------------------------------
607
 */
608
int32_t
609
ntpcal_days_in_years(
610
  int32_t years
611
  )
612
0
{
613
0
  int32_t cycle; /* full gregorian cycle */
614
615
  /* split off full calendar cycles, using floor division */
616
0
  cycle = years / 400;
617
0
  years = years % 400;
618
0
  if (years < 0) {
619
0
    cycle -= 1;
620
0
    years += 400;
621
0
  }
622
623
  /*
624
   * Calculate days in cycle. years now is a non-negative number,
625
   * holding the number of years in the 400-year cycle.
626
   */
627
0
  return cycle * GREGORIAN_CYCLE_DAYS
628
0
       + years * DAYSPERYEAR  /* days inregular years */
629
0
       + years / 4    /* 4 year leap rule */
630
0
       - years / 100;   /* 100 year leap rule */
631
  /* the 400-year rule does not apply due to full-cycle split-off */
632
0
}
633
634
/*
635
 *---------------------------------------------------------------------
636
 * Convert a number of elapsed month in a year into elapsed days in year.
637
 *
638
 * The month will be normalized, and 'res.hi' will contain the
639
 * excessive years that must be considered when converting the years,
640
 * while 'res.lo' will contain the number of elapsed days since start
641
 * of the year.
642
 *
643
 * This code uses the shifted-month-approach to convert month to days,
644
 * because then there is no need to have explicit leap year
645
 * information.  The slight disadvantage is that for most month values
646
 * the result is a negative value, and the year excess is one; the
647
 * conversion is then simply based on the start of the following year.
648
 *---------------------------------------------------------------------
649
 */
650
static ntpcal_split
651
ntpcal_days_in_months(
652
  int32_t m
653
  )
654
0
{
655
0
  ntpcal_split res;
656
657
  /* normalize month into range */
658
0
  res.hi = 0;
659
0
  res.lo = m;
660
0
  if (res.lo < 0 || res.lo >= 12) {
661
0
    res.hi = res.lo / 12;
662
0
    res.lo = res.lo % 12;
663
0
    if (res.lo < 0) {
664
0
      res.hi -= 1;
665
0
      res.lo += 12;
666
0
    }
667
0
  }
668
669
  /* add 10 month for year starting with march */
670
0
  if (res.lo < 2)
671
0
    res.lo += 10;
672
0
  else {
673
0
    res.hi += 1;
674
0
    res.lo -= 2;
675
0
  }
676
677
  /* get cumulated days in year with unshift */
678
0
  res.lo = shift_month_table[res.lo] - 306;
679
680
0
  return res;
681
0
}
682
683
/*
684
 *---------------------------------------------------------------------
685
 * Convert ELAPSED years/months/days of gregorian calendar to elapsed
686
 * days in Gregorian epoch.
687
 *
688
 * If you want to convert years and days-of-year, just give a month of
689
 * zero.
690
 *---------------------------------------------------------------------
691
 */
692
int32_t
693
ntpcal_edate_to_eradays(
694
  int32_t years,
695
  int32_t mons,
696
  int32_t mdays
697
  )
698
0
{
699
0
  ntpcal_split tmp;
700
0
  int32_t      res;
701
702
0
  if (mons) {
703
0
    tmp = ntpcal_days_in_months(mons);
704
0
    res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo;
705
0
  } else {
706
0
    res = ntpcal_days_in_years(years);
707
0
  }
708
0
  res += mdays;
709
710
0
  return res;
711
0
}
712
713
/*
714
 *---------------------------------------------------------------------
715
 * Convert ELAPSED years/months/days of gregorian calendar to elapsed
716
 * days in year.
717
 *
718
 * Note: This will give the true difference to the start of the given year,
719
 * even if months & days are off-scale.
720
 *---------------------------------------------------------------------
721
 */
722
static int32_t
723
ntpcal_edate_to_yeardays(
724
  int32_t years,
725
  int32_t mons,
726
  int32_t mdays
727
  )
728
0
{
729
0
  ntpcal_split tmp;
730
731
0
  if (0 <= mons && mons < 12) {
732
0
    years += 1;
733
0
    mdays += real_month_table[is_leapyear(years)][mons];
734
0
  } else {
735
0
    tmp = ntpcal_days_in_months(mons);
736
0
    mdays += tmp.lo
737
0
           + ntpcal_days_in_years(years + tmp.hi)
738
0
           - ntpcal_days_in_years(years);
739
0
  }
740
741
0
  return mdays;
742
0
}
743
744
/*
745
 *---------------------------------------------------------------------
746
 * Convert elapsed days and the hour/minute/second information into
747
 * total seconds.
748
 *
749
 * If 'isvalid' is not NULL, do a range check on the time specification
750
 * and tell if the time input is in the normal range, permitting for a
751
 * single leapsecond.
752
 *---------------------------------------------------------------------
753
 */
754
int32_t
755
ntpcal_etime_to_seconds(
756
  int32_t hours,
757
  int32_t minutes,
758
  int32_t seconds
759
  )
760
0
{
761
0
  int32_t res;
762
763
0
  res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds;
764
765
0
  return res;
766
0
}
767
768
/*
769
 *---------------------------------------------------------------------
770
 * Convert the date part of a 'struct tm' (that is, year, month,
771
 * day-of-month) into the RD of that day.
772
 *---------------------------------------------------------------------
773
 */
774
int32_t
775
ntpcal_tm_to_rd(
776
  const struct tm *utm
777
  )
778
0
{
779
0
  return ntpcal_edate_to_eradays(utm->tm_year + 1899,
780
0
               utm->tm_mon,
781
0
               utm->tm_mday - 1) + 1;
782
0
}
783
784
/*
785
 *---------------------------------------------------------------------
786
 * Convert the date part of a 'struct calendar' (that is, year, month,
787
 * day-of-month) into the RD of that day.
788
 *---------------------------------------------------------------------
789
 */
790
int32_t
791
ntpcal_date_to_rd(
792
  const struct calendar *jd
793
  )
794
0
{
795
0
  return ntpcal_edate_to_eradays((int32_t)jd->year - 1,
796
0
               (int32_t)jd->month - 1,
797
0
               (int32_t)jd->monthday - 1) + 1;
798
0
}
799
800
/*
801
 *---------------------------------------------------------------------
802
 * take a 'struct calendar' and get the seconds-of-day from it.
803
 *---------------------------------------------------------------------
804
 */
805
int32_t
806
ntpcal_date_to_daysec(
807
  const struct calendar *jd
808
  )
809
0
{
810
0
  return ntpcal_etime_to_seconds(jd->hour, jd->minute,
811
0
               jd->second);
812
0
}
813
814
/*
815
 *---------------------------------------------------------------------
816
 * take a 'struct tm' and get the seconds-of-day from it.
817
 *---------------------------------------------------------------------
818
 */
819
int32_t
820
ntpcal_tm_to_daysec(
821
  const struct tm *utm
822
  )
823
0
{
824
0
  return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min,
825
0
               utm->tm_sec);
826
0
}
827
828
/*
829
 *---------------------------------------------------------------------
830
 * take a 'struct calendar' and convert it to a 'time_t'
831
 *---------------------------------------------------------------------
832
 */
833
time_t
834
ntpcal_date_to_time(
835
  const struct calendar *jd
836
  )
837
0
{
838
0
  time64_t  join;
839
0
  int32_t days, secs;
840
841
0
  days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS;
842
0
  secs = ntpcal_date_to_daysec(jd);
843
0
  join = ntpcal_dayjoin(days, secs);
844
845
  /* might truncate if time_t is 32 bits */
846
0
  return (time_t)join;
847
0
}
848
849
850
int
851
ntpcal_ntp64_to_date(
852
  struct calendar *jd,
853
  const time64_t  ntp
854
  )
855
0
{
856
0
  ntpcal_split ds;
857
858
0
  ds = ntpcal_daysplit(ntp);
859
0
  ds.hi += ntpcal_daysec_to_date(jd, ds.lo);
860
861
0
  return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS);
862
0
}
863
864
int
865
ntpcal_ntp_to_date(
866
  struct calendar *jd,
867
  uint32_t   ntp,
868
  const time_t  piv
869
  )
870
0
{
871
0
  time64_t  ntp64;
872
873
  /*
874
   * Unfold ntp time around current time into NTP domain. Split
875
   * into days and seconds, shift days into CE domain and
876
   * process the parts.
877
   */
878
0
  ntp64 = ntpcal_ntp_to_ntp(ntp, piv);
879
0
  return ntpcal_ntp64_to_date(jd, ntp64);
880
0
}
881
882
/*
883
 * ymd2yd - compute the date in the year from y/m/d
884
 *
885
 * A thin wrapper around a more general calendar function.
886
 */
887
888
int
889
ymd2yd(
890
  int y,
891
  int m,
892
  int d)
893
0
{
894
  /*
895
   * convert y/m/d to elapsed calendar units, convert that to
896
   * elapsed days since the start of the given year and convert
897
   * back to unity-based day in year.
898
   *
899
   * This does no further error checking, since the underlying
900
   * function is assumed to work out how to handle the data.
901
   */
902
0
  return ntpcal_edate_to_yeardays(y-1, m-1, d-1) + 1;
903
0
}
904
/* -*-EOF-*- */