From 0e4c5adeee93756a10b16dbec49a35a2fa11c288 Mon Sep 17 00:00:00 2001 From: Rutuja-Patil-Bosch Date: Wed, 3 Jun 2026 10:52:55 +0530 Subject: [PATCH 1/2] Follow up PR for https://github.com/eclipse-score/baselibs/pull/135 --- .../datetime_converter/datetime_converter.cpp | 52 ++++++----- .../datetime_converter_test.cpp | 93 +++++++++++++++++++ 2 files changed, 122 insertions(+), 23 deletions(-) diff --git a/score/datetime_converter/datetime_converter.cpp b/score/datetime_converter/datetime_converter.cpp index 0ef61cf43e..1b7a145342 100644 --- a/score/datetime_converter/datetime_converter.cpp +++ b/score/datetime_converter/datetime_converter.cpp @@ -17,6 +17,21 @@ namespace score namespace common { +std::int8_t daysInMonth(const std::int16_t year, const std::int8_t month) +{ + if (month == 2) + { + return yearIsLeap(year) ? 29 : 28; + } + + if ((month == 4) || (month == 6) || (month == 9) || (month == 11)) + { + return 30; + } + + return 31; +} + int16_t leapYearsSince1970(const int16_t year) { int16_t numOfLeapYears = (((year - 1969) / 4) - ((year - 1901) / 100)) + ((year - 1601) / 400); @@ -70,24 +85,7 @@ bool isValidDateTimeFormat(const std::shared_ptr dateTime) return false; } - if (dateTime->m_month == 2) - { - if (yearIsLeap(dateTime->m_year)) - { - return (dateTime->m_day <= 29); - } - else - { - return (dateTime->m_day <= 28); - } - } - - if (dateTime->m_month == 4 || dateTime->m_month == 6 || dateTime->m_month == 9 || dateTime->m_month == 11) - { - return (dateTime->m_day <= 30); - } - - return true; + return (dateTime->m_day <= daysInMonth(dateTime->m_year, dateTime->m_month)); }; bool dateTimeToEpoch(const std::shared_ptr dateTime, time_t* epoch) @@ -151,10 +149,6 @@ std::shared_ptr epochToDateTime(time_t epoch) time_t daysSum = (epoch / SECONDS_PER_DAY) + 1; if (before1970) { - if (yearIsLeap(dateTime->m_year) && ((daysSum * -1) < DAYS_UNTIL_MONTHS[1])) - { - --daysSum; - } daysSum = DAYS_PER_YEAR + daysSum - 1; if (yearIsLeap(dateTime->m_year)) { @@ -200,7 +194,7 @@ std::shared_ptr epochToDateTime(time_t epoch) } else { - if (daysSum >= DAYS_PER_YEAR) + if (daysSum > DAYS_PER_YEAR) { dateTime->m_year = dateTime->m_year + 1; daysSum = daysSum - DAYS_PER_YEAR; @@ -263,6 +257,18 @@ std::shared_ptr epochToDateTime(time_t epoch) dateTime->m_hour = dateTime->m_hour - HOURS_PER_DAY; dateTime->m_day++; } + + if (dateTime->m_day == daysInMonth(dateTime->m_year, dateTime->m_month) + 1) + { + dateTime->m_day = 1; + dateTime->m_month++; + + if (dateTime->m_month == 13) + { + dateTime->m_month = 1; + dateTime->m_year++; + } + } } if (isValidDateTimeFormat(dateTime)) diff --git a/score/datetime_converter/datetime_converter_test.cpp b/score/datetime_converter/datetime_converter_test.cpp index 9ea21212b2..1abbfe8fbb 100644 --- a/score/datetime_converter/datetime_converter_test.cpp +++ b/score/datetime_converter/datetime_converter_test.cpp @@ -453,6 +453,99 @@ TEST_F(DateTimeConverterTest, test_yearIsLeap) ASSERT_FALSE(score::common::yearIsLeap(2100)); } +TEST_F(DateTimeConverterTest, invalid_month_values) +{ + RecordProperty("Description", "Verify invalid month values are rejected."); + RecordProperty("TestType", "requirements-based"); + + auto dtt = std::make_shared(2020, 0, 10, 3, 4, 5); + ASSERT_FALSE(score::common::isValidDateTimeFormat(dtt)); + + dtt->m_month = 13; + ASSERT_FALSE(score::common::isValidDateTimeFormat(dtt)); + + dtt->m_month = -1; + ASSERT_FALSE(score::common::isValidDateTimeFormat(dtt)); +} + +TEST_F(DateTimeConverterTest, invalid_day_values) +{ + RecordProperty("Description", "Verify invalid day values are rejected."); + RecordProperty("TestType", "requirements-based"); + + auto dtt = std::make_shared(2020, 12, 34, 3, 4, 5); + ASSERT_FALSE(score::common::isValidDateTimeFormat(dtt)); + + dtt->m_day = -1; + ASSERT_FALSE(score::common::isValidDateTimeFormat(dtt)); +} + +TEST_F(DateTimeConverterTest, dateTimeToEpoch_false_isValidDateTimeFormat) +{ + RecordProperty("Description", "Verify dateTimeToEpoch returns false for invalid DateTime input."); + RecordProperty("TestType", "requirements-based"); + + time_t epoch = static_cast(0x7F7F7F7F); + + // Invalid: month = 13 -> isValidDateTimeFormat returns false + auto d = std::make_shared(2024, 13, 10, 12, 30, 45); + + ASSERT_FALSE(score::common::dateTimeToEpoch(d, &epoch)); + + ASSERT_EQ(epoch, static_cast(0x7F7F7F7F)); +} + +TEST_F(DateTimeConverterTest, EpochToDateTime_Post1970_NonLeapYear_YearRollover) +{ + RecordProperty("Description", + "Verify that a post-1970 epoch landing exactly on a non-leap year boundary rolls over to the next year correctly."); + RecordProperty("TestType", "requirements-based"); + + time_t epoch{}; + auto dateTimeOriginal = std::make_shared(1971, 1, 1, 0, 0, 0); + ASSERT_TRUE(score::common::dateTimeToEpoch(dateTimeOriginal, &epoch)); + + auto dateTimeConverted = score::common::epochToDateTime(epoch); + ASSERT_NE(dateTimeConverted, nullptr); + ASSERT_EQ(dateTimeConverted->m_year, 1971); + ASSERT_EQ(dateTimeConverted->m_month, 1); + ASSERT_EQ(dateTimeConverted->m_day, 1); + ASSERT_EQ(dateTimeConverted->m_hour, 0); + ASSERT_EQ(dateTimeConverted->m_minute, 0); + ASSERT_EQ(dateTimeConverted->m_second, 0); +} + +TEST_F(DateTimeConverterTest, EpochToDateTime_Pre1970_YearBoundary_DayOverflowRollover) +{ + RecordProperty("Description", + "Verify that pre-1970 conversion handles day overflow at year boundary by rolling month and year."); + RecordProperty("TestType", "requirements-based"); + + // 1960-01-01 00:00:00 epoch; this path normalizes an intermediate Dec 32 to Jan 1 of the next year. + const time_t epochAt1960Start = static_cast(-315619200); + + auto dateTimeConverted = score::common::epochToDateTime(epochAt1960Start); + ASSERT_NE(dateTimeConverted, nullptr); + EXPECT_EQ(dateTimeConverted->m_year, 1960); + EXPECT_EQ(dateTimeConverted->m_month, 1); + EXPECT_EQ(dateTimeConverted->m_day, 1); + EXPECT_EQ(dateTimeConverted->m_hour, 0); + EXPECT_EQ(dateTimeConverted->m_minute, 0); + EXPECT_EQ(dateTimeConverted->m_second, 0); +} + +TEST_F(DateTimeConverterTest, EpochToDateTime_Pre1800_ReturnsNull) +{ + RecordProperty("Description", + "Verify that epoch values mapping outside supported year range return nullptr."); + RecordProperty("TestType", "requirements-based"); + + // This epoch maps to year 10000, which is above the supported upper bound (9999). + const time_t epochOutOfRange = static_cast(253405000000); + + auto dateTimeConverted = score::common::epochToDateTime(epochOutOfRange); + EXPECT_EQ(dateTimeConverted, nullptr); +} } // namespace testing } // namespace platform } // namespace score From 3583d2284e58fa794941422656b60a7a298443be Mon Sep 17 00:00:00 2001 From: Patil Rutuja Milind Date: Mon, 15 Jun 2026 14:43:30 +0530 Subject: [PATCH 2/2] fixed boat findings --- score/datetime_converter/datetime_converter.cpp | 2 +- score/datetime_converter/datetime_converter_test.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/score/datetime_converter/datetime_converter.cpp b/score/datetime_converter/datetime_converter.cpp index 1b7a145342..1b246058b1 100644 --- a/score/datetime_converter/datetime_converter.cpp +++ b/score/datetime_converter/datetime_converter.cpp @@ -17,7 +17,7 @@ namespace score namespace common { -std::int8_t daysInMonth(const std::int16_t year, const std::int8_t month) +static std::int8_t daysInMonth(const std::int16_t year, const std::int8_t month) { if (month == 2) { diff --git a/score/datetime_converter/datetime_converter_test.cpp b/score/datetime_converter/datetime_converter_test.cpp index 1abbfe8fbb..05c751629a 100644 --- a/score/datetime_converter/datetime_converter_test.cpp +++ b/score/datetime_converter/datetime_converter_test.cpp @@ -534,10 +534,10 @@ TEST_F(DateTimeConverterTest, EpochToDateTime_Pre1970_YearBoundary_DayOverflowRo EXPECT_EQ(dateTimeConverted->m_second, 0); } -TEST_F(DateTimeConverterTest, EpochToDateTime_Pre1800_ReturnsNull) +TEST_F(DateTimeConverterTest, EpochToDateTime_OutOfRangeUpperBound_ReturnsNullptr) { RecordProperty("Description", - "Verify that epoch values mapping outside supported year range return nullptr."); + "Verify that epoch values mapping above the supported year range return nullptr."); RecordProperty("TestType", "requirements-based"); // This epoch maps to year 10000, which is above the supported upper bound (9999).