diff --git a/CMakeLists.txt b/CMakeLists.txt index cc67eee..034c65d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ set(headers set(sources src/espchrono.cpp + src/espchrono_espimpl.cpp ) idf_component_register(INCLUDE_DIRS src SRCS ${headers} ${sources} REQUIRES freertos esp_system cpputils date espcpputils) diff --git a/src/espchrono.cpp b/src/espchrono.cpp index 95f0189..d831884 100644 --- a/src/espchrono.cpp +++ b/src/espchrono.cpp @@ -66,6 +66,11 @@ bool daylightSavingTime(local_clock::time_point _timeStamp) return (_tempDateTime.date.day() < 8_d && _tempDateTime.dayOfWeek == 1 && _tempDateTime.hour < 2); } // end else } +} // namespace + +auto local_clock::now() noexcept -> time_point +{ + return utcToLocal(utc_clock::now()); } local_clock::time_point utcToLocal(utc_clock::time_point utc, time_zone timezone) @@ -97,8 +102,13 @@ utc_clock::time_point localToUtc(local_clock::time_point local) return utc; } +local_clock::time_point utcToLocal(utc_clock::time_point ts) +{ + return utcToLocal(ts, local_clock::timezone()); +} + namespace { -DateTime toDateTime(seconds ts) +DateTime toDateTime(seconds32 ts) { auto _time = ts.count(); @@ -188,7 +198,7 @@ std::optional parseDateTime(std::string_view str) }; } -std::optional parseDaypoint(std::string_view str) +std::optional parseDaypoint(std::string_view str) { int8_t hour, minute, second{}; @@ -199,7 +209,7 @@ std::optional parseDaypoint(std::string_view str) if (hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) return std::nullopt; - return hours{hour} + minutes{minute} + seconds{second}; + return hours32{hour} + minutes32{minute} + seconds32{second}; } std::string toString(const DateTime &dateTime) @@ -217,7 +227,7 @@ std::string toString(const LocalDateTime &dateTime) { char buf[34]; - date::hh_mm_ss helper{dateTime.timezone.offset + hours{dateTime.dst ? 1 : 0}}; + date::hh_mm_ss helper{dateTime.timezone.offset + hours32{dateTime.dst ? 1 : 0}}; std::sprintf(buf, "%04i-%02u-%02uT%02hhu:%02hhu:%02hhu %s%02hhu:%02hhu", int{dateTime.date.year()}, unsigned{dateTime.date.month()}, unsigned{dateTime.date.day()}, @@ -227,7 +237,7 @@ std::string toString(const LocalDateTime &dateTime) return std::string{buf}; } -std::string toDaypointString(seconds seconds) +std::string toDaypointString(seconds32 seconds) { date::hh_mm_ss helper(seconds); char buf[10]; @@ -235,15 +245,15 @@ std::string toDaypointString(seconds seconds) return std::string{buf}; } -milliseconds ago(millis_clock::time_point a) +std::chrono::milliseconds ago(millis_clock::time_point a) { return millis_clock::now() - a; } -std::string toString(milliseconds val) { return std::to_string(val.count()) + "ms"; } -std::string toString(seconds val) { return std::to_string(val.count()) + "s"; } -std::string toString(minutes val) { return std::to_string(val.count()) + "min"; } -std::string toString(hours val) { return std::to_string(val.count()) + "h"; } +std::string toString(milliseconds32 val) { return std::to_string(val.count()) + "ms"; } +std::string toString(seconds32 val) { return std::to_string(val.count()) + "s"; } +std::string toString(minutes32 val) { return std::to_string(val.count()) + "min"; } +std::string toString(hours32 val) { return std::to_string(val.count()) + "h"; } IMPLEMENT_TYPESAFE_ENUM(DayLightSavingMode, : uint8_t, DayLightSavingModeValues) diff --git a/src/espchrono.h b/src/espchrono.h index ea71ed6..2b76673 100644 --- a/src/espchrono.h +++ b/src/espchrono.h @@ -17,14 +17,14 @@ namespace espchrono { -using milliseconds = std::chrono::duration; -using seconds = std::chrono::duration; -using minutes = std::chrono::duration>; -using hours = std::chrono::duration>; +using milliseconds32 = std::chrono::duration; +using seconds32 = std::chrono::duration; +using minutes32 = std::chrono::duration>; +using hours32 = std::chrono::duration>; struct utc_clock { - typedef seconds duration; + typedef seconds32 duration; typedef duration::rep rep; typedef duration::period period; typedef std::chrono::time_point time_point; @@ -45,7 +45,7 @@ DECLARE_TYPESAFE_ENUM(DayLightSavingMode, : uint8_t, DayLightSavingModeValues) struct time_zone { - minutes offset{}; + minutes32 offset{}; DayLightSavingMode dayLightSavingMode{}; bool operator== (const time_zone &other) const @@ -100,7 +100,7 @@ public: struct local_clock { - typedef seconds duration; + typedef seconds32 duration; typedef duration::rep rep; typedef duration::period period; typedef local_time_point time_point; @@ -111,6 +111,8 @@ struct local_clock static constexpr bool is_steady = false; static time_point now() noexcept; + + static time_zone timezone() noexcept; }; struct millis_clock @@ -162,6 +164,8 @@ struct LocalDateTime : public DateTime local_clock::time_point utcToLocal(utc_clock::time_point timeStamp, time_zone timezone); utc_clock::time_point localToUtc(local_clock::time_point local); +local_clock::time_point utcToLocal(utc_clock::time_point ts); + DateTime toDateTime(utc_clock::time_point ts); LocalDateTime toDateTime(local_clock::time_point ts); @@ -169,18 +173,18 @@ LocalDateTime toDateTime(local_clock::time_point ts); std::optional parseDateTime(std::string_view str); //! Returns null if string cannot be parsed -std::optional parseDaypoint(std::string_view str); +std::optional parseDaypoint(std::string_view str); std::string toString(const DateTime &dateTime); std::string toString(const LocalDateTime &dateTime); -std::string toDaypointString(seconds seconds); +std::string toDaypointString(seconds32 seconds); -milliseconds ago(millis_clock::time_point a); +std::chrono::milliseconds ago(millis_clock::time_point a); -std::string toString(milliseconds val); -std::string toString(seconds val); -std::string toString(minutes val); -std::string toString(hours val); +std::string toString(milliseconds32 val); +std::string toString(seconds32 val); +std::string toString(minutes32 val); +std::string toString(hours32 val); } // namespace espchrono diff --git a/src/espchrono_espimpl.cpp b/src/espchrono_espimpl.cpp new file mode 100644 index 0000000..1e89343 --- /dev/null +++ b/src/espchrono_espimpl.cpp @@ -0,0 +1,27 @@ +// local includes +#include "espchrono.h" + +// system includes +#include + +// esp-idf inlcludes +#include + +using namespace std::chrono_literals; + +// actual implementations used on the ESP32 + +namespace espchrono { +auto utc_clock::now() noexcept -> time_point +{ + timeval tv; + gettimeofday(&tv, NULL); + seconds32 seconds{tv.tv_sec}; + return time_point{seconds}; +} + +auto millis_clock::now() noexcept -> time_point +{ + return time_point{std::chrono::floor(std::chrono::microseconds{esp_timer_get_time()})}; +} +} // namespace espchrono diff --git a/test/espchronotestutils.cpp b/test/espchronotestutils.cpp index bcda972..b8a5d61 100644 --- a/test/espchronotestutils.cpp +++ b/test/espchronotestutils.cpp @@ -5,7 +5,8 @@ using namespace date; const espchrono::time_zone testTimeZone{.offset=60min, .dayLightSavingMode=espchrono::DayLightSavingMode::EuropeanSummerTime}; -espchrono::millis_clock::time_point wallClock{}; +espchrono::millis_clock::time_point mockedMillisClock{}; +espchrono::utc_clock::time_point mockedUtcClock{}; namespace QTest { template<> @@ -33,13 +34,13 @@ char *toString(const espchrono::local_clock::time_point &ts) } template<> -char *toString(const espchrono::seconds &val) +char *toString(const espchrono::seconds32 &val) { return ::QTest::toString(espchrono::toDaypointString(val)); } template<> -char *toString(const std::optional &val) +char *toString(const std::optional &val) { return val ? ::QTest::toString(*val) : ::QTest::toString("(invalid)"); } @@ -48,5 +49,13 @@ char *toString(const std::optional &val) // stub implementation to make unit tests happy auto espchrono::millis_clock::now() noexcept -> time_point { - return wallClock; + return mockedMillisClock; +} +auto espchrono::utc_clock::now() noexcept -> time_point +{ + return mockedUtcClock; +} +auto espchrono::local_clock::timezone() noexcept -> time_zone +{ + return testTimeZone; } diff --git a/test/espchronotestutils.h b/test/espchronotestutils.h index 0ad52bf..2086f0d 100644 --- a/test/espchronotestutils.h +++ b/test/espchronotestutils.h @@ -11,23 +11,28 @@ #include "espchrono.h" #include "cpputilstestutils.h" -Q_DECLARE_METATYPE(espchrono::milliseconds) -Q_DECLARE_METATYPE(espchrono::seconds) -Q_DECLARE_METATYPE(espchrono::minutes) -Q_DECLARE_METATYPE(espchrono::hours) +Q_DECLARE_METATYPE(std::chrono::milliseconds) +Q_DECLARE_METATYPE(std::chrono::seconds) +Q_DECLARE_METATYPE(std::chrono::minutes) +Q_DECLARE_METATYPE(std::chrono::hours) +Q_DECLARE_METATYPE(espchrono::milliseconds32) +Q_DECLARE_METATYPE(espchrono::seconds32) +Q_DECLARE_METATYPE(espchrono::minutes32) +Q_DECLARE_METATYPE(espchrono::hours32) Q_DECLARE_METATYPE(espchrono::utc_clock::time_point) Q_DECLARE_METATYPE(espchrono::local_clock::time_point) Q_DECLARE_METATYPE(espchrono::DateTime) Q_DECLARE_METATYPE(espchrono::LocalDateTime) -Q_DECLARE_METATYPE(std::optional) +Q_DECLARE_METATYPE(std::optional) extern const espchrono::time_zone testTimeZone; -extern espchrono::millis_clock::time_point wallClock; +extern espchrono::millis_clock::time_point mockedMillisClock; +extern espchrono::utc_clock::time_point mockedUtcClock; namespace { template -espchrono::local_clock::time_point makeLocal(T day, espchrono::minutes time) +espchrono::local_clock::time_point makeLocal(T day, espchrono::minutes32 time) { espchrono::local_clock::time_point localTime{date::sys_days{day}.time_since_epoch(), testTimeZone, false}; localTime += time; @@ -35,7 +40,7 @@ espchrono::local_clock::time_point makeLocal(T day, espchrono::minutes time) } template -espchrono::utc_clock::time_point makeUtcFromLocal(T day, espchrono::minutes time) +espchrono::utc_clock::time_point makeUtcFromLocal(T day, espchrono::minutes32 time) { const auto localTime = makeLocal(day, time); return espchrono::localToUtc(localTime); @@ -56,8 +61,8 @@ template<> char *toString(const espchrono::local_clock::time_point &ts); template<> -char *toString(const espchrono::seconds &val); +char *toString(const espchrono::seconds32 &val); template<> -char *toString(const std::optional &val); +char *toString(const std::optional &val); } // namespace QTest diff --git a/test/tst_espchrono.cpp b/test/tst_espchrono.cpp index 35997e8..86a768b 100644 --- a/test/tst_espchrono.cpp +++ b/test/tst_espchrono.cpp @@ -139,20 +139,20 @@ private slots: QTest::addColumn("time_point"); QTest::addColumn("expected"); - QTest::addRow("random") << espchrono::utc_clock::time_point{espchrono::seconds{123456}} << espchrono::DateTime{ + QTest::addRow("random") << espchrono::utc_clock::time_point{123456s} << espchrono::DateTime{ 2_d/January/1970, .hour=10, .minute=17, .second=36, .dayOfWeek=espchrono::DateTime::DayOfWeek::Friday }; - QTest::addRow("leap_year") << espchrono::utc_clock::time_point{espchrono::seconds{1582934400}} + QTest::addRow("leap_year") << espchrono::utc_clock::time_point{1582934400s} << espchrono::DateTime{ 29_d/February/2020, .hour=0, .minute=0, .second=0, .dayOfWeek=espchrono::DateTime::DayOfWeek::Saturday }; - QTest::addRow("normal_year") << espchrono::utc_clock::time_point{espchrono::seconds{1614556800}} + QTest::addRow("normal_year") << espchrono::utc_clock::time_point{1614556800s} << espchrono::DateTime{ 1_d/March/2021, .hour=0, .minute=0, .second=0, @@ -173,7 +173,7 @@ private slots: QTest::addColumn("time_point"); QTest::addColumn("expected"); - QTest::addRow("no_dst") << espchrono::local_clock::time_point(espchrono::seconds{123456}, testTimeZone, false) + QTest::addRow("no_dst") << espchrono::local_clock::time_point(123456s, testTimeZone, false) << espchrono::LocalDateTime{ espchrono::DateTime{ 2_d/January/1970, @@ -183,7 +183,7 @@ private slots: .timezone = testTimeZone, .dst = false }; - QTest::addRow("with_dst") << espchrono::local_clock::time_point(espchrono::seconds{123456}, testTimeZone, true) + QTest::addRow("with_dst") << espchrono::local_clock::time_point(123456s, testTimeZone, true) << espchrono::LocalDateTime{ espchrono::DateTime{ 2_d/January/1970, @@ -205,22 +205,22 @@ private slots: void test_toDaypointString_data() { - QTest::addColumn("input"); + QTest::addColumn("input"); QTest::addColumn("expected"); - QTest::addRow("00:00:00") << espchrono::seconds{} << "00:00:00"s; - QTest::addRow("05:00:00") << espchrono::seconds{5h} << "05:00:00"s; - QTest::addRow("05:04:00") << espchrono::seconds{5h+4min} << "05:04:00"s; - QTest::addRow("05:04:03") << espchrono::seconds{5h+4min+3s} << "05:04:03"s; - QTest::addRow("05:00:03") << espchrono::seconds{5h+3s} << "05:00:03"s; - QTest::addRow("23:59:59") << espchrono::seconds{23h+59min+59s} << "23:59:59"s; - QTest::addRow("-23:59:59") << espchrono::seconds{-23h-59min-59s} << "-23:59:59"s; - QTest::addRow("-00:59:59") << espchrono::seconds{-59min-59s} << "-00:59:59"s; + QTest::addRow("00:00:00") << espchrono::seconds32{} << "00:00:00"s; + QTest::addRow("05:00:00") << espchrono::seconds32{5h} << "05:00:00"s; + QTest::addRow("05:04:00") << espchrono::seconds32{5h+4min} << "05:04:00"s; + QTest::addRow("05:04:03") << espchrono::seconds32{5h+4min+3s} << "05:04:03"s; + QTest::addRow("05:00:03") << espchrono::seconds32{5h+3s} << "05:00:03"s; + QTest::addRow("23:59:59") << espchrono::seconds32{23h+59min+59s} << "23:59:59"s; + QTest::addRow("-23:59:59") << espchrono::seconds32{-23h-59min-59s} << "-23:59:59"s; + QTest::addRow("-00:59:59") << espchrono::seconds32{-59min-59s} << "-00:59:59"s; } void test_toDaypointString() { - QFETCH(espchrono::seconds, input); + QFETCH(espchrono::seconds32, input); QFETCH(std::string, expected); FIXEDCOMPARE(espchrono::toDaypointString(input), expected); } @@ -228,22 +228,22 @@ private slots: void test_parseDaypoint_data() { QTest::addColumn("input"); - QTest::addColumn>("expected"); + QTest::addColumn>("expected"); - QTest::addRow("bullshit") << "bullshit"s << std::optional{}; - QTest::addRow("missing_minute") << "00:"s << std::optional{}; - QTest::addRow("zero") << "00:00"s << std::optional{0s}; - QTest::addRow("zero3") << "00:00:00"s << std::optional{0s}; - QTest::addRow("random") << "12:34:56"s << std::optional{12h+34min+56s}; - QTest::addRow("random2") << "12:34"s << std::optional{12h+34min}; -// QTest::addRow("negative") << "-12:34:56"s << std::optional{-12h-34min-56s}; -// QTest::addRow("negative_leading_zero") << "-00:34:56"s << std::optional{-34min-56s}; + QTest::addRow("bullshit") << "bullshit"s << std::optional{}; + QTest::addRow("missing_minute") << "00:"s << std::optional{}; + QTest::addRow("zero") << "00:00"s << std::optional{0s}; + QTest::addRow("zero3") << "00:00:00"s << std::optional{0s}; + QTest::addRow("random") << "12:34:56"s << std::optional{12h+34min+56s}; + QTest::addRow("random2") << "12:34"s << std::optional{12h+34min}; +// QTest::addRow("negative") << "-12:34:56"s << std::optional{-12h-34min-56s}; +// QTest::addRow("negative_leading_zero") << "-00:34:56"s << std::optional{-34min-56s}; } void test_parseDaypoint() { QFETCH(std::string, input); - QFETCH(std::optional, expected); + QFETCH(std::optional, expected); FIXEDCOMPARE(espchrono::parseDaypoint(input), expected); } @@ -286,7 +286,7 @@ private slots: void test_compareLocalTimepoints() { espchrono::local_clock::time_point a { - espchrono::seconds{10}, + 10s, espchrono::time_zone{ .offset = 1h, .dayLightSavingMode = espchrono::DayLightSavingMode::EuropeanSummerTime @@ -294,7 +294,7 @@ private slots: false }; espchrono::local_clock::time_point b { - espchrono::seconds{10}, + 10s, espchrono::time_zone{ .offset = 1h, .dayLightSavingMode = espchrono::DayLightSavingMode::EuropeanSummerTime