Skip to content

File espos_time_policy.h

File List > espos_time > include > espos_time_policy.h

Go to the documentation of this file

/*
 * SPDX-FileCopyrightText: 2026 Dirk Wahrheit
 * SPDX-License-Identifier: Apache-2.0
 *
 * espos_time policy — the clock's decisions as pure C.
 *
 * Three things about a wall clock are worth getting right and none of them
 * need a platform: which source is allowed to overwrite which (ranking), when
 * a time carried through a deep sleep has aged past usefulness (staleness),
 * and how an instant is written down (ISO 8601 UTC with milliseconds). All
 * three live here, driven by an injected monotonic clock, so a host test can
 * step time forward by a day without sleeping — the same shape as
 * espos_wifi_sm, espos_sk_token_sm and espos_health_policy.
 *
 * The instant itself is kept as an offset: `epoch_at_zero_ms` is what the wall
 * clock read when the monotonic counter was zero. Reading the time is then one
 * addition, and — the point of the exercise — a time learned late still dates
 * an event that happened early, because the offset applies to every monotonic
 * stamp ever taken, not only to the ones after the sync.
 *
 * Threading: none. The caller (espos_time.c) serialises access under its own
 * mutex; nothing here allocates, blocks or calls out.
 */
#pragma once

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"

#include "espos_time.h"

#ifdef __cplusplus
extern "C" {
#endif

/* Everything the policy needs from the outside world. */
typedef struct {
    /* Monotonic milliseconds since boot. 64-bit on purpose: this one must not
     * wrap, because it is the base every wall-clock reading is built on. */
    int64_t (*now_ms)(void *ctx);
} espos_time_policy_port_t;

typedef struct espos_time_policy {
    const espos_time_policy_port_t *port;
    void *ctx;
    espos_time_src_t src;      /* NONE until something sets the clock */
    int64_t epoch_at_zero_ms;  /* unix ms the monotonic counter's zero corresponds to */
    int64_t set_at_mono_ms;    /* when the current source last set it */
    int64_t prior_age_ms;      /* age the value already had when it was adopted (a deep sleep) */
    uint32_t stale_after_h;    /* an RTC time older than this is not "synced"; 0 = never stale */
    uint32_t sets;             /* how often a source set the clock, for the status document */
} espos_time_policy_t;

void espos_time_policy_init(espos_time_policy_t *p, const espos_time_policy_port_t *port, void *ctx,
                            uint32_t stale_after_h);

esp_err_t espos_time_policy_set(espos_time_policy_t *p, int64_t unix_ms, espos_time_src_t src);

esp_err_t espos_time_policy_set_aged(espos_time_policy_t *p, int64_t unix_ms, espos_time_src_t src,
                                     int64_t prior_age_ms);

int64_t espos_time_policy_now_ms(const espos_time_policy_t *p);

int64_t espos_time_policy_at_ms(const espos_time_policy_t *p, int64_t mono_ms);

bool espos_time_policy_is_synced(const espos_time_policy_t *p);

size_t espos_time_iso8601_format(int64_t unix_ms, char *buf, size_t n);

int64_t espos_time_iso8601_parse(const char *s);

#ifdef __cplusplus
}
#endif