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 <zephyr/types.h>
14 #include <errno.h>
15 #include <stddef.h>
16 #include <stdbool.h>
17 #include <zephyr/sys/timeutil.h>
18 
19 /** Convert a civil (proleptic Gregorian) date to days relative to
20  * 1970-01-01.
21  *
22  * @param y the calendar year
23  * @param m the calendar month, in the range [1, 12]
24  * @param d the day of the month, in the range [1, last_day_of_month(y, m)]
25  *
26  * @return the signed number of days between the specified day and
27  * 1970-01-01
28  *
29  * @see http://howardhinnant.github.io/date_algorithms.html#days_from_civil
30  */
time_days_from_civil(int64_t y,unsigned int m,unsigned int d)31 static int64_t time_days_from_civil(int64_t y,
32 				    unsigned int m,
33 				    unsigned int d)
34 {
35 	y -= m <= 2;
36 
37 	int64_t era = (y >= 0 ? y : y - 399) / 400;
38 	unsigned int yoe = y - era * 400;
39 	unsigned int doy = (153U * (m + (m > 2 ? -3 : 9)) + 2U) / 5U + d;
40 	unsigned int doe = yoe * 365U + yoe / 4U - yoe / 100U + doy;
41 
42 	return era * 146097 + (time_t)doe - 719468;
43 }
44 
timeutil_timegm64(const struct tm * tm)45 int64_t timeutil_timegm64(const struct tm *tm)
46 {
47 	int64_t y = 1900 + (int64_t)tm->tm_year;
48 	unsigned int m = tm->tm_mon + 1;
49 	unsigned int d = tm->tm_mday - 1;
50 	int64_t ndays = time_days_from_civil(y, m, d);
51 	int64_t time = tm->tm_sec;
52 
53 	time += 60LL * (tm->tm_min + 60LL * tm->tm_hour);
54 	time += 86400LL * ndays;
55 
56 	return time;
57 }
58 
timeutil_timegm(const struct tm * tm)59 time_t timeutil_timegm(const struct tm *tm)
60 {
61 	int64_t time = timeutil_timegm64(tm);
62 	time_t rv = (time_t)time;
63 
64 	errno = 0;
65 	if ((sizeof(rv) == sizeof(int32_t))
66 	    && ((time < (int64_t)INT32_MIN)
67 		|| (time > (int64_t)INT32_MAX))) {
68 		errno = ERANGE;
69 		rv = -1;
70 	}
71 
72 	return rv;
73 }
74 
timeutil_sync_state_update(struct timeutil_sync_state * tsp,const struct timeutil_sync_instant * inst)75 int timeutil_sync_state_update(struct timeutil_sync_state *tsp,
76 			       const struct timeutil_sync_instant *inst)
77 {
78 	int rv = -EINVAL;
79 
80 	if (((tsp->base.ref == 0) && (inst->ref > 0))
81 	    || ((inst->ref > tsp->base.ref)
82 		&& (inst->local > tsp->base.local))) {
83 		if (tsp->base.ref == 0) {
84 			tsp->base = *inst;
85 			tsp->latest = (struct timeutil_sync_instant){};
86 			tsp->skew = 1.0f;
87 			rv = 0;
88 		} else {
89 			tsp->latest = *inst;
90 			rv = 1;
91 		}
92 	}
93 
94 	return rv;
95 }
96 
timeutil_sync_state_set_skew(struct timeutil_sync_state * tsp,float skew,const struct timeutil_sync_instant * base)97 int timeutil_sync_state_set_skew(struct timeutil_sync_state *tsp, float skew,
98 				 const struct timeutil_sync_instant *base)
99 {
100 	int rv = -EINVAL;
101 
102 	if (skew > 0) {
103 		tsp->skew = skew;
104 		if (base != NULL) {
105 			tsp->base = *base;
106 			tsp->latest = (struct timeutil_sync_instant){};
107 		}
108 		rv = 0;
109 	}
110 
111 	return rv;
112 }
113 
timeutil_sync_estimate_skew(const struct timeutil_sync_state * tsp)114 float timeutil_sync_estimate_skew(const struct timeutil_sync_state *tsp)
115 {
116 	float rv = 0;
117 
118 	if ((tsp->base.ref != 0) && (tsp->latest.ref != 0)
119 	    && (tsp->latest.local > tsp->base.local)) {
120 		const struct timeutil_sync_config *cfg = tsp->cfg;
121 		double ref_delta = tsp->latest.ref - tsp->base.ref;
122 		double local_delta = tsp->latest.local - tsp->base.local;
123 
124 		rv = ref_delta * cfg->local_Hz / local_delta / cfg->ref_Hz;
125 	}
126 
127 	return rv;
128 }
129 
timeutil_sync_ref_from_local(const struct timeutil_sync_state * tsp,uint64_t local,uint64_t * refp)130 int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp,
131 				 uint64_t local, uint64_t *refp)
132 {
133 	int rv = -EINVAL;
134 
135 	if ((tsp->skew > 0) && (tsp->base.ref > 0) && (refp != NULL)) {
136 		const struct timeutil_sync_config *cfg = tsp->cfg;
137 		int64_t local_delta = local - tsp->base.local;
138 		/* (x * 1.0) != x for large values of x.
139 		 * Therefore only apply the multiplication if the skew is not one.
140 		 */
141 		if (tsp->skew != 1.0f) {
142 			local_delta *= (double)tsp->skew;
143 		}
144 		int64_t ref_delta = local_delta * cfg->ref_Hz / cfg->local_Hz;
145 		int64_t ref_abs = (int64_t)tsp->base.ref + ref_delta;
146 
147 		if (ref_abs < 0) {
148 			rv = -ERANGE;
149 		} else {
150 			*refp = ref_abs;
151 			rv = (int)(tsp->skew != 1.0f);
152 		}
153 	}
154 
155 	return rv;
156 }
157 
timeutil_sync_local_from_ref(const struct timeutil_sync_state * tsp,uint64_t ref,int64_t * localp)158 int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp,
159 				 uint64_t ref, int64_t *localp)
160 {
161 	int rv = -EINVAL;
162 
163 	if ((tsp->skew > 0) && (tsp->base.ref > 0) && (localp != NULL)) {
164 		const struct timeutil_sync_config *cfg = tsp->cfg;
165 		int64_t ref_delta = (int64_t)(ref - tsp->base.ref);
166 		/* (x / 1.0) != x for large values of x.
167 		 * Therefore only apply the division if the skew is not one.
168 		 */
169 		int64_t local_delta = (ref_delta * cfg->local_Hz) / cfg->ref_Hz;
170 
171 		if (tsp->skew != 1.0f) {
172 			local_delta /= (double)tsp->skew;
173 		}
174 		int64_t local_abs = (int64_t)tsp->base.local
175 				    + (int64_t)local_delta;
176 
177 		*localp = local_abs;
178 		rv = (int)(tsp->skew != 1.0f);
179 	}
180 
181 	return rv;
182 }
183 
timeutil_sync_skew_to_ppb(float skew)184 int32_t timeutil_sync_skew_to_ppb(float skew)
185 {
186 	int64_t ppb64 = (int64_t)((1.0 - (double)skew) * 1E9);
187 	int32_t ppb32 = (int32_t)ppb64;
188 
189 	return (ppb64 == ppb32) ? ppb32 : INT32_MIN;
190 }
191