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