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