]>
Commit | Line | Data |
---|---|---|
320054e8 DG |
1 | // Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/ |
2 | // | |
3 | // SPDX-License-Identifier: BSD-2-Clause | |
4 | ||
5 | #ifndef COMMON_TIME_H | |
6 | #define COMMON_TIME_H | |
7 | ||
8 | #include <common/limits.h> | |
9 | #include <common/overflow.h> | |
10 | ||
11 | #include <sys/time.h> | |
12 | ||
446cb3f1 | 13 | #include <wasi/api.h> |
320054e8 DG |
14 | #include <stdbool.h> |
15 | #include <time.h> | |
16 | ||
17 | #define NSEC_PER_SEC 1000000000 | |
18 | ||
19 | // Timezone agnostic conversion routines. | |
20 | int __localtime_utc(time_t, struct tm *); | |
21 | void __mktime_utc(const struct tm *, struct timespec *); | |
22 | ||
23 | static inline bool is_leap(time_t year) { | |
24 | year %= 400; | |
25 | if (year < 0) | |
26 | year += 400; | |
27 | return ((year % 4) == 0 && (year % 100) != 0) || year == 100; | |
28 | } | |
29 | ||
30 | // Gets the length of the months in a year. | |
31 | static inline const char *get_months(time_t year) { | |
32 | static const char leap[12] = { | |
33 | 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | |
34 | }; | |
35 | static const char common[12] = { | |
36 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | |
37 | }; | |
38 | return is_leap(year) ? leap : common; | |
39 | } | |
40 | ||
41 | // Gets the cumulative length of the months in a year. | |
42 | static inline const short *get_months_cumulative(time_t year) { | |
43 | static const short leap[13] = { | |
44 | 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, | |
45 | }; | |
46 | static const short common[13] = { | |
47 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, | |
48 | }; | |
49 | return is_leap(year) ? leap : common; | |
50 | } | |
51 | ||
52 | static inline short get_ydays(time_t year) { | |
53 | return is_leap(year) ? 366 : 365; | |
54 | } | |
55 | ||
56 | static inline bool timespec_to_timestamp_exact( | |
57 | const struct timespec *timespec, __wasi_timestamp_t *timestamp) { | |
58 | // Invalid nanoseconds field. | |
59 | if (timespec->tv_nsec < 0 || timespec->tv_nsec >= NSEC_PER_SEC) | |
60 | return false; | |
61 | ||
62 | // Timestamps before the Epoch are not supported. | |
63 | if (timespec->tv_sec < 0) | |
64 | return false; | |
65 | ||
66 | // Make sure our timestamp does not overflow. | |
67 | return !mul_overflow(timespec->tv_sec, NSEC_PER_SEC, timestamp) && | |
68 | !add_overflow(*timestamp, timespec->tv_nsec, timestamp); | |
69 | } | |
70 | ||
71 | static inline bool timespec_to_timestamp_clamp( | |
72 | const struct timespec *timespec, __wasi_timestamp_t *timestamp) { | |
73 | // Invalid nanoseconds field. | |
74 | if (timespec->tv_nsec < 0 || timespec->tv_nsec >= NSEC_PER_SEC) | |
75 | return false; | |
76 | ||
77 | if (timespec->tv_sec < 0) { | |
78 | // Timestamps before the Epoch are not supported. | |
79 | *timestamp = 0; | |
80 | } else if (mul_overflow(timespec->tv_sec, NSEC_PER_SEC, timestamp) || | |
81 | add_overflow(*timestamp, timespec->tv_nsec, timestamp)) { | |
82 | // Make sure our timestamp does not overflow. | |
83 | *timestamp = NUMERIC_MAX(__wasi_timestamp_t); | |
84 | } | |
85 | return true; | |
86 | } | |
87 | ||
88 | static inline struct timespec timestamp_to_timespec( | |
89 | __wasi_timestamp_t timestamp) { | |
90 | // Decompose timestamp into seconds and nanoseconds. | |
91 | return (struct timespec){.tv_sec = timestamp / NSEC_PER_SEC, | |
92 | .tv_nsec = timestamp % NSEC_PER_SEC}; | |
93 | } | |
94 | ||
95 | static inline struct timeval timestamp_to_timeval( | |
96 | __wasi_timestamp_t timestamp) { | |
97 | struct timespec ts = timestamp_to_timespec(timestamp); | |
98 | return (struct timeval){.tv_sec = ts.tv_sec, ts.tv_nsec / 1000}; | |
99 | } | |
100 | ||
101 | #endif |