#pragma once #include "Model.h" #include namespace timbre_core { struct CivilDateTime { std::int32_t year = 1970; std::uint8_t month = 1; // 1..12 std::uint8_t day = 1; // 1..31 std::uint8_t hour = 0; // 0..23 std::uint8_t minute = 0; // 0..59 std::uint8_t second = 0; // 0..59 std::uint8_t weekday = 0; // 0 = Sunday, 6 = Saturday bool valid() const noexcept; std::uint16_t minuteOfDay() const noexcept; std::int64_t dateKey() const noexcept; std::int64_t minuteSerial() const noexcept; bool operator==(const CivilDateTime& other) const noexcept; bool operator!=(const CivilDateTime& other) const noexcept { return !(*this == other); } }; using CivilDateTimePoint = CivilDateTime; struct LocalMinuteKey { std::int64_t date = 0; // YYYYMMDD std::uint16_t minuteOfDay = 0; static LocalMinuteKey from(const CivilDateTime& value) noexcept { return LocalMinuteKey{value.dateKey(), value.minuteOfDay()}; } std::int64_t serial() const noexcept; std::int64_t dateKey() const noexcept { return date; } std::uint16_t minute() const noexcept { return minuteOfDay; } bool operator==(const LocalMinuteKey& other) const noexcept { return date == other.date && minuteOfDay == other.minuteOfDay; } bool operator!=(const LocalMinuteKey& other) const noexcept { return !(*this == other); } bool operator<(const LocalMinuteKey& other) const noexcept { return serial() < other.serial(); } bool operator>(const LocalMinuteKey& other) const noexcept { return serial() > other.serial(); } bool operator<=(const LocalMinuteKey& other) const noexcept { return serial() <= other.serial(); } bool operator>=(const LocalMinuteKey& other) const noexcept { return serial() >= other.serial(); } }; class CivilTime { public: // The product deliberately has one fixed civil offset: UTC-06:00. static constexpr std::int32_t kUtcOffsetSeconds = -6 * 60 * 60; static constexpr std::int32_t kUtcOffsetMinutes = -6 * 60; static constexpr std::int32_t UTC_OFFSET_SECONDS = kUtcOffsetSeconds; static constexpr std::int32_t UTC_OFFSET_MINUTES = kUtcOffsetMinutes; static bool isLeapYear(std::int32_t year) noexcept; static std::uint8_t daysInMonth(std::int32_t year, std::uint8_t month) noexcept; static bool isValidDate(std::int32_t year, std::uint8_t month, std::uint8_t day) noexcept; static std::uint8_t weekdayForDate(std::int32_t year, std::uint8_t month, std::uint8_t day) noexcept; static CivilDateTime fromUnixSeconds(std::int64_t utcSeconds) noexcept; static bool fromUnixSeconds(std::int64_t utcSeconds, CivilDateTime& result) noexcept; static CivilDateTime fromUnix(std::int64_t utcSeconds) noexcept { return fromUnixSeconds(utcSeconds); } static bool fromUnix(std::int64_t utcSeconds, CivilDateTime& result) noexcept { return fromUnixSeconds(utcSeconds, result); } static CivilDateTime fromUtc(std::int64_t utcSeconds) noexcept { return fromUnixSeconds(utcSeconds); } static CivilDateTime utcToLocal(std::int64_t utcSeconds) noexcept { return fromUnixSeconds(utcSeconds); } static CivilDateTime fromUnixMilliseconds(std::int64_t utcMilliseconds) noexcept; static std::int64_t toUnixSeconds(const CivilDateTime& localTime) noexcept; static bool toUnixSeconds(const CivilDateTime& localTime, std::int64_t& result) noexcept; static std::int64_t toUnixMilliseconds(const CivilDateTime& localTime) noexcept; static LocalMinuteKey minuteKey(const CivilDateTime& localTime) noexcept { return LocalMinuteKey::from(localTime); } // These helpers are public because they are useful to a scheduler and are // deterministic, allocation-free operations on an ESP32. static std::int64_t daysFromCivil(std::int32_t year, std::uint8_t month, std::uint8_t day) noexcept; static bool civilFromDays(std::int64_t days, std::int32_t& year, std::uint8_t& month, std::uint8_t& day) noexcept; }; } // namespace timbre_core