Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2019 Peter Bigot Consulting, LLC
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : :
7 : : /*
8 : : * The time_days_from_civil function is derived directly from public
9 : : * domain content written by Howard Hinnant and available at:
10 : : * http://howardhinnant.github.io/date_algorithms.html#days_from_civil
11 : : */
12 : :
13 : : #include <errno.h>
14 : : #include <stdbool.h>
15 : : #include <stddef.h>
16 : : #include <stdint.h>
17 : : #include <time.h>
18 : :
19 : : #include <zephyr/sys/__assert.h>
20 : : #include <zephyr/sys/clock.h>
21 : : #include <zephyr/sys/timeutil.h>
22 : :
23 : : /** Convert a civil (proleptic Gregorian) date to days relative to
24 : : * 1970-01-01.
25 : : *
26 : : * @param y the calendar year
27 : : * @param m the calendar month, in the range [1, 12]
28 : : * @param d the day of the month, in the range [1, last_day_of_month(y, m)]
29 : : *
30 : : * @return the signed number of days between the specified day and
31 : : * 1970-01-01
32 : : *
33 : : * @see http://howardhinnant.github.io/date_algorithms.html#days_from_civil
34 : : */
35 : 0 : static int64_t time_days_from_civil(int64_t y,
36 : : unsigned int m,
37 : : unsigned int d)
38 : : {
39 : 0 : y -= m <= 2;
40 : :
41 [ # # ]: 0 : int64_t era = ((y >= 0) ? y : (y - 399)) / 400;
42 : 0 : unsigned int yoe = y - era * 400;
43 [ # # ]: 0 : unsigned int doy = (153U * (m + ((m > 2) ? -3 : 9)) + 2U) / 5U + d;
44 : 0 : unsigned int doe = yoe * 365U + yoe / 4U - yoe / 100U + doy;
45 : :
46 : 0 : return era * 146097 + (time_t)doe - 719468;
47 : : }
48 : :
49 : 0 : int64_t timeutil_timegm64(const struct tm *tm)
50 : : {
51 : 0 : int64_t y = TIME_UTILS_BASE_YEAR + (int64_t)tm->tm_year;
52 : 0 : unsigned int m = tm->tm_mon + 1;
53 : 0 : unsigned int d = tm->tm_mday - 1;
54 : 0 : int64_t ndays = time_days_from_civil(y, m, d);
55 : 0 : int64_t time = tm->tm_sec;
56 : :
57 : 0 : time += 60LL * (tm->tm_min + 60LL * tm->tm_hour);
58 : 0 : time += 86400LL * ndays;
59 : :
60 : 0 : return time;
61 : : }
62 : :
63 : 0 : time_t timeutil_timegm(const struct tm *tm)
64 : : {
65 : 0 : int64_t time = timeutil_timegm64(tm);
66 : 0 : time_t rv = (time_t)time;
67 : :
68 : 0 : errno = 0;
69 : 0 : if ((sizeof(rv) == sizeof(int32_t))
70 : : && ((time < (int64_t)INT32_MIN)
71 [ # # ]: 0 : || (time > (int64_t)INT32_MAX))) {
72 : 0 : errno = ERANGE;
73 : 0 : rv = -1;
74 : : }
75 : :
76 : 0 : return rv;
77 : : }
78 : :
79 : 0 : int timeutil_sync_state_update(struct timeutil_sync_state *tsp,
80 : : const struct timeutil_sync_instant *inst)
81 : : {
82 : 0 : int rv = -EINVAL;
83 : :
84 [ # # # # ]: 0 : if (((tsp->base.ref == 0) && (inst->ref > 0))
85 [ # # ]: 0 : || ((inst->ref > tsp->base.ref)
86 [ # # ]: 0 : && (inst->local > tsp->base.local))) {
87 [ # # ]: 0 : if (tsp->base.ref == 0) {
88 : 0 : tsp->base = *inst;
89 : 0 : tsp->latest = (struct timeutil_sync_instant){};
90 : 0 : tsp->skew = 1.0f;
91 : 0 : rv = 0;
92 : : } else {
93 : 0 : tsp->latest = *inst;
94 : 0 : rv = 1;
95 : : }
96 : : }
97 : :
98 : 0 : return rv;
99 : : }
100 : :
101 : 0 : int timeutil_sync_state_set_skew(struct timeutil_sync_state *tsp, float skew,
102 : : const struct timeutil_sync_instant *base)
103 : : {
104 : 0 : int rv = -EINVAL;
105 : :
106 [ # # ]: 0 : if (skew > 0) {
107 : 0 : tsp->skew = skew;
108 [ # # ]: 0 : if (base != NULL) {
109 : 0 : tsp->base = *base;
110 : 0 : tsp->latest = (struct timeutil_sync_instant){};
111 : : }
112 : : rv = 0;
113 : : }
114 : :
115 : 0 : return rv;
116 : : }
117 : :
118 : 0 : float timeutil_sync_estimate_skew(const struct timeutil_sync_state *tsp)
119 : : {
120 : 0 : float rv = 0;
121 : :
122 [ # # # # ]: 0 : if ((tsp->base.ref != 0) && (tsp->latest.ref != 0)
123 [ # # ]: 0 : && (tsp->latest.local > tsp->base.local)) {
124 : 0 : const struct timeutil_sync_config *cfg = tsp->cfg;
125 : 0 : double ref_delta = tsp->latest.ref - tsp->base.ref;
126 : 0 : double local_delta = tsp->latest.local - tsp->base.local;
127 : :
128 : 0 : rv = ref_delta * cfg->local_Hz / local_delta / cfg->ref_Hz;
129 : : }
130 : :
131 : 0 : return rv;
132 : : }
133 : :
134 : 0 : int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp,
135 : : uint64_t local, uint64_t *refp)
136 : : {
137 : 0 : int rv = -EINVAL;
138 : :
139 [ # # # # : 0 : if ((tsp->skew > 0) && (tsp->base.ref > 0) && (refp != NULL)) {
# # ]
140 : 0 : const struct timeutil_sync_config *cfg = tsp->cfg;
141 : 0 : int64_t local_delta = local - tsp->base.local;
142 : : #ifdef CONFIG_TIMEUTIL_APPLY_SKEW
143 : : /* (x * 1.0) != x for large values of x.
144 : : * Therefore only apply the multiplication if the skew is not one.
145 : : */
146 [ # # ]: 0 : if (tsp->skew != 1.0f) {
147 : 0 : local_delta *= (double)tsp->skew;
148 : : }
149 : : #endif /* CONFIG_TIMEUTIL_APPLY_SKEW */
150 : 0 : int64_t ref_delta = local_delta * cfg->ref_Hz / cfg->local_Hz;
151 : 0 : int64_t ref_abs = (int64_t)tsp->base.ref + ref_delta;
152 : :
153 [ # # ]: 0 : if (ref_abs < 0) {
154 : : rv = -ERANGE;
155 : : } else {
156 : 0 : *refp = ref_abs;
157 : 0 : rv = (tsp->skew != 1.0f) ? 1 : 0;
158 : : }
159 : : }
160 : :
161 : 0 : return rv;
162 : : }
163 : :
164 : 0 : int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp,
165 : : uint64_t ref, int64_t *localp)
166 : : {
167 : 0 : int rv = -EINVAL;
168 : :
169 [ # # # # : 0 : if ((tsp->skew > 0) && (tsp->base.ref > 0) && (localp != NULL)) {
# # ]
170 : 0 : const struct timeutil_sync_config *cfg = tsp->cfg;
171 : 0 : int64_t ref_delta = (int64_t)(ref - tsp->base.ref);
172 : 0 : int64_t local_delta = (ref_delta * cfg->local_Hz) / cfg->ref_Hz;
173 : : #ifdef CONFIG_TIMEUTIL_APPLY_SKEW
174 : : /* (x / 1.0) != x for large values of x.
175 : : * Therefore only apply the division if the skew is not one.
176 : : */
177 [ # # ]: 0 : if (tsp->skew != 1.0f) {
178 : 0 : local_delta /= (double)tsp->skew;
179 : : }
180 : : #endif /* CONFIG_TIMEUTIL_APPLY_SKEW */
181 : 0 : int64_t local_abs = (int64_t)tsp->base.local
182 : : + (int64_t)local_delta;
183 : :
184 : 0 : *localp = local_abs;
185 : 0 : rv = (tsp->skew != 1.0f) ? 1 : 0;
186 : : }
187 : :
188 : 0 : return rv;
189 : : }
190 : :
191 : 0 : int32_t timeutil_sync_skew_to_ppb(float skew)
192 : : {
193 : 0 : int64_t ppb64 = (int64_t)((1.0 - (double)skew) * 1E9);
194 : 0 : int32_t ppb32 = (int32_t)ppb64;
195 : :
196 [ # # ]: 0 : return (ppb64 == ppb32) ? ppb32 : INT32_MIN;
197 : : }
198 : :
199 : 0 : bool timespec_normalize(struct timespec *ts)
200 : : {
201 [ # # ]: 0 : __ASSERT_NO_MSG(ts != NULL);
202 : :
203 : 0 : long sec;
204 : :
205 [ # # ]: 0 : if (ts->tv_nsec >= (long)NSEC_PER_SEC) {
206 : 0 : sec = ts->tv_nsec / (long)NSEC_PER_SEC;
207 [ # # ]: 0 : } else if (ts->tv_nsec < 0) {
208 : 0 : sec = DIV_ROUND_UP((unsigned long)-ts->tv_nsec, NSEC_PER_SEC);
209 : : } else {
210 : : sec = 0;
211 : : }
212 : :
213 [ # # # # : 0 : if ((ts->tv_nsec < 0) && (ts->tv_sec < 0) && (ts->tv_sec - SYS_TIME_T_MIN < sec)) {
# # ]
214 : : /*
215 : : * When `tv_nsec` is negative and `tv_sec` is already most negative,
216 : : * further subtraction would cause integer overflow.
217 : : */
218 : : return false;
219 : : }
220 : :
221 [ # # # # ]: 0 : if ((ts->tv_nsec >= (long)NSEC_PER_SEC) && (ts->tv_sec > 0) &&
222 [ # # ]: 0 : (SYS_TIME_T_MAX - ts->tv_sec < sec)) {
223 : : /*
224 : : * When `tv_nsec` is >= `NSEC_PER_SEC` and `tv_sec` is already most
225 : : * positive, further addition would cause integer overflow.
226 : : */
227 : : return false;
228 : : }
229 : :
230 [ # # ]: 0 : if (ts->tv_nsec >= (long)NSEC_PER_SEC) {
231 : 0 : ts->tv_sec += sec;
232 : 0 : ts->tv_nsec -= sec * (long)NSEC_PER_SEC;
233 [ # # ]: 0 : } else if (ts->tv_nsec < 0) {
234 : 0 : ts->tv_sec -= sec;
235 : 0 : ts->tv_nsec += sec * (long)NSEC_PER_SEC;
236 : : } else {
237 : : /* no change: SonarQube was complaining */
238 : 0 : }
239 : :
240 [ # # ]: 0 : __ASSERT_NO_MSG(timespec_is_valid(ts));
241 : :
242 : : return true;
243 : : }
|