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