/src/ntp-dev/libntp/ntp_calendar.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ntp_calendar.c - calendar and helper functions |
3 | | * |
4 | | * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. |
5 | | * The contents of 'html/copyright.html' apply. |
6 | | * |
7 | | * -------------------------------------------------------------------- |
8 | | * Some notes on the implementation: |
9 | | * |
10 | | * Calendar algorithms thrive on the division operation, which is one of |
11 | | * the slowest numerical operations in any CPU. What saves us here from |
12 | | * abysmal performance is the fact that all divisions are divisions by |
13 | | * constant numbers, and most compilers can do this by a multiplication |
14 | | * operation. But this might not work when using the div/ldiv/lldiv |
15 | | * function family, because many compilers are not able to do inline |
16 | | * expansion of the code with following optimisation for the |
17 | | * constant-divider case. |
18 | | * |
19 | | * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which |
20 | | * are inherently target dependent. Nothing that could not be cured with |
21 | | * autoconf, but still a mess... |
22 | | * |
23 | | * Furthermore, we need floor division in many places. C either leaves |
24 | | * the division behaviour undefined (< C99) or demands truncation to |
25 | | * zero (>= C99), so additional steps are required to make sure the |
26 | | * algorithms work. The {l,ll}div function family is requested to |
27 | | * truncate towards zero, which is also the wrong direction for our |
28 | | * purpose. |
29 | | * |
30 | | * For all this, all divisions by constant are coded manually, even when |
31 | | * there is a joined div/mod operation: The optimiser should sort that |
32 | | * out, if possible. Most of the calculations are done with unsigned |
33 | | * types, explicitely using two's complement arithmetics where |
34 | | * necessary. This minimises the dependecies to compiler and target, |
35 | | * while still giving reasonable to good performance. |
36 | | * |
37 | | * The implementation uses a few tricks that exploit properties of the |
38 | | * two's complement: Floor division on negative dividents can be |
39 | | * executed by using the one's complement of the divident. One's |
40 | | * complement can be easily created using XOR and a mask. |
41 | | * |
42 | | * Finally, check for overflow conditions is minimal. There are only two |
43 | | * calculation steps in the whole calendar that suffer from an internal |
44 | | * overflow, and these conditions are checked: errno is set to EDOM and |
45 | | * the results are clamped/saturated in this case. All other functions |
46 | | * do not suffer from internal overflow and simply return the result |
47 | | * truncated to 32 bits. |
48 | | * |
49 | | * This is a sacrifice made for execution speed. Since a 32-bit day |
50 | | * counter covers +/- 5,879,610 years and the clamp limits the effective |
51 | | * range to +/-2.9 million years, this should not pose a problem here. |
52 | | * |
53 | | */ |
54 | | |
55 | | #include <config.h> |
56 | | #include <sys/types.h> |
57 | | |
58 | | #include "ntp_types.h" |
59 | | #include "ntp_calendar.h" |
60 | | #include "ntp_stdlib.h" |
61 | | #include "ntp_fp.h" |
62 | | #include "ntp_unixtime.h" |
63 | | |
64 | | /* For now, let's take the conservative approach: if the target property |
65 | | * macros are not defined, check a few well-known compiler/architecture |
66 | | * settings. Default is to assume that the representation of signed |
67 | | * integers is unknown and shift-arithmetic-right is not available. |
68 | | */ |
69 | | #ifndef TARGET_HAS_2CPL |
70 | | # if defined(__GNUC__) |
71 | | # if defined(__i386__) || defined(__x86_64__) || defined(__arm__) |
72 | | # define TARGET_HAS_2CPL 1 |
73 | | # else |
74 | | # define TARGET_HAS_2CPL 0 |
75 | | # endif |
76 | | # elif defined(_MSC_VER) |
77 | | # if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) |
78 | | # define TARGET_HAS_2CPL 1 |
79 | | # else |
80 | | # define TARGET_HAS_2CPL 0 |
81 | | # endif |
82 | | # else |
83 | | # define TARGET_HAS_2CPL 0 |
84 | | # endif |
85 | | #endif |
86 | | |
87 | | #ifndef TARGET_HAS_SAR |
88 | | # define TARGET_HAS_SAR 0 |
89 | | #endif |
90 | | |
91 | | /* |
92 | | *--------------------------------------------------------------------- |
93 | | * replacing the 'time()' function |
94 | | *--------------------------------------------------------------------- |
95 | | */ |
96 | | |
97 | | static systime_func_ptr systime_func = &time; |
98 | | static inline time_t now(void); |
99 | | |
100 | | |
101 | | systime_func_ptr |
102 | | ntpcal_set_timefunc( |
103 | | systime_func_ptr nfunc |
104 | | ) |
105 | 0 | { |
106 | 0 | systime_func_ptr res; |
107 | |
|
108 | 0 | res = systime_func; |
109 | 0 | if (NULL == nfunc) |
110 | 0 | nfunc = &time; |
111 | 0 | systime_func = nfunc; |
112 | |
|
113 | 0 | return res; |
114 | 0 | } |
115 | | |
116 | | |
117 | | static inline time_t |
118 | | now(void) |
119 | 0 | { |
120 | 0 | return (*systime_func)(NULL); |
121 | 0 | } |
122 | | |
123 | | /* |
124 | | *--------------------------------------------------------------------- |
125 | | * Get sign extension mask and unsigned 2cpl rep for a signed integer |
126 | | *--------------------------------------------------------------------- |
127 | | */ |
128 | | |
129 | | static inline uint32_t |
130 | | int32_sflag( |
131 | | const int32_t v) |
132 | 0 | { |
133 | | # if TARGET_HAS_2CPL && TARGET_HAS_SAR && SIZEOF_INT >= 4 |
134 | | |
135 | | /* Let's assume that shift is the fastest way to get the sign |
136 | | * extension of of a signed integer. This might not always be |
137 | | * true, though -- On 8bit CPUs or machines without barrel |
138 | | * shifter this will kill the performance. So we make sure |
139 | | * we do this only if 'int' has at least 4 bytes. |
140 | | */ |
141 | | return (uint32_t)(v >> 31); |
142 | | |
143 | | # else |
144 | | |
145 | | /* This should be a rather generic approach for getting a sign |
146 | | * extension mask... |
147 | | */ |
148 | 0 | return UINT32_C(0) - (uint32_t)(v < 0); |
149 | | |
150 | 0 | # endif |
151 | 0 | } |
152 | | |
153 | | static inline uint32_t |
154 | | int32_to_uint32_2cpl( |
155 | | const int32_t v) |
156 | 0 | { |
157 | 0 | uint32_t vu; |
158 | | |
159 | 0 | # if TARGET_HAS_2CPL |
160 | | |
161 | | /* Just copy through the 32 bits from the signed value if we're |
162 | | * on a two's complement target. |
163 | | */ |
164 | 0 | vu = (uint32_t)v; |
165 | | |
166 | | # else |
167 | | |
168 | | /* Convert from signed int to unsigned int two's complement. Do |
169 | | * not make any assumptions about the representation of signed |
170 | | * integers, but make sure signed integer overflow cannot happen |
171 | | * here. A compiler on a two's complement target *might* find |
172 | | * out that this is just a complicated cast (as above), but your |
173 | | * mileage might vary. |
174 | | */ |
175 | | if (v < 0) |
176 | | vu = ~(uint32_t)(-(v + 1)); |
177 | | else |
178 | | vu = (uint32_t)v; |
179 | | |
180 | | # endif |
181 | | |
182 | 0 | return vu; |
183 | 0 | } |
184 | | |
185 | | static inline int32_t |
186 | | uint32_2cpl_to_int32( |
187 | | const uint32_t vu) |
188 | 0 | { |
189 | 0 | int32_t v; |
190 | | |
191 | 0 | # if TARGET_HAS_2CPL |
192 | | |
193 | | /* Just copy through the 32 bits from the unsigned value if |
194 | | * we're on a two's complement target. |
195 | | */ |
196 | 0 | v = (int32_t)vu; |
197 | |
|
198 | | # else |
199 | | |
200 | | /* Convert to signed integer, making sure signed integer |
201 | | * overflow cannot happen. Again, the optimiser might or might |
202 | | * not find out that this is just a copy of 32 bits on a target |
203 | | * with two's complement representation for signed integers. |
204 | | */ |
205 | | if (vu > INT32_MAX) |
206 | | v = -(int32_t)(~vu) - 1; |
207 | | else |
208 | | v = (int32_t)vu; |
209 | | |
210 | | # endif |
211 | | |
212 | 0 | return v; |
213 | 0 | } |
214 | | |
215 | | /* Some of the calculations need to multiply the input by 4 before doing |
216 | | * a division. This can cause overflow and strange results. Therefore we |
217 | | * clamp / saturate the input operand. And since we do the calculations |
218 | | * in unsigned int with an extra sign flag/mask, we only loose one bit |
219 | | * of the input value range. |
220 | | */ |
221 | | static inline uint32_t |
222 | | uint32_saturate( |
223 | | uint32_t vu, |
224 | | uint32_t mu) |
225 | 0 | { |
226 | 0 | static const uint32_t limit = UINT32_MAX/4u; |
227 | 0 | if ((mu ^ vu) > limit) { |
228 | 0 | vu = mu ^ limit; |
229 | 0 | errno = EDOM; |
230 | 0 | } |
231 | 0 | return vu; |
232 | 0 | } |
233 | | |
234 | | /* |
235 | | *--------------------------------------------------------------------- |
236 | | * Convert between 'time_t' and 'vint64' |
237 | | *--------------------------------------------------------------------- |
238 | | */ |
239 | | vint64 |
240 | | time_to_vint64( |
241 | | const time_t * ptt |
242 | | ) |
243 | 0 | { |
244 | 0 | vint64 res; |
245 | 0 | time_t tt; |
246 | |
|
247 | 0 | tt = *ptt; |
248 | |
|
249 | | # if SIZEOF_TIME_T <= 4 |
250 | | |
251 | | res.D_s.hi = 0; |
252 | | if (tt < 0) { |
253 | | res.D_s.lo = (uint32_t)-tt; |
254 | | M_NEG(res.D_s.hi, res.D_s.lo); |
255 | | } else { |
256 | | res.D_s.lo = (uint32_t)tt; |
257 | | } |
258 | | |
259 | | # elif defined(HAVE_INT64) |
260 | |
|
261 | 0 | res.q_s = tt; |
262 | |
|
263 | | # else |
264 | | /* |
265 | | * shifting negative signed quantities is compiler-dependent, so |
266 | | * we better avoid it and do it all manually. And shifting more |
267 | | * than the width of a quantity is undefined. Also a don't do! |
268 | | */ |
269 | | if (tt < 0) { |
270 | | tt = -tt; |
271 | | res.D_s.lo = (uint32_t)tt; |
272 | | res.D_s.hi = (uint32_t)(tt >> 32); |
273 | | M_NEG(res.D_s.hi, res.D_s.lo); |
274 | | } else { |
275 | | res.D_s.lo = (uint32_t)tt; |
276 | | res.D_s.hi = (uint32_t)(tt >> 32); |
277 | | } |
278 | | |
279 | | # endif |
280 | |
|
281 | 0 | return res; |
282 | 0 | } |
283 | | |
284 | | |
285 | | time_t |
286 | | vint64_to_time( |
287 | | const vint64 *tv |
288 | | ) |
289 | 0 | { |
290 | 0 | time_t res; |
291 | |
|
292 | | # if SIZEOF_TIME_T <= 4 |
293 | | |
294 | | res = (time_t)tv->D_s.lo; |
295 | | |
296 | | # elif defined(HAVE_INT64) |
297 | |
|
298 | 0 | res = (time_t)tv->q_s; |
299 | |
|
300 | | # else |
301 | | |
302 | | res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo; |
303 | | |
304 | | # endif |
305 | |
|
306 | 0 | return res; |
307 | 0 | } |
308 | | |
309 | | /* |
310 | | *--------------------------------------------------------------------- |
311 | | * Get the build date & time |
312 | | *--------------------------------------------------------------------- |
313 | | */ |
314 | | int |
315 | | ntpcal_get_build_date( |
316 | | struct calendar * jd |
317 | | ) |
318 | 0 | { |
319 | | /* The C standard tells us the format of '__DATE__': |
320 | | * |
321 | | * __DATE__ The date of translation of the preprocessing |
322 | | * translation unit: a character string literal of the form "Mmm |
323 | | * dd yyyy", where the names of the months are the same as those |
324 | | * generated by the asctime function, and the first character of |
325 | | * dd is a space character if the value is less than 10. If the |
326 | | * date of translation is not available, an |
327 | | * implementation-defined valid date shall be supplied. |
328 | | * |
329 | | * __TIME__ The time of translation of the preprocessing |
330 | | * translation unit: a character string literal of the form |
331 | | * "hh:mm:ss" as in the time generated by the asctime |
332 | | * function. If the time of translation is not available, an |
333 | | * implementation-defined valid time shall be supplied. |
334 | | * |
335 | | * Note that MSVC declares DATE and TIME to be in the local time |
336 | | * zone, while neither the C standard nor the GCC docs make any |
337 | | * statement about this. As a result, we may be +/-12hrs off |
338 | | * UTC. But for practical purposes, this should not be a |
339 | | * problem. |
340 | | * |
341 | | */ |
342 | | # ifdef MKREPRO_DATE |
343 | | static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE; |
344 | | # else |
345 | 0 | static const char build[] = __TIME__ "/" __DATE__; |
346 | 0 | # endif |
347 | 0 | static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; |
348 | |
|
349 | 0 | char monstr[4]; |
350 | 0 | const char * cp; |
351 | 0 | unsigned short hour, minute, second, day, year; |
352 | | /* Note: The above quantities are used for sscanf 'hu' format, |
353 | | * so using 'uint16_t' is contra-indicated! |
354 | | */ |
355 | |
|
356 | 0 | # ifdef DEBUG |
357 | 0 | static int ignore = 0; |
358 | 0 | # endif |
359 | |
|
360 | 0 | ZERO(*jd); |
361 | 0 | jd->year = 1970; |
362 | 0 | jd->month = 1; |
363 | 0 | jd->monthday = 1; |
364 | |
|
365 | 0 | # ifdef DEBUG |
366 | | /* check environment if build date should be ignored */ |
367 | 0 | if (0 == ignore) { |
368 | 0 | const char * envstr; |
369 | 0 | envstr = getenv("NTPD_IGNORE_BUILD_DATE"); |
370 | 0 | ignore = 1 + (envstr && (!*envstr || !strcasecmp(envstr, "yes"))); |
371 | 0 | } |
372 | 0 | if (ignore > 1) |
373 | 0 | return FALSE; |
374 | 0 | # endif |
375 | | |
376 | 0 | if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu", |
377 | 0 | &hour, &minute, &second, monstr, &day, &year)) { |
378 | 0 | cp = strstr(mlist, monstr); |
379 | 0 | if (NULL != cp) { |
380 | 0 | jd->year = year; |
381 | 0 | jd->month = (uint8_t)((cp - mlist) / 3 + 1); |
382 | 0 | jd->monthday = (uint8_t)day; |
383 | 0 | jd->hour = (uint8_t)hour; |
384 | 0 | jd->minute = (uint8_t)minute; |
385 | 0 | jd->second = (uint8_t)second; |
386 | |
|
387 | 0 | return TRUE; |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | 0 | return FALSE; |
392 | 0 | } |
393 | | |
394 | | |
395 | | /* |
396 | | *--------------------------------------------------------------------- |
397 | | * basic calendar stuff |
398 | | *--------------------------------------------------------------------- |
399 | | */ |
400 | | |
401 | | /* month table for a year starting with March,1st */ |
402 | | static const uint16_t shift_month_table[13] = { |
403 | | 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366 |
404 | | }; |
405 | | |
406 | | /* month tables for years starting with January,1st; regular & leap */ |
407 | | static const uint16_t real_month_table[2][13] = { |
408 | | /* -*- table for regular years -*- */ |
409 | | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, |
410 | | /* -*- table for leap years -*- */ |
411 | | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } |
412 | | }; |
413 | | |
414 | | /* |
415 | | * Some notes on the terminology: |
416 | | * |
417 | | * We use the proleptic Gregorian calendar, which is the Gregorian |
418 | | * calendar extended in both directions ad infinitum. This totally |
419 | | * disregards the fact that this calendar was invented in 1582, and |
420 | | * was adopted at various dates over the world; sometimes even after |
421 | | * the start of the NTP epoch. |
422 | | * |
423 | | * Normally date parts are given as current cycles, while time parts |
424 | | * are given as elapsed cycles: |
425 | | * |
426 | | * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month, |
427 | | * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed. |
428 | | * |
429 | | * The basic calculations for this calendar implementation deal with |
430 | | * ELAPSED date units, which is the number of full years, full months |
431 | | * and full days before a date: 1970-01-01 would be (1969, 0, 0) in |
432 | | * that notation. |
433 | | * |
434 | | * To ease the numeric computations, month and day values outside the |
435 | | * normal range are acceptable: 2001-03-00 will be treated as the day |
436 | | * before 2001-03-01, 2000-13-32 will give the same result as |
437 | | * 2001-02-01 and so on. |
438 | | * |
439 | | * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die' |
440 | | * (day number). This is the number of days elapsed since 0000-12-31 |
441 | | * in the proleptic Gregorian calendar. The begin of the Christian Era |
442 | | * (0001-01-01) is RD(1). |
443 | | */ |
444 | | |
445 | | /* |
446 | | * ==================================================================== |
447 | | * |
448 | | * General algorithmic stuff |
449 | | * |
450 | | * ==================================================================== |
451 | | */ |
452 | | |
453 | | /* |
454 | | *--------------------------------------------------------------------- |
455 | | * Do a periodic extension of 'value' around 'pivot' with a period of |
456 | | * 'cycle'. |
457 | | * |
458 | | * The result 'res' is a number that holds to the following properties: |
459 | | * |
460 | | * 1) res MOD cycle == value MOD cycle |
461 | | * 2) pivot <= res < pivot + cycle |
462 | | * (replace </<= with >/>= for negative cycles) |
463 | | * |
464 | | * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which |
465 | | * is not the same as the '%' operator in C: C requires division to be |
466 | | * a truncated division, where remainder and dividend have the same |
467 | | * sign if the remainder is not zero, whereas floor division requires |
468 | | * divider and modulus to have the same sign for a non-zero modulus. |
469 | | * |
470 | | * This function has some useful applications: |
471 | | * |
472 | | * + let Y be a calendar year and V a truncated 2-digit year: then |
473 | | * periodic_extend(Y-50, V, 100) |
474 | | * is the closest expansion of the truncated year with respect to |
475 | | * the full year, that is a 4-digit year with a difference of less |
476 | | * than 50 years to the year Y. ("century unfolding") |
477 | | * |
478 | | * + let T be a UN*X time stamp and V be seconds-of-day: then |
479 | | * perodic_extend(T-43200, V, 86400) |
480 | | * is a time stamp that has the same seconds-of-day as the input |
481 | | * value, with an absolute difference to T of <= 12hrs. ("day |
482 | | * unfolding") |
483 | | * |
484 | | * + Wherever you have a truncated periodic value and a non-truncated |
485 | | * base value and you want to match them somehow... |
486 | | * |
487 | | * Basically, the function delivers 'pivot + (value - pivot) % cycle', |
488 | | * but the implementation takes some pains to avoid internal signed |
489 | | * integer overflows in the '(value - pivot) % cycle' part and adheres |
490 | | * to the floor division convention. |
491 | | * |
492 | | * If 64bit scalars where available on all intended platforms, writing a |
493 | | * version that uses 64 bit ops would be easy; writing a general |
494 | | * division routine for 64bit ops on a platform that can only do |
495 | | * 32/16bit divisions and is still performant is a bit more |
496 | | * difficult. Since most usecases can be coded in a way that does only |
497 | | * require the 32-bit version a 64bit version is NOT provided here. |
498 | | *--------------------------------------------------------------------- |
499 | | */ |
500 | | int32_t |
501 | | ntpcal_periodic_extend( |
502 | | int32_t pivot, |
503 | | int32_t value, |
504 | | int32_t cycle |
505 | | ) |
506 | 0 | { |
507 | 0 | uint32_t diff; |
508 | 0 | char cpl = 0; /* modulo complement flag */ |
509 | 0 | char neg = 0; /* sign change flag */ |
510 | | |
511 | | /* make the cycle positive and adjust the flags */ |
512 | 0 | if (cycle < 0) { |
513 | 0 | cycle = - cycle; |
514 | 0 | neg ^= 1; |
515 | 0 | cpl ^= 1; |
516 | 0 | } |
517 | | /* guard against div by zero or one */ |
518 | 0 | if (cycle > 1) { |
519 | | /* |
520 | | * Get absolute difference as unsigned quantity and |
521 | | * the complement flag. This is done by always |
522 | | * subtracting the smaller value from the bigger |
523 | | * one. |
524 | | */ |
525 | 0 | if (value >= pivot) { |
526 | 0 | diff = int32_to_uint32_2cpl(value) |
527 | 0 | - int32_to_uint32_2cpl(pivot); |
528 | 0 | } else { |
529 | 0 | diff = int32_to_uint32_2cpl(pivot) |
530 | 0 | - int32_to_uint32_2cpl(value); |
531 | 0 | cpl ^= 1; |
532 | 0 | } |
533 | 0 | diff %= (uint32_t)cycle; |
534 | 0 | if (diff) { |
535 | 0 | if (cpl) |
536 | 0 | diff = (uint32_t)cycle - diff; |
537 | 0 | if (neg) |
538 | 0 | diff = ~diff + 1; |
539 | 0 | pivot += uint32_2cpl_to_int32(diff); |
540 | 0 | } |
541 | 0 | } |
542 | 0 | return pivot; |
543 | 0 | } |
544 | | |
545 | | /*--------------------------------------------------------------------- |
546 | | * Note to the casual reader |
547 | | * |
548 | | * In the next two functions you will find (or would have found...) |
549 | | * the expression |
550 | | * |
551 | | * res.Q_s -= 0x80000000; |
552 | | * |
553 | | * There was some ruckus about a possible programming error due to |
554 | | * integer overflow and sign propagation. |
555 | | * |
556 | | * This assumption is based on a lack of understanding of the C |
557 | | * standard. (Though this is admittedly not one of the most 'natural' |
558 | | * aspects of the 'C' language and easily to get wrong.) |
559 | | * |
560 | | * see |
561 | | * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf |
562 | | * "ISO/IEC 9899:201x Committee Draft — April 12, 2011" |
563 | | * 6.4.4.1 Integer constants, clause 5 |
564 | | * |
565 | | * why there is no sign extension/overflow problem here. |
566 | | * |
567 | | * But to ease the minds of the doubtful, I added back the 'u' qualifiers |
568 | | * that somehow got lost over the last years. |
569 | | */ |
570 | | |
571 | | |
572 | | /* |
573 | | *--------------------------------------------------------------------- |
574 | | * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X |
575 | | * scale with proper epoch unfolding around a given pivot or the current |
576 | | * system time. This function happily accepts negative pivot values as |
577 | | * timestamps befor 1970-01-01, so be aware of possible trouble on |
578 | | * platforms with 32bit 'time_t'! |
579 | | * |
580 | | * This is also a periodic extension, but since the cycle is 2^32 and |
581 | | * the shift is 2^31, we can do some *very* fast math without explicit |
582 | | * divisions. |
583 | | *--------------------------------------------------------------------- |
584 | | */ |
585 | | vint64 |
586 | | ntpcal_ntp_to_time( |
587 | | uint32_t ntp, |
588 | | const time_t * pivot |
589 | | ) |
590 | 0 | { |
591 | 0 | vint64 res; |
592 | |
|
593 | 0 | # if defined(HAVE_INT64) |
594 | |
|
595 | 0 | res.q_s = (pivot != NULL) |
596 | 0 | ? *pivot |
597 | 0 | : now(); |
598 | 0 | res.Q_s -= 0x80000000u; /* unshift of half range */ |
599 | 0 | ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ |
600 | 0 | ntp -= res.D_s.lo; /* cycle difference */ |
601 | 0 | res.Q_s += (uint64_t)ntp; /* get expanded time */ |
602 | |
|
603 | | # else /* no 64bit scalars */ |
604 | | |
605 | | time_t tmp; |
606 | | |
607 | | tmp = (pivot != NULL) |
608 | | ? *pivot |
609 | | : now(); |
610 | | res = time_to_vint64(&tmp); |
611 | | M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); |
612 | | ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ |
613 | | ntp -= res.D_s.lo; /* cycle difference */ |
614 | | M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); |
615 | | |
616 | | # endif /* no 64bit scalars */ |
617 | |
|
618 | 0 | return res; |
619 | 0 | } |
620 | | |
621 | | /* |
622 | | *--------------------------------------------------------------------- |
623 | | * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP |
624 | | * scale with proper epoch unfolding around a given pivot or the current |
625 | | * system time. |
626 | | * |
627 | | * Note: The pivot must be given in the UN*X time domain! |
628 | | * |
629 | | * This is also a periodic extension, but since the cycle is 2^32 and |
630 | | * the shift is 2^31, we can do some *very* fast math without explicit |
631 | | * divisions. |
632 | | *--------------------------------------------------------------------- |
633 | | */ |
634 | | vint64 |
635 | | ntpcal_ntp_to_ntp( |
636 | | uint32_t ntp, |
637 | | const time_t *pivot |
638 | | ) |
639 | 0 | { |
640 | 0 | vint64 res; |
641 | |
|
642 | 0 | # if defined(HAVE_INT64) |
643 | |
|
644 | 0 | res.q_s = (pivot) |
645 | 0 | ? *pivot |
646 | 0 | : now(); |
647 | 0 | res.Q_s -= 0x80000000u; /* unshift of half range */ |
648 | 0 | res.Q_s += (uint32_t)JAN_1970; /* warp into NTP domain */ |
649 | 0 | ntp -= res.D_s.lo; /* cycle difference */ |
650 | 0 | res.Q_s += (uint64_t)ntp; /* get expanded time */ |
651 | |
|
652 | | # else /* no 64bit scalars */ |
653 | | |
654 | | time_t tmp; |
655 | | |
656 | | tmp = (pivot) |
657 | | ? *pivot |
658 | | : now(); |
659 | | res = time_to_vint64(&tmp); |
660 | | M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); |
661 | | M_ADD(res.D_s.hi, res.D_s.lo, 0, (uint32_t)JAN_1970);/*into NTP */ |
662 | | ntp -= res.D_s.lo; /* cycle difference */ |
663 | | M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); |
664 | | |
665 | | # endif /* no 64bit scalars */ |
666 | |
|
667 | 0 | return res; |
668 | 0 | } |
669 | | |
670 | | |
671 | | /* |
672 | | * ==================================================================== |
673 | | * |
674 | | * Splitting values to composite entities |
675 | | * |
676 | | * ==================================================================== |
677 | | */ |
678 | | |
679 | | /* |
680 | | *--------------------------------------------------------------------- |
681 | | * Split a 64bit seconds value into elapsed days in 'res.hi' and |
682 | | * elapsed seconds since midnight in 'res.lo' using explicit floor |
683 | | * division. This function happily accepts negative time values as |
684 | | * timestamps before the respective epoch start. |
685 | | *--------------------------------------------------------------------- |
686 | | */ |
687 | | ntpcal_split |
688 | | ntpcal_daysplit( |
689 | | const vint64 *ts |
690 | | ) |
691 | 0 | { |
692 | 0 | ntpcal_split res; |
693 | 0 | uint32_t Q; |
694 | |
|
695 | 0 | # if defined(HAVE_INT64) |
696 | | |
697 | | /* Manual floor division by SECSPERDAY. This uses the one's |
698 | | * complement trick, too, but without an extra flag value: The |
699 | | * flag would be 64bit, and that's a bit of overkill on a 32bit |
700 | | * target that has to use a register pair for a 64bit number. |
701 | | */ |
702 | 0 | if (ts->q_s < 0) |
703 | 0 | Q = ~(uint32_t)(~ts->Q_s / SECSPERDAY); |
704 | 0 | else |
705 | 0 | Q = (uint32_t)(ts->Q_s / SECSPERDAY); |
706 | |
|
707 | | # else |
708 | | |
709 | | uint32_t ah, al, sflag, A; |
710 | | |
711 | | /* get operand into ah/al (either ts or ts' one's complement, |
712 | | * for later floor division) |
713 | | */ |
714 | | sflag = int32_sflag(ts->d_s.hi); |
715 | | ah = sflag ^ ts->D_s.hi; |
716 | | al = sflag ^ ts->D_s.lo; |
717 | | |
718 | | /* Since 86400 == 128*675 we can drop the least 7 bits and |
719 | | * divide by 675 instead of 86400. Then the maximum remainder |
720 | | * after each devision step is 674, and we need 10 bits for |
721 | | * that. So in the next step we can shift in 22 bits from the |
722 | | * numerator. |
723 | | * |
724 | | * Therefore we load the accu with the top 13 bits (51..63) in |
725 | | * the first shot. We don't have to remember the quotient -- it |
726 | | * would be shifted out anyway. |
727 | | */ |
728 | | A = ah >> 19; |
729 | | if (A >= 675) |
730 | | A = (A % 675u); |
731 | | |
732 | | /* Now assemble the remainder with bits 29..50 from the |
733 | | * numerator and divide. This creates the upper ten bits of the |
734 | | * quotient. (Well, the top 22 bits of a 44bit result. But that |
735 | | * will be truncated to 32 bits anyway.) |
736 | | */ |
737 | | A = (A << 19) | (ah & 0x0007FFFFu); |
738 | | A = (A << 3) | (al >> 29); |
739 | | Q = A / 675u; |
740 | | A = A % 675u; |
741 | | |
742 | | /* Now assemble the remainder with bits 7..28 from the numerator |
743 | | * and do a final division step. |
744 | | */ |
745 | | A = (A << 22) | ((al >> 7) & 0x003FFFFFu); |
746 | | Q = (Q << 22) | (A / 675u); |
747 | | |
748 | | /* The last 7 bits get simply dropped, as they have no affect on |
749 | | * the quotient when dividing by 86400. |
750 | | */ |
751 | | |
752 | | /* apply sign correction and calculate the true floor |
753 | | * remainder. |
754 | | */ |
755 | | Q ^= sflag; |
756 | | |
757 | | # endif |
758 | | |
759 | 0 | res.hi = uint32_2cpl_to_int32(Q); |
760 | 0 | res.lo = ts->D_s.lo - Q * SECSPERDAY; |
761 | |
|
762 | 0 | return res; |
763 | 0 | } |
764 | | |
765 | | /* |
766 | | *--------------------------------------------------------------------- |
767 | | * Split a 32bit seconds value into h/m/s and excessive days. This |
768 | | * function happily accepts negative time values as timestamps before |
769 | | * midnight. |
770 | | *--------------------------------------------------------------------- |
771 | | */ |
772 | | static int32_t |
773 | | priv_timesplit( |
774 | | int32_t split[3], |
775 | | int32_t ts |
776 | | ) |
777 | 0 | { |
778 | | /* Do 3 chained floor divisions by positive constants, using the |
779 | | * one's complement trick and factoring out the intermediate XOR |
780 | | * ops to reduce the number of operations. |
781 | | */ |
782 | 0 | uint32_t us, um, uh, ud, sflag; |
783 | |
|
784 | 0 | sflag = int32_sflag(ts); |
785 | 0 | us = int32_to_uint32_2cpl(ts); |
786 | |
|
787 | 0 | um = (sflag ^ us) / SECSPERMIN; |
788 | 0 | uh = um / MINSPERHR; |
789 | 0 | ud = uh / HRSPERDAY; |
790 | |
|
791 | 0 | um ^= sflag; |
792 | 0 | uh ^= sflag; |
793 | 0 | ud ^= sflag; |
794 | |
|
795 | 0 | split[0] = (int32_t)(uh - ud * HRSPERDAY ); |
796 | 0 | split[1] = (int32_t)(um - uh * MINSPERHR ); |
797 | 0 | split[2] = (int32_t)(us - um * SECSPERMIN); |
798 | | |
799 | 0 | return uint32_2cpl_to_int32(ud); |
800 | 0 | } |
801 | | |
802 | | /* |
803 | | *--------------------------------------------------------------------- |
804 | | * Given the number of elapsed days in the calendar era, split this |
805 | | * number into the number of elapsed years in 'res.hi' and the number |
806 | | * of elapsed days of that year in 'res.lo'. |
807 | | * |
808 | | * if 'isleapyear' is not NULL, it will receive an integer that is 0 for |
809 | | * regular years and a non-zero value for leap years. |
810 | | *--------------------------------------------------------------------- |
811 | | */ |
812 | | ntpcal_split |
813 | | ntpcal_split_eradays( |
814 | | int32_t days, |
815 | | int *isleapyear |
816 | | ) |
817 | 0 | { |
818 | | /* Use the fast cyclesplit algorithm here, to calculate the |
819 | | * centuries and years in a century with one division each. This |
820 | | * reduces the number of division operations to two, but is |
821 | | * susceptible to internal range overflow. We make sure the |
822 | | * input operands are in the safe range; this still gives us |
823 | | * approx +/-2.9 million years. |
824 | | */ |
825 | 0 | ntpcal_split res; |
826 | 0 | int32_t n100, n001; /* calendar year cycles */ |
827 | 0 | uint32_t uday, Q, sflag; |
828 | | |
829 | | /* split off centuries first */ |
830 | 0 | sflag = int32_sflag(days); |
831 | 0 | uday = uint32_saturate(int32_to_uint32_2cpl(days), sflag); |
832 | 0 | uday = (4u * uday) | 3u; |
833 | 0 | Q = sflag ^ ((sflag ^ uday) / GREGORIAN_CYCLE_DAYS); |
834 | 0 | uday = uday - Q * GREGORIAN_CYCLE_DAYS; |
835 | 0 | n100 = uint32_2cpl_to_int32(Q); |
836 | | |
837 | | /* Split off years in century -- days >= 0 here, and we're far |
838 | | * away from integer overflow trouble now. */ |
839 | 0 | uday |= 3; |
840 | 0 | n001 = uday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; |
841 | 0 | uday = uday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; |
842 | | |
843 | | /* Assemble the year and day in year */ |
844 | 0 | res.hi = n100 * 100 + n001; |
845 | 0 | res.lo = uday / 4u; |
846 | | |
847 | | /* Eventually set the leap year flag. Note: 0 <= n001 <= 99 and |
848 | | * Q is still the two's complement representation of the |
849 | | * centuries: The modulo 4 ops can be done with masking here. |
850 | | * We also shift the year and the century by one, so the tests |
851 | | * can be done against zero instead of 3. |
852 | | */ |
853 | 0 | if (isleapyear) |
854 | 0 | *isleapyear = !((n001+1) & 3) |
855 | 0 | && ((n001 != 99) || !((Q+1) & 3)); |
856 | | |
857 | 0 | return res; |
858 | 0 | } |
859 | | |
860 | | /* |
861 | | *--------------------------------------------------------------------- |
862 | | * Given a number of elapsed days in a year and a leap year indicator, |
863 | | * split the number of elapsed days into the number of elapsed months in |
864 | | * 'res.hi' and the number of elapsed days of that month in 'res.lo'. |
865 | | * |
866 | | * This function will fail and return {-1,-1} if the number of elapsed |
867 | | * days is not in the valid range! |
868 | | *--------------------------------------------------------------------- |
869 | | */ |
870 | | ntpcal_split |
871 | | ntpcal_split_yeardays( |
872 | | int32_t eyd, |
873 | | int isleapyear |
874 | | ) |
875 | 0 | { |
876 | 0 | ntpcal_split res; |
877 | 0 | const uint16_t *lt; /* month length table */ |
878 | | |
879 | | /* check leap year flag and select proper table */ |
880 | 0 | lt = real_month_table[(isleapyear != 0)]; |
881 | 0 | if (0 <= eyd && eyd < lt[12]) { |
882 | | /* get zero-based month by approximation & correction step */ |
883 | 0 | res.hi = eyd >> 5; /* approx month; might be 1 too low */ |
884 | 0 | if (lt[res.hi + 1] <= eyd) /* fixup approximative month value */ |
885 | 0 | res.hi += 1; |
886 | 0 | res.lo = eyd - lt[res.hi]; |
887 | 0 | } else { |
888 | 0 | res.lo = res.hi = -1; |
889 | 0 | } |
890 | |
|
891 | 0 | return res; |
892 | 0 | } |
893 | | |
894 | | /* |
895 | | *--------------------------------------------------------------------- |
896 | | * Convert a RD into the date part of a 'struct calendar'. |
897 | | *--------------------------------------------------------------------- |
898 | | */ |
899 | | int |
900 | | ntpcal_rd_to_date( |
901 | | struct calendar *jd, |
902 | | int32_t rd |
903 | | ) |
904 | 0 | { |
905 | 0 | ntpcal_split split; |
906 | 0 | int leapy; |
907 | 0 | u_int ymask; |
908 | | |
909 | | /* Get day-of-week first. Since rd is signed, the remainder can |
910 | | * be in the range [-6..+6], but the assignment to an unsigned |
911 | | * variable maps the negative values to positive values >=7. |
912 | | * This makes the sign correction look strange, but adding 7 |
913 | | * causes the needed wrap-around into the desired value range of |
914 | | * zero to six, both inclusive. |
915 | | */ |
916 | 0 | jd->weekday = rd % DAYSPERWEEK; |
917 | 0 | if (jd->weekday >= DAYSPERWEEK) /* weekday is unsigned! */ |
918 | 0 | jd->weekday += DAYSPERWEEK; |
919 | |
|
920 | 0 | split = ntpcal_split_eradays(rd - 1, &leapy); |
921 | | /* Get year and day-of-year, with overflow check. If any of the |
922 | | * upper 16 bits is set after shifting to unity-based years, we |
923 | | * will have an overflow when converting to an unsigned 16bit |
924 | | * year. Shifting to the right is OK here, since it does not |
925 | | * matter if the shift is logic or arithmetic. |
926 | | */ |
927 | 0 | split.hi += 1; |
928 | 0 | ymask = 0u - ((split.hi >> 16) == 0); |
929 | 0 | jd->year = (uint16_t)(split.hi & ymask); |
930 | 0 | jd->yearday = (uint16_t)split.lo + 1; |
931 | | |
932 | | /* convert to month and mday */ |
933 | 0 | split = ntpcal_split_yeardays(split.lo, leapy); |
934 | 0 | jd->month = (uint8_t)split.hi + 1; |
935 | 0 | jd->monthday = (uint8_t)split.lo + 1; |
936 | |
|
937 | 0 | return ymask ? leapy : -1; |
938 | 0 | } |
939 | | |
940 | | /* |
941 | | *--------------------------------------------------------------------- |
942 | | * Convert a RD into the date part of a 'struct tm'. |
943 | | *--------------------------------------------------------------------- |
944 | | */ |
945 | | int |
946 | | ntpcal_rd_to_tm( |
947 | | struct tm *utm, |
948 | | int32_t rd |
949 | | ) |
950 | 0 | { |
951 | 0 | ntpcal_split split; |
952 | 0 | int leapy; |
953 | | |
954 | | /* get day-of-week first */ |
955 | 0 | utm->tm_wday = rd % DAYSPERWEEK; |
956 | 0 | if (utm->tm_wday < 0) |
957 | 0 | utm->tm_wday += DAYSPERWEEK; |
958 | | |
959 | | /* get year and day-of-year */ |
960 | 0 | split = ntpcal_split_eradays(rd - 1, &leapy); |
961 | 0 | utm->tm_year = split.hi - 1899; |
962 | 0 | utm->tm_yday = split.lo; /* 0-based */ |
963 | | |
964 | | /* convert to month and mday */ |
965 | 0 | split = ntpcal_split_yeardays(split.lo, leapy); |
966 | 0 | utm->tm_mon = split.hi; /* 0-based */ |
967 | 0 | utm->tm_mday = split.lo + 1; /* 1-based */ |
968 | |
|
969 | 0 | return leapy; |
970 | 0 | } |
971 | | |
972 | | /* |
973 | | *--------------------------------------------------------------------- |
974 | | * Take a value of seconds since midnight and split it into hhmmss in a |
975 | | * 'struct calendar'. |
976 | | *--------------------------------------------------------------------- |
977 | | */ |
978 | | int32_t |
979 | | ntpcal_daysec_to_date( |
980 | | struct calendar *jd, |
981 | | int32_t sec |
982 | | ) |
983 | 0 | { |
984 | 0 | int32_t days; |
985 | 0 | int ts[3]; |
986 | |
|
987 | 0 | days = priv_timesplit(ts, sec); |
988 | 0 | jd->hour = (uint8_t)ts[0]; |
989 | 0 | jd->minute = (uint8_t)ts[1]; |
990 | 0 | jd->second = (uint8_t)ts[2]; |
991 | |
|
992 | 0 | return days; |
993 | 0 | } |
994 | | |
995 | | /* |
996 | | *--------------------------------------------------------------------- |
997 | | * Take a value of seconds since midnight and split it into hhmmss in a |
998 | | * 'struct tm'. |
999 | | *--------------------------------------------------------------------- |
1000 | | */ |
1001 | | int32_t |
1002 | | ntpcal_daysec_to_tm( |
1003 | | struct tm *utm, |
1004 | | int32_t sec |
1005 | | ) |
1006 | 0 | { |
1007 | 0 | int32_t days; |
1008 | 0 | int32_t ts[3]; |
1009 | |
|
1010 | 0 | days = priv_timesplit(ts, sec); |
1011 | 0 | utm->tm_hour = ts[0]; |
1012 | 0 | utm->tm_min = ts[1]; |
1013 | 0 | utm->tm_sec = ts[2]; |
1014 | |
|
1015 | 0 | return days; |
1016 | 0 | } |
1017 | | |
1018 | | /* |
1019 | | *--------------------------------------------------------------------- |
1020 | | * take a split representation for day/second-of-day and day offset |
1021 | | * and convert it to a 'struct calendar'. The seconds will be normalised |
1022 | | * into the range of a day, and the day will be adjusted accordingly. |
1023 | | * |
1024 | | * returns >0 if the result is in a leap year, 0 if in a regular |
1025 | | * year and <0 if the result did not fit into the calendar struct. |
1026 | | *--------------------------------------------------------------------- |
1027 | | */ |
1028 | | int |
1029 | | ntpcal_daysplit_to_date( |
1030 | | struct calendar *jd, |
1031 | | const ntpcal_split *ds, |
1032 | | int32_t dof |
1033 | | ) |
1034 | 0 | { |
1035 | 0 | dof += ntpcal_daysec_to_date(jd, ds->lo); |
1036 | 0 | return ntpcal_rd_to_date(jd, ds->hi + dof); |
1037 | 0 | } |
1038 | | |
1039 | | /* |
1040 | | *--------------------------------------------------------------------- |
1041 | | * take a split representation for day/second-of-day and day offset |
1042 | | * and convert it to a 'struct tm'. The seconds will be normalised |
1043 | | * into the range of a day, and the day will be adjusted accordingly. |
1044 | | * |
1045 | | * returns 1 if the result is in a leap year and zero if in a regular |
1046 | | * year. |
1047 | | *--------------------------------------------------------------------- |
1048 | | */ |
1049 | | int |
1050 | | ntpcal_daysplit_to_tm( |
1051 | | struct tm *utm, |
1052 | | const ntpcal_split *ds , |
1053 | | int32_t dof |
1054 | | ) |
1055 | 0 | { |
1056 | 0 | dof += ntpcal_daysec_to_tm(utm, ds->lo); |
1057 | |
|
1058 | 0 | return ntpcal_rd_to_tm(utm, ds->hi + dof); |
1059 | 0 | } |
1060 | | |
1061 | | /* |
1062 | | *--------------------------------------------------------------------- |
1063 | | * Take a UN*X time and convert to a calendar structure. |
1064 | | *--------------------------------------------------------------------- |
1065 | | */ |
1066 | | int |
1067 | | ntpcal_time_to_date( |
1068 | | struct calendar *jd, |
1069 | | const vint64 *ts |
1070 | | ) |
1071 | 0 | { |
1072 | 0 | ntpcal_split ds; |
1073 | |
|
1074 | 0 | ds = ntpcal_daysplit(ts); |
1075 | 0 | ds.hi += ntpcal_daysec_to_date(jd, ds.lo); |
1076 | 0 | ds.hi += DAY_UNIX_STARTS; |
1077 | |
|
1078 | 0 | return ntpcal_rd_to_date(jd, ds.hi); |
1079 | 0 | } |
1080 | | |
1081 | | |
1082 | | /* |
1083 | | * ==================================================================== |
1084 | | * |
1085 | | * merging composite entities |
1086 | | * |
1087 | | * ==================================================================== |
1088 | | */ |
1089 | | |
1090 | | /* |
1091 | | *--------------------------------------------------------------------- |
1092 | | * Merge a number of days and a number of seconds into seconds, |
1093 | | * expressed in 64 bits to avoid overflow. |
1094 | | *--------------------------------------------------------------------- |
1095 | | */ |
1096 | | vint64 |
1097 | | ntpcal_dayjoin( |
1098 | | int32_t days, |
1099 | | int32_t secs |
1100 | | ) |
1101 | 0 | { |
1102 | 0 | vint64 res; |
1103 | |
|
1104 | 0 | # if defined(HAVE_INT64) |
1105 | |
|
1106 | 0 | res.q_s = days; |
1107 | 0 | res.q_s *= SECSPERDAY; |
1108 | 0 | res.q_s += secs; |
1109 | |
|
1110 | | # else |
1111 | | |
1112 | | uint32_t p1, p2; |
1113 | | int isneg; |
1114 | | |
1115 | | /* |
1116 | | * res = days *86400 + secs, using manual 16/32 bit |
1117 | | * multiplications and shifts. |
1118 | | */ |
1119 | | isneg = (days < 0); |
1120 | | if (isneg) |
1121 | | days = -days; |
1122 | | |
1123 | | /* assemble days * 675 */ |
1124 | | res.D_s.lo = (days & 0xFFFF) * 675u; |
1125 | | res.D_s.hi = 0; |
1126 | | p1 = (days >> 16) * 675u; |
1127 | | p2 = p1 >> 16; |
1128 | | p1 = p1 << 16; |
1129 | | M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); |
1130 | | |
1131 | | /* mul by 128, using shift */ |
1132 | | res.D_s.hi = (res.D_s.hi << 7) | (res.D_s.lo >> 25); |
1133 | | res.D_s.lo = (res.D_s.lo << 7); |
1134 | | |
1135 | | /* fix sign */ |
1136 | | if (isneg) |
1137 | | M_NEG(res.D_s.hi, res.D_s.lo); |
1138 | | |
1139 | | /* properly add seconds */ |
1140 | | p2 = 0; |
1141 | | if (secs < 0) { |
1142 | | p1 = (uint32_t)-secs; |
1143 | | M_NEG(p2, p1); |
1144 | | } else { |
1145 | | p1 = (uint32_t)secs; |
1146 | | } |
1147 | | M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); |
1148 | | |
1149 | | # endif |
1150 | |
|
1151 | 0 | return res; |
1152 | 0 | } |
1153 | | |
1154 | | /* |
1155 | | *--------------------------------------------------------------------- |
1156 | | * get leap years since epoch in elapsed years |
1157 | | *--------------------------------------------------------------------- |
1158 | | */ |
1159 | | int32_t |
1160 | | ntpcal_leapyears_in_years( |
1161 | | int32_t years |
1162 | | ) |
1163 | 0 | { |
1164 | | /* We use the in-out-in algorithm here, using the one's |
1165 | | * complement division trick for negative numbers. The chained |
1166 | | * division sequence by 4/25/4 gives the compiler the chance to |
1167 | | * get away with only one true division and doing shifts otherwise. |
1168 | | */ |
1169 | |
|
1170 | 0 | uint32_t sflag, sum, uyear; |
1171 | |
|
1172 | 0 | sflag = int32_sflag(years); |
1173 | 0 | uyear = int32_to_uint32_2cpl(years); |
1174 | 0 | uyear ^= sflag; |
1175 | |
|
1176 | 0 | sum = (uyear /= 4u); /* 4yr rule --> IN */ |
1177 | 0 | sum -= (uyear /= 25u); /* 100yr rule --> OUT */ |
1178 | 0 | sum += (uyear /= 4u); /* 400yr rule --> IN */ |
1179 | | |
1180 | | /* Thanks to the alternation of IN/OUT/IN we can do the sum |
1181 | | * directly and have a single one's complement operation |
1182 | | * here. (Only if the years are negative, of course.) Otherwise |
1183 | | * the one's complement would have to be done when |
1184 | | * adding/subtracting the terms. |
1185 | | */ |
1186 | 0 | return uint32_2cpl_to_int32(sflag ^ sum); |
1187 | 0 | } |
1188 | | |
1189 | | /* |
1190 | | *--------------------------------------------------------------------- |
1191 | | * Convert elapsed years in Era into elapsed days in Era. |
1192 | | *--------------------------------------------------------------------- |
1193 | | */ |
1194 | | int32_t |
1195 | | ntpcal_days_in_years( |
1196 | | int32_t years |
1197 | | ) |
1198 | 0 | { |
1199 | 0 | return years * DAYSPERYEAR + ntpcal_leapyears_in_years(years); |
1200 | 0 | } |
1201 | | |
1202 | | /* |
1203 | | *--------------------------------------------------------------------- |
1204 | | * Convert a number of elapsed month in a year into elapsed days in year. |
1205 | | * |
1206 | | * The month will be normalized, and 'res.hi' will contain the |
1207 | | * excessive years that must be considered when converting the years, |
1208 | | * while 'res.lo' will contain the number of elapsed days since start |
1209 | | * of the year. |
1210 | | * |
1211 | | * This code uses the shifted-month-approach to convert month to days, |
1212 | | * because then there is no need to have explicit leap year |
1213 | | * information. The slight disadvantage is that for most month values |
1214 | | * the result is a negative value, and the year excess is one; the |
1215 | | * conversion is then simply based on the start of the following year. |
1216 | | *--------------------------------------------------------------------- |
1217 | | */ |
1218 | | ntpcal_split |
1219 | | ntpcal_days_in_months( |
1220 | | int32_t m |
1221 | | ) |
1222 | 0 | { |
1223 | 0 | ntpcal_split res; |
1224 | | |
1225 | | /* Add ten months and correct if needed. (It likely is...) */ |
1226 | 0 | res.lo = m + 10; |
1227 | 0 | res.hi = (res.lo >= 12); |
1228 | 0 | if (res.hi) |
1229 | 0 | res.lo -= 12; |
1230 | | |
1231 | | /* if still out of range, normalise by floor division ... */ |
1232 | 0 | if (res.lo < 0 || res.lo >= 12) { |
1233 | 0 | uint32_t mu, Q, sflag; |
1234 | 0 | sflag = int32_sflag(res.lo); |
1235 | 0 | mu = int32_to_uint32_2cpl(res.lo); |
1236 | 0 | Q = sflag ^ ((sflag ^ mu) / 12u); |
1237 | 0 | res.hi += uint32_2cpl_to_int32(Q); |
1238 | 0 | res.lo = mu - Q * 12u; |
1239 | 0 | } |
1240 | | |
1241 | | /* get cummulated days in year with unshift */ |
1242 | 0 | res.lo = shift_month_table[res.lo] - 306; |
1243 | |
|
1244 | 0 | return res; |
1245 | 0 | } |
1246 | | |
1247 | | /* |
1248 | | *--------------------------------------------------------------------- |
1249 | | * Convert ELAPSED years/months/days of gregorian calendar to elapsed |
1250 | | * days in Gregorian epoch. |
1251 | | * |
1252 | | * If you want to convert years and days-of-year, just give a month of |
1253 | | * zero. |
1254 | | *--------------------------------------------------------------------- |
1255 | | */ |
1256 | | int32_t |
1257 | | ntpcal_edate_to_eradays( |
1258 | | int32_t years, |
1259 | | int32_t mons, |
1260 | | int32_t mdays |
1261 | | ) |
1262 | 0 | { |
1263 | 0 | ntpcal_split tmp; |
1264 | 0 | int32_t res; |
1265 | |
|
1266 | 0 | if (mons) { |
1267 | 0 | tmp = ntpcal_days_in_months(mons); |
1268 | 0 | res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo; |
1269 | 0 | } else |
1270 | 0 | res = ntpcal_days_in_years(years); |
1271 | 0 | res += mdays; |
1272 | |
|
1273 | 0 | return res; |
1274 | 0 | } |
1275 | | |
1276 | | /* |
1277 | | *--------------------------------------------------------------------- |
1278 | | * Convert ELAPSED years/months/days of gregorian calendar to elapsed |
1279 | | * days in year. |
1280 | | * |
1281 | | * Note: This will give the true difference to the start of the given |
1282 | | * year, even if months & days are off-scale. |
1283 | | *--------------------------------------------------------------------- |
1284 | | */ |
1285 | | int32_t |
1286 | | ntpcal_edate_to_yeardays( |
1287 | | int32_t years, |
1288 | | int32_t mons, |
1289 | | int32_t mdays |
1290 | | ) |
1291 | 0 | { |
1292 | 0 | ntpcal_split tmp; |
1293 | |
|
1294 | 0 | if (0 <= mons && mons < 12) { |
1295 | 0 | years += 1; |
1296 | 0 | mdays += real_month_table[is_leapyear(years)][mons]; |
1297 | 0 | } else { |
1298 | 0 | tmp = ntpcal_days_in_months(mons); |
1299 | 0 | mdays += tmp.lo |
1300 | 0 | + ntpcal_days_in_years(years + tmp.hi) |
1301 | 0 | - ntpcal_days_in_years(years); |
1302 | 0 | } |
1303 | |
|
1304 | 0 | return mdays; |
1305 | 0 | } |
1306 | | |
1307 | | /* |
1308 | | *--------------------------------------------------------------------- |
1309 | | * Convert elapsed days and the hour/minute/second information into |
1310 | | * total seconds. |
1311 | | * |
1312 | | * If 'isvalid' is not NULL, do a range check on the time specification |
1313 | | * and tell if the time input is in the normal range, permitting for a |
1314 | | * single leapsecond. |
1315 | | *--------------------------------------------------------------------- |
1316 | | */ |
1317 | | int32_t |
1318 | | ntpcal_etime_to_seconds( |
1319 | | int32_t hours, |
1320 | | int32_t minutes, |
1321 | | int32_t seconds |
1322 | | ) |
1323 | 0 | { |
1324 | 0 | int32_t res; |
1325 | |
|
1326 | 0 | res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds; |
1327 | |
|
1328 | 0 | return res; |
1329 | 0 | } |
1330 | | |
1331 | | /* |
1332 | | *--------------------------------------------------------------------- |
1333 | | * Convert the date part of a 'struct tm' (that is, year, month, |
1334 | | * day-of-month) into the RD of that day. |
1335 | | *--------------------------------------------------------------------- |
1336 | | */ |
1337 | | int32_t |
1338 | | ntpcal_tm_to_rd( |
1339 | | const struct tm *utm |
1340 | | ) |
1341 | 0 | { |
1342 | 0 | return ntpcal_edate_to_eradays(utm->tm_year + 1899, |
1343 | 0 | utm->tm_mon, |
1344 | 0 | utm->tm_mday - 1) + 1; |
1345 | 0 | } |
1346 | | |
1347 | | /* |
1348 | | *--------------------------------------------------------------------- |
1349 | | * Convert the date part of a 'struct calendar' (that is, year, month, |
1350 | | * day-of-month) into the RD of that day. |
1351 | | *--------------------------------------------------------------------- |
1352 | | */ |
1353 | | int32_t |
1354 | | ntpcal_date_to_rd( |
1355 | | const struct calendar *jd |
1356 | | ) |
1357 | 0 | { |
1358 | 0 | return ntpcal_edate_to_eradays((int32_t)jd->year - 1, |
1359 | 0 | (int32_t)jd->month - 1, |
1360 | 0 | (int32_t)jd->monthday - 1) + 1; |
1361 | 0 | } |
1362 | | |
1363 | | /* |
1364 | | *--------------------------------------------------------------------- |
1365 | | * convert a year number to rata die of year start |
1366 | | *--------------------------------------------------------------------- |
1367 | | */ |
1368 | | int32_t |
1369 | | ntpcal_year_to_ystart( |
1370 | | int32_t year |
1371 | | ) |
1372 | 0 | { |
1373 | 0 | return ntpcal_days_in_years(year - 1) + 1; |
1374 | 0 | } |
1375 | | |
1376 | | /* |
1377 | | *--------------------------------------------------------------------- |
1378 | | * For a given RD, get the RD of the associated year start, |
1379 | | * that is, the RD of the last January,1st on or before that day. |
1380 | | *--------------------------------------------------------------------- |
1381 | | */ |
1382 | | int32_t |
1383 | | ntpcal_rd_to_ystart( |
1384 | | int32_t rd |
1385 | | ) |
1386 | 0 | { |
1387 | | /* |
1388 | | * Rather simple exercise: split the day number into elapsed |
1389 | | * years and elapsed days, then remove the elapsed days from the |
1390 | | * input value. Nice'n sweet... |
1391 | | */ |
1392 | 0 | return rd - ntpcal_split_eradays(rd - 1, NULL).lo; |
1393 | 0 | } |
1394 | | |
1395 | | /* |
1396 | | *--------------------------------------------------------------------- |
1397 | | * For a given RD, get the RD of the associated month start. |
1398 | | *--------------------------------------------------------------------- |
1399 | | */ |
1400 | | int32_t |
1401 | | ntpcal_rd_to_mstart( |
1402 | | int32_t rd |
1403 | | ) |
1404 | 0 | { |
1405 | 0 | ntpcal_split split; |
1406 | 0 | int leaps; |
1407 | |
|
1408 | 0 | split = ntpcal_split_eradays(rd - 1, &leaps); |
1409 | 0 | split = ntpcal_split_yeardays(split.lo, leaps); |
1410 | |
|
1411 | 0 | return rd - split.lo; |
1412 | 0 | } |
1413 | | |
1414 | | /* |
1415 | | *--------------------------------------------------------------------- |
1416 | | * take a 'struct calendar' and get the seconds-of-day from it. |
1417 | | *--------------------------------------------------------------------- |
1418 | | */ |
1419 | | int32_t |
1420 | | ntpcal_date_to_daysec( |
1421 | | const struct calendar *jd |
1422 | | ) |
1423 | 0 | { |
1424 | 0 | return ntpcal_etime_to_seconds(jd->hour, jd->minute, |
1425 | 0 | jd->second); |
1426 | 0 | } |
1427 | | |
1428 | | /* |
1429 | | *--------------------------------------------------------------------- |
1430 | | * take a 'struct tm' and get the seconds-of-day from it. |
1431 | | *--------------------------------------------------------------------- |
1432 | | */ |
1433 | | int32_t |
1434 | | ntpcal_tm_to_daysec( |
1435 | | const struct tm *utm |
1436 | | ) |
1437 | 0 | { |
1438 | 0 | return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min, |
1439 | 0 | utm->tm_sec); |
1440 | 0 | } |
1441 | | |
1442 | | /* |
1443 | | *--------------------------------------------------------------------- |
1444 | | * take a 'struct calendar' and convert it to a 'time_t' |
1445 | | *--------------------------------------------------------------------- |
1446 | | */ |
1447 | | time_t |
1448 | | ntpcal_date_to_time( |
1449 | | const struct calendar *jd |
1450 | | ) |
1451 | 0 | { |
1452 | 0 | vint64 join; |
1453 | 0 | int32_t days, secs; |
1454 | |
|
1455 | 0 | days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS; |
1456 | 0 | secs = ntpcal_date_to_daysec(jd); |
1457 | 0 | join = ntpcal_dayjoin(days, secs); |
1458 | |
|
1459 | 0 | return vint64_to_time(&join); |
1460 | 0 | } |
1461 | | |
1462 | | |
1463 | | /* |
1464 | | * ==================================================================== |
1465 | | * |
1466 | | * extended and unchecked variants of caljulian/caltontp |
1467 | | * |
1468 | | * ==================================================================== |
1469 | | */ |
1470 | | int |
1471 | | ntpcal_ntp64_to_date( |
1472 | | struct calendar *jd, |
1473 | | const vint64 *ntp |
1474 | | ) |
1475 | 0 | { |
1476 | 0 | ntpcal_split ds; |
1477 | |
|
1478 | 0 | ds = ntpcal_daysplit(ntp); |
1479 | 0 | ds.hi += ntpcal_daysec_to_date(jd, ds.lo); |
1480 | |
|
1481 | 0 | return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS); |
1482 | 0 | } |
1483 | | |
1484 | | int |
1485 | | ntpcal_ntp_to_date( |
1486 | | struct calendar *jd, |
1487 | | uint32_t ntp, |
1488 | | const time_t *piv |
1489 | | ) |
1490 | 0 | { |
1491 | 0 | vint64 ntp64; |
1492 | | |
1493 | | /* |
1494 | | * Unfold ntp time around current time into NTP domain. Split |
1495 | | * into days and seconds, shift days into CE domain and |
1496 | | * process the parts. |
1497 | | */ |
1498 | 0 | ntp64 = ntpcal_ntp_to_ntp(ntp, piv); |
1499 | 0 | return ntpcal_ntp64_to_date(jd, &ntp64); |
1500 | 0 | } |
1501 | | |
1502 | | |
1503 | | vint64 |
1504 | | ntpcal_date_to_ntp64( |
1505 | | const struct calendar *jd |
1506 | | ) |
1507 | 0 | { |
1508 | | /* |
1509 | | * Convert date to NTP. Ignore yearday, use d/m/y only. |
1510 | | */ |
1511 | 0 | return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS, |
1512 | 0 | ntpcal_date_to_daysec(jd)); |
1513 | 0 | } |
1514 | | |
1515 | | |
1516 | | uint32_t |
1517 | | ntpcal_date_to_ntp( |
1518 | | const struct calendar *jd |
1519 | | ) |
1520 | 0 | { |
1521 | | /* |
1522 | | * Get lower half of 64-bit NTP timestamp from date/time. |
1523 | | */ |
1524 | 0 | return ntpcal_date_to_ntp64(jd).d_s.lo; |
1525 | 0 | } |
1526 | | |
1527 | | |
1528 | | |
1529 | | /* |
1530 | | * ==================================================================== |
1531 | | * |
1532 | | * day-of-week calculations |
1533 | | * |
1534 | | * ==================================================================== |
1535 | | */ |
1536 | | /* |
1537 | | * Given a RataDie and a day-of-week, calculate a RDN that is reater-than, |
1538 | | * greater-or equal, closest, less-or-equal or less-than the given RDN |
1539 | | * and denotes the given day-of-week |
1540 | | */ |
1541 | | int32_t |
1542 | | ntpcal_weekday_gt( |
1543 | | int32_t rdn, |
1544 | | int32_t dow |
1545 | | ) |
1546 | 0 | { |
1547 | 0 | return ntpcal_periodic_extend(rdn+1, dow, 7); |
1548 | 0 | } |
1549 | | |
1550 | | int32_t |
1551 | | ntpcal_weekday_ge( |
1552 | | int32_t rdn, |
1553 | | int32_t dow |
1554 | | ) |
1555 | 0 | { |
1556 | 0 | return ntpcal_periodic_extend(rdn, dow, 7); |
1557 | 0 | } |
1558 | | |
1559 | | int32_t |
1560 | | ntpcal_weekday_close( |
1561 | | int32_t rdn, |
1562 | | int32_t dow |
1563 | | ) |
1564 | 0 | { |
1565 | 0 | return ntpcal_periodic_extend(rdn-3, dow, 7); |
1566 | 0 | } |
1567 | | |
1568 | | int32_t |
1569 | | ntpcal_weekday_le( |
1570 | | int32_t rdn, |
1571 | | int32_t dow |
1572 | | ) |
1573 | 0 | { |
1574 | 0 | return ntpcal_periodic_extend(rdn, dow, -7); |
1575 | 0 | } |
1576 | | |
1577 | | int32_t |
1578 | | ntpcal_weekday_lt( |
1579 | | int32_t rdn, |
1580 | | int32_t dow |
1581 | | ) |
1582 | 0 | { |
1583 | 0 | return ntpcal_periodic_extend(rdn-1, dow, -7); |
1584 | 0 | } |
1585 | | |
1586 | | /* |
1587 | | * ==================================================================== |
1588 | | * |
1589 | | * ISO week-calendar conversions |
1590 | | * |
1591 | | * The ISO8601 calendar defines a calendar of years, weeks and weekdays. |
1592 | | * It is related to the Gregorian calendar, and a ISO year starts at the |
1593 | | * Monday closest to Jan,1st of the corresponding Gregorian year. A ISO |
1594 | | * calendar year has always 52 or 53 weeks, and like the Grogrian |
1595 | | * calendar the ISO8601 calendar repeats itself every 400 years, or |
1596 | | * 146097 days, or 20871 weeks. |
1597 | | * |
1598 | | * While it is possible to write ISO calendar functions based on the |
1599 | | * Gregorian calendar functions, the following implementation takes a |
1600 | | * different approach, based directly on years and weeks. |
1601 | | * |
1602 | | * Analysis of the tabulated data shows that it is not possible to |
1603 | | * interpolate from years to weeks over a full 400 year range; cyclic |
1604 | | * shifts over 400 years do not provide a solution here. But it *is* |
1605 | | * possible to interpolate over every single century of the 400-year |
1606 | | * cycle. (The centennial leap year rule seems to be the culprit here.) |
1607 | | * |
1608 | | * It can be shown that a conversion from years to weeks can be done |
1609 | | * using a linear transformation of the form |
1610 | | * |
1611 | | * w = floor( y * a + b ) |
1612 | | * |
1613 | | * where the slope a must hold to |
1614 | | * |
1615 | | * 52.1780821918 <= a < 52.1791044776 |
1616 | | * |
1617 | | * and b must be chosen according to the selected slope and the number |
1618 | | * of the century in a 400-year period. |
1619 | | * |
1620 | | * The inverse calculation can also be done in this way. Careful scaling |
1621 | | * provides an unlimited set of integer coefficients a,k,b that enable |
1622 | | * us to write the calulation in the form |
1623 | | * |
1624 | | * w = (y * a + b ) / k |
1625 | | * y = (w * a' + b') / k' |
1626 | | * |
1627 | | * In this implementation the values of k and k' are chosen to be |
1628 | | * smallest possible powers of two, so the division can be implemented |
1629 | | * as shifts if the optimiser chooses to do so. |
1630 | | * |
1631 | | * ==================================================================== |
1632 | | */ |
1633 | | |
1634 | | /* |
1635 | | * Given a number of elapsed (ISO-)years since the begin of the |
1636 | | * christian era, return the number of elapsed weeks corresponding to |
1637 | | * the number of years. |
1638 | | */ |
1639 | | int32_t |
1640 | | isocal_weeks_in_years( |
1641 | | int32_t years |
1642 | | ) |
1643 | 0 | { |
1644 | | /* |
1645 | | * use: w = (y * 53431 + b[c]) / 1024 as interpolation |
1646 | | */ |
1647 | 0 | static const uint16_t bctab[4] = { 157, 449, 597, 889 }; |
1648 | |
|
1649 | 0 | int32_t cs, cw; |
1650 | 0 | uint32_t cc, ci, yu, sflag; |
1651 | |
|
1652 | 0 | sflag = int32_sflag(years); |
1653 | 0 | yu = int32_to_uint32_2cpl(years); |
1654 | | |
1655 | | /* split off centuries, using floor division */ |
1656 | 0 | cc = sflag ^ ((sflag ^ yu) / 100u); |
1657 | 0 | yu -= cc * 100u; |
1658 | | |
1659 | | /* calculate century cycles shift and cycle index: |
1660 | | * Assuming a century is 5217 weeks, we have to add a cycle |
1661 | | * shift that is 3 for every 4 centuries, because 3 of the four |
1662 | | * centuries have 5218 weeks. So '(cc*3 + 1) / 4' is the actual |
1663 | | * correction, and the second century is the defective one. |
1664 | | * |
1665 | | * Needs floor division by 4, which is done with masking and |
1666 | | * shifting. |
1667 | | */ |
1668 | 0 | ci = cc * 3u + 1; |
1669 | 0 | cs = uint32_2cpl_to_int32(sflag ^ ((sflag ^ ci) / 4u)); |
1670 | 0 | ci = ci % 4u; |
1671 | | |
1672 | | /* Get weeks in century. Can use plain division here as all ops |
1673 | | * are >= 0, and let the compiler sort out the possible |
1674 | | * optimisations. |
1675 | | */ |
1676 | 0 | cw = (yu * 53431u + bctab[ci]) / 1024u; |
1677 | |
|
1678 | 0 | return uint32_2cpl_to_int32(cc) * 5217 + cs + cw; |
1679 | 0 | } |
1680 | | |
1681 | | /* |
1682 | | * Given a number of elapsed weeks since the begin of the christian |
1683 | | * era, split this number into the number of elapsed years in res.hi |
1684 | | * and the excessive number of weeks in res.lo. (That is, res.lo is |
1685 | | * the number of elapsed weeks in the remaining partial year.) |
1686 | | */ |
1687 | | ntpcal_split |
1688 | | isocal_split_eraweeks( |
1689 | | int32_t weeks |
1690 | | ) |
1691 | 0 | { |
1692 | | /* |
1693 | | * use: y = (w * 157 + b[c]) / 8192 as interpolation |
1694 | | */ |
1695 | |
|
1696 | 0 | static const uint16_t bctab[4] = { 85, 130, 17, 62 }; |
1697 | |
|
1698 | 0 | ntpcal_split res; |
1699 | 0 | int32_t cc, ci; |
1700 | 0 | uint32_t sw, cy, Q, sflag; |
1701 | | |
1702 | | /* Use two fast cycle-split divisions here. This is again |
1703 | | * susceptible to internal overflow, so we check the range. This |
1704 | | * still permits more than +/-20 million years, so this is |
1705 | | * likely a pure academical problem. |
1706 | | * |
1707 | | * We want to execute '(weeks * 4 + 2) /% 20871' under floor |
1708 | | * division rules in the first step. |
1709 | | */ |
1710 | 0 | sflag = int32_sflag(weeks); |
1711 | 0 | sw = uint32_saturate(int32_to_uint32_2cpl(weeks), sflag); |
1712 | 0 | sw = 4u * sw + 2; |
1713 | 0 | Q = sflag ^ ((sflag ^ sw) / GREGORIAN_CYCLE_WEEKS); |
1714 | 0 | sw -= Q * GREGORIAN_CYCLE_WEEKS; |
1715 | 0 | ci = Q % 4u; |
1716 | 0 | cc = uint32_2cpl_to_int32(Q); |
1717 | | |
1718 | | /* Split off years; sw >= 0 here! The scaled weeks in the years |
1719 | | * are scaled up by 157 afterwards. |
1720 | | */ |
1721 | 0 | sw = (sw / 4u) * 157u + bctab[ci]; |
1722 | 0 | cy = sw / 8192u; /* ws >> 13 , let the compiler sort it out */ |
1723 | 0 | sw = sw % 8192u; /* ws & 8191, let the compiler sort it out */ |
1724 | | |
1725 | | /* assemble elapsed years and downscale the elapsed weeks in |
1726 | | * the year. |
1727 | | */ |
1728 | 0 | res.hi = 100*cc + cy; |
1729 | 0 | res.lo = sw / 157u; |
1730 | |
|
1731 | 0 | return res; |
1732 | 0 | } |
1733 | | |
1734 | | /* |
1735 | | * Given a second in the NTP time scale and a pivot, expand the NTP |
1736 | | * time stamp around the pivot and convert into an ISO calendar time |
1737 | | * stamp. |
1738 | | */ |
1739 | | int |
1740 | | isocal_ntp64_to_date( |
1741 | | struct isodate *id, |
1742 | | const vint64 *ntp |
1743 | | ) |
1744 | 0 | { |
1745 | 0 | ntpcal_split ds; |
1746 | 0 | int32_t ts[3]; |
1747 | 0 | uint32_t uw, ud, sflag; |
1748 | | |
1749 | | /* |
1750 | | * Split NTP time into days and seconds, shift days into CE |
1751 | | * domain and process the parts. |
1752 | | */ |
1753 | 0 | ds = ntpcal_daysplit(ntp); |
1754 | | |
1755 | | /* split time part */ |
1756 | 0 | ds.hi += priv_timesplit(ts, ds.lo); |
1757 | 0 | id->hour = (uint8_t)ts[0]; |
1758 | 0 | id->minute = (uint8_t)ts[1]; |
1759 | 0 | id->second = (uint8_t)ts[2]; |
1760 | | |
1761 | | /* split days into days and weeks, using floor division in unsigned */ |
1762 | 0 | ds.hi += DAY_NTP_STARTS - 1; /* shift from NTP to RDN */ |
1763 | 0 | sflag = int32_sflag(ds.hi); |
1764 | 0 | ud = int32_to_uint32_2cpl(ds.hi); |
1765 | 0 | uw = sflag ^ ((sflag ^ ud) / DAYSPERWEEK); |
1766 | 0 | ud -= uw * DAYSPERWEEK; |
1767 | 0 | ds.hi = uint32_2cpl_to_int32(uw); |
1768 | 0 | ds.lo = ud; |
1769 | |
|
1770 | 0 | id->weekday = (uint8_t)ds.lo + 1; /* weekday result */ |
1771 | | |
1772 | | /* get year and week in year */ |
1773 | 0 | ds = isocal_split_eraweeks(ds.hi); /* elapsed years&week*/ |
1774 | 0 | id->year = (uint16_t)ds.hi + 1; /* shift to current */ |
1775 | 0 | id->week = (uint8_t )ds.lo + 1; |
1776 | |
|
1777 | 0 | return (ds.hi >= 0 && ds.hi < 0x0000FFFF); |
1778 | 0 | } |
1779 | | |
1780 | | int |
1781 | | isocal_ntp_to_date( |
1782 | | struct isodate *id, |
1783 | | uint32_t ntp, |
1784 | | const time_t *piv |
1785 | | ) |
1786 | 0 | { |
1787 | 0 | vint64 ntp64; |
1788 | | |
1789 | | /* |
1790 | | * Unfold ntp time around current time into NTP domain, then |
1791 | | * convert the full time stamp. |
1792 | | */ |
1793 | 0 | ntp64 = ntpcal_ntp_to_ntp(ntp, piv); |
1794 | 0 | return isocal_ntp64_to_date(id, &ntp64); |
1795 | 0 | } |
1796 | | |
1797 | | /* |
1798 | | * Convert a ISO date spec into a second in the NTP time scale, |
1799 | | * properly truncated to 32 bit. |
1800 | | */ |
1801 | | vint64 |
1802 | | isocal_date_to_ntp64( |
1803 | | const struct isodate *id |
1804 | | ) |
1805 | 0 | { |
1806 | 0 | int32_t weeks, days, secs; |
1807 | |
|
1808 | 0 | weeks = isocal_weeks_in_years((int32_t)id->year - 1) |
1809 | 0 | + (int32_t)id->week - 1; |
1810 | 0 | days = weeks * 7 + (int32_t)id->weekday; |
1811 | | /* days is RDN of ISO date now */ |
1812 | 0 | secs = ntpcal_etime_to_seconds(id->hour, id->minute, id->second); |
1813 | |
|
1814 | 0 | return ntpcal_dayjoin(days - DAY_NTP_STARTS, secs); |
1815 | 0 | } |
1816 | | |
1817 | | uint32_t |
1818 | | isocal_date_to_ntp( |
1819 | | const struct isodate *id |
1820 | | ) |
1821 | 0 | { |
1822 | | /* |
1823 | | * Get lower half of 64-bit NTP timestamp from date/time. |
1824 | | */ |
1825 | 0 | return isocal_date_to_ntp64(id).d_s.lo; |
1826 | 0 | } |
1827 | | |
1828 | | /* |
1829 | | * ==================================================================== |
1830 | | * 'basedate' support functions |
1831 | | * ==================================================================== |
1832 | | */ |
1833 | | |
1834 | | static int32_t s_baseday = NTP_TO_UNIX_DAYS; |
1835 | | static int32_t s_gpsweek = 0; |
1836 | | |
1837 | | int32_t |
1838 | | basedate_eval_buildstamp(void) |
1839 | 0 | { |
1840 | 0 | struct calendar jd; |
1841 | 0 | int32_t ed; |
1842 | | |
1843 | 0 | if (!ntpcal_get_build_date(&jd)) |
1844 | 0 | return NTP_TO_UNIX_DAYS; |
1845 | | |
1846 | | /* The time zone of the build stamp is unspecified; we remove |
1847 | | * one day to provide a certain slack. And in case somebody |
1848 | | * fiddled with the system clock, we make sure we do not go |
1849 | | * before the UNIX epoch (1970-01-01). It's probably not possible |
1850 | | * to do this to the clock on most systems, but there are other |
1851 | | * ways to tweak the build stamp. |
1852 | | */ |
1853 | 0 | jd.monthday -= 1; |
1854 | 0 | ed = ntpcal_date_to_rd(&jd) - DAY_NTP_STARTS; |
1855 | 0 | return (ed < NTP_TO_UNIX_DAYS) ? NTP_TO_UNIX_DAYS : ed; |
1856 | 0 | } |
1857 | | |
1858 | | int32_t |
1859 | | basedate_eval_string( |
1860 | | const char * str |
1861 | | ) |
1862 | 0 | { |
1863 | 0 | u_short y,m,d; |
1864 | 0 | u_long ned; |
1865 | 0 | int rc, nc; |
1866 | 0 | size_t sl; |
1867 | |
|
1868 | 0 | sl = strlen(str); |
1869 | 0 | rc = sscanf(str, "%4hu-%2hu-%2hu%n", &y, &m, &d, &nc); |
1870 | 0 | if (rc == 3 && (size_t)nc == sl) { |
1871 | 0 | if (m >= 1 && m <= 12 && d >= 1 && d <= 31) |
1872 | 0 | return ntpcal_edate_to_eradays(y-1, m-1, d) |
1873 | 0 | - DAY_NTP_STARTS; |
1874 | 0 | goto buildstamp; |
1875 | 0 | } |
1876 | | |
1877 | 0 | rc = sscanf(str, "%lu%n", &ned, &nc); |
1878 | 0 | if (rc == 1 && (size_t)nc == sl) { |
1879 | 0 | if (ned <= INT32_MAX) |
1880 | 0 | return (int32_t)ned; |
1881 | 0 | goto buildstamp; |
1882 | 0 | } |
1883 | | |
1884 | 0 | buildstamp: |
1885 | 0 | msyslog(LOG_WARNING, |
1886 | 0 | "basedate string \"%s\" invalid, build date substituted!", |
1887 | 0 | str); |
1888 | 0 | return basedate_eval_buildstamp(); |
1889 | 0 | } |
1890 | | |
1891 | | uint32_t |
1892 | | basedate_get_day(void) |
1893 | 0 | { |
1894 | 0 | return s_baseday; |
1895 | 0 | } |
1896 | | |
1897 | | int32_t |
1898 | | basedate_set_day( |
1899 | | int32_t day |
1900 | | ) |
1901 | 0 | { |
1902 | 0 | struct calendar jd; |
1903 | 0 | int32_t retv; |
1904 | | |
1905 | | /* set NTP base date for NTP era unfolding */ |
1906 | 0 | if (day < NTP_TO_UNIX_DAYS) { |
1907 | 0 | msyslog(LOG_WARNING, |
1908 | 0 | "baseday_set_day: invalid day (%lu), UNIX epoch substituted", |
1909 | 0 | (unsigned long)day); |
1910 | 0 | day = NTP_TO_UNIX_DAYS; |
1911 | 0 | } |
1912 | 0 | retv = s_baseday; |
1913 | 0 | s_baseday = day; |
1914 | 0 | ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS); |
1915 | 0 | msyslog(LOG_INFO, "basedate set to %04hu-%02hu-%02hu", |
1916 | 0 | jd.year, (u_short)jd.month, (u_short)jd.monthday); |
1917 | | |
1918 | | /* set GPS base week for GPS week unfolding */ |
1919 | 0 | day = ntpcal_weekday_ge(day + DAY_NTP_STARTS, CAL_SUNDAY) |
1920 | 0 | - DAY_NTP_STARTS; |
1921 | 0 | if (day < NTP_TO_GPS_DAYS) |
1922 | 0 | day = NTP_TO_GPS_DAYS; |
1923 | 0 | s_gpsweek = (day - NTP_TO_GPS_DAYS) / DAYSPERWEEK; |
1924 | 0 | ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS); |
1925 | 0 | msyslog(LOG_INFO, "gps base set to %04hu-%02hu-%02hu (week %d)", |
1926 | 0 | jd.year, (u_short)jd.month, (u_short)jd.monthday, s_gpsweek); |
1927 | | |
1928 | 0 | return retv; |
1929 | 0 | } |
1930 | | |
1931 | | time_t |
1932 | | basedate_get_eracenter(void) |
1933 | 0 | { |
1934 | 0 | time_t retv; |
1935 | 0 | retv = (time_t)(s_baseday - NTP_TO_UNIX_DAYS); |
1936 | 0 | retv *= SECSPERDAY; |
1937 | 0 | retv += (UINT32_C(1) << 31); |
1938 | 0 | return retv; |
1939 | 0 | } |
1940 | | |
1941 | | time_t |
1942 | | basedate_get_erabase(void) |
1943 | 0 | { |
1944 | 0 | time_t retv; |
1945 | 0 | retv = (time_t)(s_baseday - NTP_TO_UNIX_DAYS); |
1946 | 0 | retv *= SECSPERDAY; |
1947 | 0 | return retv; |
1948 | 0 | } |
1949 | | |
1950 | | uint32_t |
1951 | | basedate_get_gpsweek(void) |
1952 | 0 | { |
1953 | 0 | return s_gpsweek; |
1954 | 0 | } |
1955 | | |
1956 | | uint32_t |
1957 | | basedate_expand_gpsweek( |
1958 | | unsigned short weekno |
1959 | | ) |
1960 | 0 | { |
1961 | | /* We do a fast modulus expansion here. Since all quantities are |
1962 | | * unsigned and we cannot go before the start of the GPS epoch |
1963 | | * anyway, and since the truncated GPS week number is 10 bit, the |
1964 | | * expansion becomes a simple sub/and/add sequence. |
1965 | | */ |
1966 | | #if GPSWEEKS != 1024 |
1967 | | # error GPSWEEKS defined wrong -- should be 1024! |
1968 | | #endif |
1969 | | |
1970 | 0 | uint32_t diff; |
1971 | 0 | diff = ((uint32_t)weekno - s_gpsweek) & (GPSWEEKS - 1); |
1972 | 0 | return s_gpsweek + diff; |
1973 | 0 | } |
1974 | | |
1975 | | /* -*-EOF-*- */ |