Branch data Line data Source code
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 : : */ 31 : 0 : static int64_t time_days_from_civil(int64_t y, 32 : : unsigned int m, 33 : : unsigned int d) 34 : : { 35 : 0 : y -= m <= 2; 36 : : 37 [ # # ]: 0 : int64_t era = ((y >= 0) ? y : (y - 399)) / 400; 38 : 0 : unsigned int yoe = y - era * 400; 39 [ # # ]: 0 : unsigned int doy = (153U * (m + ((m > 2) ? -3 : 9)) + 2U) / 5U + d; 40 : 0 : unsigned int doe = yoe * 365U + yoe / 4U - yoe / 100U + doy; 41 : : 42 : 0 : return era * 146097 + (time_t)doe - 719468; 43 : : } 44 : : 45 : 0 : int64_t timeutil_timegm64(const struct tm *tm) 46 : : { 47 : 0 : int64_t y = TIME_UTILS_BASE_YEAR + (int64_t)tm->tm_year; 48 : 0 : unsigned int m = tm->tm_mon + 1; 49 : 0 : unsigned int d = tm->tm_mday - 1; 50 : 0 : int64_t ndays = time_days_from_civil(y, m, d); 51 : 0 : int64_t time = tm->tm_sec; 52 : : 53 : 0 : time += 60LL * (tm->tm_min + 60LL * tm->tm_hour); 54 : 0 : time += 86400LL * ndays; 55 : : 56 : 0 : return time; 57 : : } 58 : : 59 : 0 : time_t timeutil_timegm(const struct tm *tm) 60 : : { 61 : 0 : int64_t time = timeutil_timegm64(tm); 62 : 0 : time_t rv = (time_t)time; 63 : : 64 : 0 : errno = 0; 65 : 0 : if ((sizeof(rv) == sizeof(int32_t)) 66 : : && ((time < (int64_t)INT32_MIN) 67 [ # # ]: 0 : || (time > (int64_t)INT32_MAX))) { 68 : 0 : errno = ERANGE; 69 : 0 : rv = -1; 70 : : } 71 : : 72 : 0 : return rv; 73 : : } 74 : : 75 : 0 : int timeutil_sync_state_update(struct timeutil_sync_state *tsp, 76 : : const struct timeutil_sync_instant *inst) 77 : : { 78 : 0 : int rv = -EINVAL; 79 : : 80 [ # # # # ]: 0 : if (((tsp->base.ref == 0) && (inst->ref > 0)) 81 [ # # ]: 0 : || ((inst->ref > tsp->base.ref) 82 [ # # ]: 0 : && (inst->local > tsp->base.local))) { 83 [ # # ]: 0 : if (tsp->base.ref == 0) { 84 : 0 : tsp->base = *inst; 85 : 0 : tsp->latest = (struct timeutil_sync_instant){}; 86 : 0 : tsp->skew = 1.0f; 87 : 0 : rv = 0; 88 : : } else { 89 : 0 : tsp->latest = *inst; 90 : 0 : rv = 1; 91 : : } 92 : : } 93 : : 94 : 0 : return rv; 95 : : } 96 : : 97 : 0 : int timeutil_sync_state_set_skew(struct timeutil_sync_state *tsp, float skew, 98 : : const struct timeutil_sync_instant *base) 99 : : { 100 : 0 : int rv = -EINVAL; 101 : : 102 [ # # ]: 0 : if (skew > 0) { 103 : 0 : tsp->skew = skew; 104 [ # # ]: 0 : if (base != NULL) { 105 : 0 : tsp->base = *base; 106 : 0 : tsp->latest = (struct timeutil_sync_instant){}; 107 : : } 108 : : rv = 0; 109 : : } 110 : : 111 : 0 : return rv; 112 : : } 113 : : 114 : 0 : float timeutil_sync_estimate_skew(const struct timeutil_sync_state *tsp) 115 : : { 116 : 0 : float rv = 0; 117 : : 118 [ # # # # ]: 0 : if ((tsp->base.ref != 0) && (tsp->latest.ref != 0) 119 [ # # ]: 0 : && (tsp->latest.local > tsp->base.local)) { 120 : 0 : const struct timeutil_sync_config *cfg = tsp->cfg; 121 : 0 : double ref_delta = tsp->latest.ref - tsp->base.ref; 122 : 0 : double local_delta = tsp->latest.local - tsp->base.local; 123 : : 124 : 0 : rv = ref_delta * cfg->local_Hz / local_delta / cfg->ref_Hz; 125 : : } 126 : : 127 : 0 : return rv; 128 : : } 129 : : 130 : 0 : int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp, 131 : : uint64_t local, uint64_t *refp) 132 : : { 133 : 0 : int rv = -EINVAL; 134 : : 135 [ # # # # : 0 : if ((tsp->skew > 0) && (tsp->base.ref > 0) && (refp != NULL)) { # # ] 136 : 0 : const struct timeutil_sync_config *cfg = tsp->cfg; 137 : 0 : 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 [ # # ]: 0 : if (tsp->skew != 1.0f) { 142 : 0 : local_delta *= (double)tsp->skew; 143 : : } 144 : 0 : int64_t ref_delta = local_delta * cfg->ref_Hz / cfg->local_Hz; 145 : 0 : int64_t ref_abs = (int64_t)tsp->base.ref + ref_delta; 146 : : 147 [ # # ]: 0 : if (ref_abs < 0) { 148 : : rv = -ERANGE; 149 : : } else { 150 : 0 : *refp = ref_abs; 151 : 0 : rv = (tsp->skew != 1.0f) ? 1 : 0; 152 : : } 153 : : } 154 : : 155 : 0 : return rv; 156 : : } 157 : : 158 : 0 : int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp, 159 : : uint64_t ref, int64_t *localp) 160 : : { 161 : 0 : int rv = -EINVAL; 162 : : 163 [ # # # # : 0 : if ((tsp->skew > 0) && (tsp->base.ref > 0) && (localp != NULL)) { # # ] 164 : 0 : const struct timeutil_sync_config *cfg = tsp->cfg; 165 : 0 : 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 : 0 : int64_t local_delta = (ref_delta * cfg->local_Hz) / cfg->ref_Hz; 170 : : 171 [ # # ]: 0 : if (tsp->skew != 1.0f) { 172 : 0 : local_delta /= (double)tsp->skew; 173 : : } 174 : 0 : int64_t local_abs = (int64_t)tsp->base.local 175 : : + (int64_t)local_delta; 176 : : 177 : 0 : *localp = local_abs; 178 : 0 : rv = (tsp->skew != 1.0f) ? 1 : 0; 179 : : } 180 : : 181 : 0 : return rv; 182 : : } 183 : : 184 : 0 : int32_t timeutil_sync_skew_to_ppb(float skew) 185 : : { 186 : 0 : int64_t ppb64 = (int64_t)((1.0 - (double)skew) * 1E9); 187 : 0 : int32_t ppb32 = (int32_t)ppb64; 188 : : 189 [ # # ]: 0 : return (ppb64 == ppb32) ? ppb32 : INT32_MIN; 190 : : }