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