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  */
time_days_from_civil(int64_t y,unsigned int m,unsigned int d)35 static int64_t time_days_from_civil(int64_t y,
36 				    unsigned int m,
37 				    unsigned int d)
38 {
39 	y -= m <= 2;
40 
41 	int64_t era = ((y >= 0) ? y : (y - 399)) / 400;
42 	unsigned int yoe = y - era * 400;
43 	unsigned int doy = (153U * (m + ((m > 2) ? -3 : 9)) + 2U) / 5U + d;
44 	unsigned int doe = yoe * 365U + yoe / 4U - yoe / 100U + doy;
45 
46 	return era * 146097 + (time_t)doe - 719468;
47 }
48 
timeutil_timegm64(const struct tm * tm)49 int64_t timeutil_timegm64(const struct tm *tm)
50 {
51 	int64_t y = TIME_UTILS_BASE_YEAR + (int64_t)tm->tm_year;
52 	unsigned int m = tm->tm_mon + 1;
53 	unsigned int d = tm->tm_mday - 1;
54 	int64_t ndays = time_days_from_civil(y, m, d);
55 	int64_t time = tm->tm_sec;
56 
57 	time += 60LL * (tm->tm_min + 60LL * tm->tm_hour);
58 	time += 86400LL * ndays;
59 
60 	return time;
61 }
62 
timeutil_timegm(const struct tm * tm)63 time_t timeutil_timegm(const struct tm *tm)
64 {
65 	int64_t time = timeutil_timegm64(tm);
66 	time_t rv = (time_t)time;
67 
68 	errno = 0;
69 	if ((sizeof(rv) == sizeof(int32_t))
70 	    && ((time < (int64_t)INT32_MIN)
71 		|| (time > (int64_t)INT32_MAX))) {
72 		errno = ERANGE;
73 		rv = -1;
74 	}
75 
76 	return rv;
77 }
78 
timeutil_sync_state_update(struct timeutil_sync_state * tsp,const struct timeutil_sync_instant * inst)79 int timeutil_sync_state_update(struct timeutil_sync_state *tsp,
80 			       const struct timeutil_sync_instant *inst)
81 {
82 	int rv = -EINVAL;
83 
84 	if (((tsp->base.ref == 0) && (inst->ref > 0))
85 	    || ((inst->ref > tsp->base.ref)
86 		&& (inst->local > tsp->base.local))) {
87 		if (tsp->base.ref == 0) {
88 			tsp->base = *inst;
89 			tsp->latest = (struct timeutil_sync_instant){};
90 			tsp->skew = 1.0f;
91 			rv = 0;
92 		} else {
93 			tsp->latest = *inst;
94 			rv = 1;
95 		}
96 	}
97 
98 	return rv;
99 }
100 
timeutil_sync_state_set_skew(struct timeutil_sync_state * tsp,float skew,const struct timeutil_sync_instant * base)101 int timeutil_sync_state_set_skew(struct timeutil_sync_state *tsp, float skew,
102 				 const struct timeutil_sync_instant *base)
103 {
104 	int rv = -EINVAL;
105 
106 	if (skew > 0) {
107 		tsp->skew = skew;
108 		if (base != NULL) {
109 			tsp->base = *base;
110 			tsp->latest = (struct timeutil_sync_instant){};
111 		}
112 		rv = 0;
113 	}
114 
115 	return rv;
116 }
117 
timeutil_sync_estimate_skew(const struct timeutil_sync_state * tsp)118 float timeutil_sync_estimate_skew(const struct timeutil_sync_state *tsp)
119 {
120 	float rv = 0;
121 
122 	if ((tsp->base.ref != 0) && (tsp->latest.ref != 0)
123 	    && (tsp->latest.local > tsp->base.local)) {
124 		const struct timeutil_sync_config *cfg = tsp->cfg;
125 		double ref_delta = tsp->latest.ref - tsp->base.ref;
126 		double local_delta = tsp->latest.local - tsp->base.local;
127 
128 		rv = ref_delta * cfg->local_Hz / local_delta / cfg->ref_Hz;
129 	}
130 
131 	return rv;
132 }
133 
timeutil_sync_ref_from_local(const struct timeutil_sync_state * tsp,uint64_t local,uint64_t * refp)134 int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp,
135 				 uint64_t local, uint64_t *refp)
136 {
137 	int rv = -EINVAL;
138 
139 	if ((tsp->skew > 0) && (tsp->base.ref > 0) && (refp != NULL)) {
140 		const struct timeutil_sync_config *cfg = tsp->cfg;
141 		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 		if (tsp->skew != 1.0f) {
147 			local_delta *= (double)tsp->skew;
148 		}
149 #endif /* CONFIG_TIMEUTIL_APPLY_SKEW */
150 		int64_t ref_delta = local_delta * cfg->ref_Hz / cfg->local_Hz;
151 		int64_t ref_abs = (int64_t)tsp->base.ref + ref_delta;
152 
153 		if (ref_abs < 0) {
154 			rv = -ERANGE;
155 		} else {
156 			*refp = ref_abs;
157 			rv = (tsp->skew != 1.0f) ? 1 : 0;
158 		}
159 	}
160 
161 	return rv;
162 }
163 
timeutil_sync_local_from_ref(const struct timeutil_sync_state * tsp,uint64_t ref,int64_t * localp)164 int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp,
165 				 uint64_t ref, int64_t *localp)
166 {
167 	int rv = -EINVAL;
168 
169 	if ((tsp->skew > 0) && (tsp->base.ref > 0) && (localp != NULL)) {
170 		const struct timeutil_sync_config *cfg = tsp->cfg;
171 		int64_t ref_delta = (int64_t)(ref - tsp->base.ref);
172 		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 		if (tsp->skew != 1.0f) {
178 			local_delta /= (double)tsp->skew;
179 		}
180 #endif /* CONFIG_TIMEUTIL_APPLY_SKEW */
181 		int64_t local_abs = (int64_t)tsp->base.local
182 				    + (int64_t)local_delta;
183 
184 		*localp = local_abs;
185 		rv = (tsp->skew != 1.0f) ? 1 : 0;
186 	}
187 
188 	return rv;
189 }
190 
timeutil_sync_skew_to_ppb(float skew)191 int32_t timeutil_sync_skew_to_ppb(float skew)
192 {
193 	int64_t ppb64 = (int64_t)((1.0 - (double)skew) * 1E9);
194 	int32_t ppb32 = (int32_t)ppb64;
195 
196 	return (ppb64 == ppb32) ? ppb32 : INT32_MIN;
197 }
198 
timespec_normalize(struct timespec * ts)199 bool timespec_normalize(struct timespec *ts)
200 {
201 	__ASSERT_NO_MSG(ts != NULL);
202 
203 	long sec;
204 
205 	if (ts->tv_nsec >= (long)NSEC_PER_SEC) {
206 		sec = ts->tv_nsec / (long)NSEC_PER_SEC;
207 	} else if (ts->tv_nsec < 0) {
208 		sec = DIV_ROUND_UP((unsigned long)-ts->tv_nsec, NSEC_PER_SEC);
209 	} else {
210 		sec = 0;
211 	}
212 
213 	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 	if ((ts->tv_nsec >= (long)NSEC_PER_SEC) && (ts->tv_sec > 0) &&
222 	    (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 	if (ts->tv_nsec >= (long)NSEC_PER_SEC) {
231 		ts->tv_sec += sec;
232 		ts->tv_nsec -= sec * (long)NSEC_PER_SEC;
233 	} else if (ts->tv_nsec < 0) {
234 		ts->tv_sec -= sec;
235 		ts->tv_nsec += sec * (long)NSEC_PER_SEC;
236 	} else {
237 		/* no change: SonarQube was complaining */
238 	}
239 
240 	__ASSERT_NO_MSG(timespec_is_valid(ts));
241 
242 	return true;
243 }
244