From 4485a90c0497d299b10ad97177da72a4e2b68da7 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Fri, 25 Apr 2025 19:21:41 +0100 Subject: [PATCH] Added etl::chrono::year_month --- include/etl/chrono.h | 2 + include/etl/private/chrono/hh_mm_ss.h | 2 + include/etl/private/chrono/year.h | 2 +- include/etl/private/chrono/year_month.h | 233 ++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/test_chrono_operators.cpp | 64 +++++++ test/test_chrono_year_month.cpp | 144 +++++++++++++++ test/vs2022/etl.vcxproj | 2 + test/vs2022/etl.vcxproj.filters | 6 + 9 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 include/etl/private/chrono/year_month.h create mode 100644 test/test_chrono_operators.cpp create mode 100644 test/test_chrono_year_month.cpp diff --git a/include/etl/chrono.h b/include/etl/chrono.h index 84674ea5..2d20db82 100644 --- a/include/etl/chrono.h +++ b/include/etl/chrono.h @@ -35,6 +35,7 @@ SOFTWARE. #include "platform.h" #include "type_traits.h" +#include "integral_limits.h" #if ETL_NOT_USING_CPP11 #error NOT SUPPORTED FOR C++03 OR BELOW @@ -67,6 +68,7 @@ namespace etl #include "private/chrono/month_day.h" #include "private/chrono/month_weekday.h" #include "private/chrono/year.h" +#include "private/chrono/year_month.h" #include "private/chrono/hh_mm_ss.h" #include "private/chrono/operators.h" #endif diff --git a/include/etl/private/chrono/hh_mm_ss.h b/include/etl/private/chrono/hh_mm_ss.h index b987c96d..90b9eeb7 100644 --- a/include/etl/private/chrono/hh_mm_ss.h +++ b/include/etl/private/chrono/hh_mm_ss.h @@ -60,6 +60,8 @@ namespace etl { public: + ETL_STATIC_ASSERT(etl::chrono::is_duration::value, "TDuration not etl::chrono::duration"); + //*********************************************************************** // Fractional width for the duration's subseconds //*********************************************************************** diff --git a/include/etl/private/chrono/year.h b/include/etl/private/chrono/year.h index 84306f78..df90770d 100644 --- a/include/etl/private/chrono/year.h +++ b/include/etl/private/chrono/year.h @@ -144,7 +144,7 @@ namespace etl //*********************************************************************** ETL_CONSTEXPR bool ok() const ETL_NOEXCEPT { - return (value != -32768); + return (value != etl::integral_limits::min); } //*********************************************************************** diff --git a/include/etl/private/chrono/year_month.h b/include/etl/private/chrono/year_month.h new file mode 100644 index 00000000..3c76033b --- /dev/null +++ b/include/etl/private/chrono/year_month.h @@ -0,0 +1,233 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2023 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_IN_CHRONO_H + #error DO NOT DIRECTLY INCLUDE THIS FILE. USE CHRONO.H +#endif + +namespace etl +{ + namespace chrono + { + class year_month + { + public: + + //************************************************************************* + /// Default constructor. + //************************************************************************* + year_month() + : y(etl::integral_limits::min) + , m(0) + { + } + + //************************************************************************* + /// Construct from month and day. + //************************************************************************* + ETL_CONSTEXPR year_month(const etl::chrono::year& y_, const etl::chrono::month& m_) ETL_NOEXCEPT + : y(y_) + , m(m_) + { + } + + //************************************************************************* + /// Returns the year. + //************************************************************************* + ETL_CONSTEXPR etl::chrono::year year() const ETL_NOEXCEPT + { + return y; + } + + //************************************************************************* + /// Returns the month. + //************************************************************************* + ETL_CONSTEXPR etl::chrono::month month() const ETL_NOEXCEPT + { + return m; + } + + //************************************************************************* + /// Returns true if the month/day is valid. + //************************************************************************* + ETL_CONSTEXPR14 bool ok() const ETL_NOEXCEPT + { + return y.ok() && m.ok(); + } + + //************************************************************************* + /// Adds etl::chrono::years + //************************************************************************* + friend ETL_CONSTEXPR14 etl::chrono::year_month operator +(const etl::chrono::year_month& ym, + const etl::chrono::years& dy) ETL_NOEXCEPT + { + return etl::chrono::year_month(ym.year() + dy, ym.month()); + } + + //************************************************************************* + /// Adds etl::chrono::years + //************************************************************************* + friend ETL_CONSTEXPR14 etl::chrono::year_month operator +(const etl::chrono::years& dy, + const etl::chrono::year_month& ym) ETL_NOEXCEPT + { + return etl::chrono::year_month(ym.year() + dy, ym.month()); + } + + //************************************************************************* + /// Adds etl::chrono::months + //************************************************************************* + friend ETL_CONSTEXPR14 etl::chrono::year_month operator +(const etl::chrono::year_month& ym, + const etl::chrono::months& dm) ETL_NOEXCEPT + { + return etl::chrono::year_month(ym.year(), ym.month() + dm); + } + + //************************************************************************* + /// Adds etl::chrono::months + //************************************************************************* + friend ETL_CONSTEXPR14 etl::chrono::year_month operator +(const etl::chrono::months& dm, + const etl::chrono::year_month& ym) ETL_NOEXCEPT + { + return etl::chrono::year_month(ym.year(), ym.month() + dm); + } + + //************************************************************************* + /// Subtracts etl::chrono::years + //************************************************************************* + friend ETL_CONSTEXPR14 etl::chrono::year_month operator -(const etl::chrono::year_month& ym, + const etl::chrono::years& dy) ETL_NOEXCEPT + { + return etl::chrono::year_month(ym.year() - dy, ym.month()); + } + + //************************************************************************* + /// Subtracts etl::chrono::months + //************************************************************************* + friend ETL_CONSTEXPR14 etl::chrono::year_month operator -(const etl::chrono::year_month& ym, + const etl::chrono::months& dm) ETL_NOEXCEPT + { + return etl::chrono::year_month(ym.year(), ym.month() - dm); + } + + //************************************************************************* + /// Subtracts etl::chrono::year_month + //************************************************************************* + friend ETL_CONSTEXPR14 etl::chrono::months operator -(const etl::chrono::year_month& ym1, + const etl::chrono::year_month& ym2) ETL_NOEXCEPT + { + return etl::chrono::months(static_cast(((int(ym1.year()) - int(ym2.year())) * 12) + (unsigned(ym1.month()) - unsigned(ym2.month())))); + } + + //************************************************************************* + /// Equality operator. + //************************************************************************* + friend ETL_CONSTEXPR bool operator ==(const etl::chrono::year_month& lhs, + const etl::chrono::year_month& rhs) ETL_NOEXCEPT + { + return (lhs.y == rhs.y) && (lhs.m == rhs.m); + } + + //************************************************************************* + /// Inequality operator. + //************************************************************************* + friend ETL_CONSTEXPR bool operator !=(const etl::chrono::year_month& lhs, + const etl::chrono::year_month& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //*********************************************************************** + /// Spaceship operator + //*********************************************************************** +#if ETL_USING_CPP20 + [[nodiscard]] friend constexpr auto operator <=>(const etl::chrono::year_month& lhs, + const etl::chrono::year_month& rhs) ETL_NOEXCEPT + { + auto cmp = lhs.year()<=> rhs.year(); + + if (cmp != 0) + { + return cmp; + } + else + { + return lhs.month() <=> rhs.month(); + } + } +#endif + + //*********************************************************************** + /// Compare year_month with another. + /// if month < other.month, returns -1; + /// else if month > other.month, returns 1; + /// else if day < other.day, returns -1; + /// else if day > other.day, returns 1; + /// else returns 0; + //*********************************************************************** + ETL_CONSTEXPR14 int compare(const year_month& other) const ETL_NOEXCEPT + { + if (y < other.y) return -1; + if (y > other.y) return 1; + if (m < other.m) return -1; + if (m > other.m) return 1; + + return 0; + } + + private: + + etl::chrono::year y; + etl::chrono::month m; + }; + } + + //************************************************************************* + /// Hash function for etl::chrono::year_month + //************************************************************************* +#if ETL_USING_8BIT_TYPES + template <> + struct hash + { + size_t operator()(const etl::chrono::year_month& md) const + { + uint8_t buffer[sizeof(unsigned int) + sizeof(unsigned int)]; + + unsigned int y = md.year(); + unsigned int m = md.month(); + + memcpy(buffer, &y, sizeof(y)); + memcpy(buffer + sizeof(y), &m, sizeof(m)); + + return etl::private_hash::generic_hash(buffer, buffer + sizeof(unsigned int) + sizeof(unsigned int)); + } + }; +#endif +} + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d5781a44..5729a1b7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -70,6 +70,7 @@ add_executable(etl_tests test_chrono_weekday_indexed.cpp test_chrono_weekday_last.cpp test_chrono_year.cpp + test_chrono_year_month.cpp # test_circular_buffer.cpp # test_circular_buffer_external_buffer.cpp # test_circular_iterator.cpp diff --git a/test/test_chrono_operators.cpp b/test/test_chrono_operators.cpp new file mode 100644 index 00000000..30e398d3 --- /dev/null +++ b/test/test_chrono_operators.cpp @@ -0,0 +1,64 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Documentation: + +Copyright(c) 2025 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "etl/platform.h" + +#include "unit_test_framework.h" + +#include "etl/chrono.h" + +#include +#include + +// Set to 0 to reference against std::chrono +#define ETL_USING_ETL_CHRONO 1 + +#if ETL_USING_ETL_CHRONO + #define Chrono etl::chrono +#else + #if ETL_USING_CPP20 + #include + #define Chrono std::chrono + #else + #error std::chrono not supported + #endif +#endif + +namespace +{ + SUITE(test_chrono_operators) + { + //************************************************************************* + TEST(test_operator_for_year_month) + { + + } + }; +} diff --git a/test/test_chrono_year_month.cpp b/test/test_chrono_year_month.cpp new file mode 100644 index 00000000..bd3a87f5 --- /dev/null +++ b/test/test_chrono_year_month.cpp @@ -0,0 +1,144 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Documentation: + +Copyright(c) 2024 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "etl/platform.h" + +#include "unit_test_framework.h" + +#include "etl/chrono.h" + +#include +#include + +// Set to 0 to reference against std::chrono +#define ETL_USING_ETL_CHRONO 1 + +#if ETL_USING_ETL_CHRONO + #define Chrono etl::chrono +#else + #if ETL_USING_CPP20 + #include + #define Chrono std::chrono + #else + #error std::chrono not supported + #endif +#endif + +namespace +{ + SUITE(test_chrono_year_month) + { + //************************************************************************* + TEST(test_default_constructor) + { + Chrono::year_month ym; + + CHECK_FALSE(ym.ok()); // Default-constructed year_month is not valid + } + + //************************************************************************* + TEST(test_constructor_with_month_and_day) + { + Chrono::year_month ym{Chrono::year(2000), Chrono::January}; + + CHECK_TRUE(ym.ok()); // Valid year_month + CHECK_EQUAL(2000, (int)ym.year()); + CHECK_EQUAL((unsigned)Chrono::January, (unsigned)ym.month()); + } + + //************************************************************************* + TEST(test_invalid_year_month) + { + Chrono::year_month ym{Chrono::year{32768}, Chrono::January}; // Invalid year + + CHECK_FALSE(ym.ok()); // Invalid year_month + } + + //************************************************************************* + TEST(test_invalid_month_in_year_month) + { + Chrono::year_month ym{Chrono::year{2000}, Chrono::month{13}}; // Invalid month (13) + + CHECK_FALSE(ym.ok()); // Invalid year_month + } + +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_year_month_spaceship_operator) + { + Chrono::year_month ym1{Chrono::year(2000), Chrono::January}; + Chrono::year_month ym2{Chrono::year(2001), Chrono::February}; + Chrono::year_month ym3{Chrono::year(2000), Chrono::January}; + + CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::equal); + CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less); + CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater); + } +#endif + +#if ETL_USING_ETL_CHRONO + //************************************************************************* + TEST(test_year_month_compare) + { + Chrono::year_month ym1{Chrono::year(2000), Chrono::January}; + Chrono::year_month ym2{Chrono::year(2001), Chrono::February}; + Chrono::year_month ym3{Chrono::year(2000), Chrono::January}; + + CHECK_TRUE(ym1.compare(ym3) == 0); + CHECK_TRUE(ym1.compare(ym2) == -1); + CHECK_TRUE(ym2.compare(ym1) == 1); + } +#endif + + //************************************************************************* + TEST(test_year_month_equality_operator) + { + Chrono::year_month ym1{Chrono::year(2000), Chrono::January}; + Chrono::year_month ym2{Chrono::year(2001), Chrono::February}; + Chrono::year_month ym3{Chrono::year(2000), Chrono::February}; + + CHECK_TRUE(ym1 == ym1); // 2000/January + CHECK_FALSE(ym1 == ym2); // 2000/January != 2001/February + CHECK_FALSE(ym1 == ym3); // 2000/January != 2000/February + } + + //************************************************************************* + TEST(test_year_month_not_equality_operator) + { + Chrono::year_month ym1{Chrono::year(2000), Chrono::January}; + Chrono::year_month ym2{Chrono::year(2001), Chrono::February}; + Chrono::year_month ym3{Chrono::year(2000), Chrono::February}; + + CHECK_FALSE(ym1 != ym1); // 2000/January + CHECK_TRUE(ym1 != ym2); // 2000/January != 2001/February + CHECK_TRUE(ym1 != ym3); // 2000/January != 2000/February + } + }; +} diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 90fa9853..af492705 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3197,6 +3197,7 @@ + @@ -8475,6 +8476,7 @@ + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 4f5baf9e..02a49851 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -1494,6 +1494,9 @@ ETL\Private\chrono + + ETL\Private\chrono + @@ -3545,6 +3548,9 @@ Tests\Syntax Checks\Source + + Tests\Chrono +