/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-*- */ |