Skip to content

Commit

Permalink
Clean-up time conversions on the way to fix #30990 (#31021)
Browse files Browse the repository at this point in the history
* Clean-up time conversions locations

- Test conversions in Time Sync cluster were not in a unit-testable
  location.
- TestTimeUtils was not using nl-unit-test
- Time sync cluster server build rules were missing dependencies
- Documentation for many time conversion methods was not accurate
- `secondsToMilliseconds` did not match coding style
- Cleaned-up cut and paste errors in some adjacent tests.

Issue #30990

Testing done:
- Unit tests and integration tests still pass.
- Started tests for edge conditions (which found #30990)

* Restyled by clang-format

* Add backwards compatibility

---------

Co-authored-by: [email protected] <[email protected]>
Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
3 people authored Dec 15, 2023
1 parent 96970df commit 93be827
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 83 deletions.
1 change: 1 addition & 0 deletions src/app/chip_data_model.gni
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ template("chip_data_model") {
} else if (cluster == "time-synchronization-server") {
sources += [
"${_app_root}/clusters/${cluster}/${cluster}.cpp",
"${_app_root}/clusters/${cluster}/${cluster}.h",
"${_app_root}/clusters/${cluster}/DefaultTimeSyncDelegate.cpp",
"${_app_root}/clusters/${cluster}/TimeSyncDataProvider.cpp",
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,33 +122,11 @@ Delegate * GetDefaultDelegate()
} // namespace app
} // namespace chip

constexpr uint64_t kChipEpochUsSinceUnixEpoch =
static_cast<uint64_t>(kChipEpochSecondsSinceUnixEpoch) * chip::kMicrosecondsPerSecond;

static bool ChipEpochToUnixEpochMicro(uint64_t chipEpochTime, uint64_t & unixEpochTime)
{
// in case chipEpochTime is too big and result overflows return false
if (chipEpochTime + kChipEpochUsSinceUnixEpoch < kChipEpochUsSinceUnixEpoch)
{
return false;
}
unixEpochTime = chipEpochTime + kChipEpochUsSinceUnixEpoch;
return true;
}

static bool UnixEpochToChipEpochMicro(uint64_t unixEpochTime, uint64_t & chipEpochTime)
{
VerifyOrReturnValue(unixEpochTime >= kChipEpochUsSinceUnixEpoch, false);
chipEpochTime = unixEpochTime - kChipEpochUsSinceUnixEpoch;

return true;
}

static CHIP_ERROR UpdateUTCTime(uint64_t UTCTimeInChipEpochUs)
{
uint64_t UTCTimeInUnixEpochUs;

VerifyOrReturnError(ChipEpochToUnixEpochMicro(UTCTimeInChipEpochUs, UTCTimeInUnixEpochUs), CHIP_ERROR_INVALID_TIME);
VerifyOrReturnError(ChipEpochToUnixEpochMicros(UTCTimeInChipEpochUs, UTCTimeInUnixEpochUs), CHIP_ERROR_INVALID_TIME);
uint64_t secs = UTCTimeInChipEpochUs / chip::kMicrosecondsPerSecond;
// https://github.com/project-chip/connectedhomeip/issues/27501
VerifyOrReturnError(secs <= UINT32_MAX, CHIP_IM_GLOBAL_STATUS(ResourceExhausted));
Expand Down Expand Up @@ -820,7 +798,7 @@ CHIP_ERROR TimeSynchronizationServer::GetLocalTime(EndpointId ep, DataModel::Nul
TimeState newState = UpdateDSTOffsetState();
VerifyOrReturnError(TimeState::kInvalid != newState, CHIP_ERROR_INVALID_TIME);
ReturnErrorOnFailure(System::SystemClock().GetClock_RealTime(utcTime));
VerifyOrReturnError(UnixEpochToChipEpochMicro(utcTime.count(), chipEpochTime), CHIP_ERROR_INVALID_TIME);
VerifyOrReturnError(UnixEpochToChipEpochMicros(utcTime.count(), chipEpochTime), CHIP_ERROR_INVALID_TIME);
if (TimeState::kChanged == UpdateTimeZoneState())
{
emitTimeZoneStatusEvent(ep);
Expand Down Expand Up @@ -863,7 +841,7 @@ TimeState TimeSynchronizationServer::UpdateTimeZoneState()

VerifyOrReturnValue(System::SystemClock().GetClock_RealTime(utcTime) == CHIP_NO_ERROR, TimeState::kInvalid);
VerifyOrReturnValue(tzList.size() != 0, TimeState::kInvalid);
VerifyOrReturnValue(UnixEpochToChipEpochMicro(utcTime.count(), chipEpochTime), TimeState::kInvalid);
VerifyOrReturnValue(UnixEpochToChipEpochMicros(utcTime.count(), chipEpochTime), TimeState::kInvalid);

for (size_t i = 0; i < tzList.size(); i++)
{
Expand Down Expand Up @@ -902,7 +880,7 @@ TimeState TimeSynchronizationServer::UpdateDSTOffsetState()

VerifyOrReturnValue(System::SystemClock().GetClock_RealTime(utcTime) == CHIP_NO_ERROR, TimeState::kInvalid);
VerifyOrReturnValue(dstList.size() != 0, TimeState::kInvalid);
VerifyOrReturnValue(UnixEpochToChipEpochMicro(utcTime.count(), chipEpochTime), TimeState::kInvalid);
VerifyOrReturnValue(UnixEpochToChipEpochMicros(utcTime.count(), chipEpochTime), TimeState::kInvalid);

for (size_t i = 0; i < dstList.size(); i++)
{
Expand Down Expand Up @@ -1066,7 +1044,7 @@ CHIP_ERROR TimeSynchronizationAttrAccess::Read(const ConcreteReadAttributePath &
return aEncoder.EncodeNull();
}
VerifyOrReturnError(System::SystemClock().GetClock_RealTime(utcTimeUnix) == CHIP_NO_ERROR, aEncoder.EncodeNull());
VerifyOrReturnError(UnixEpochToChipEpochMicro(utcTimeUnix.count(), chipEpochTime), aEncoder.EncodeNull());
VerifyOrReturnError(UnixEpochToChipEpochMicros(utcTimeUnix.count(), chipEpochTime), aEncoder.EncodeNull());
return aEncoder.Encode(chipEpochTime);
}
case Granularity::Id: {
Expand Down
6 changes: 3 additions & 3 deletions src/app/tests/TestICDManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class TestICDManager
*
* @param time_ms: Value in milliseconds.
*/
static void AdvanceClockAndRunEventLoop(TestContext * ctx, uint32_t time_ms)
static void AdvanceClockAndRunEventLoop(TestContext * ctx, uint64_t time_ms)
{
ctx->mMockClock.AdvanceMonotonic(System::Clock::Timeout(time_ms));
ctx->GetIOContext().DriveIO();
Expand All @@ -141,13 +141,13 @@ class TestICDManager

// After the init we should be in Idle mode
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
AdvanceClockAndRunEventLoop(ctx, secondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
AdvanceClockAndRunEventLoop(ctx, SecondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
// Idle mode interval expired, ICDManager transitioned to the ActiveMode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDurationMs() + 1);
// Active mode interval expired, ICDManager transitioned to the IdleMode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
AdvanceClockAndRunEventLoop(ctx, secondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
AdvanceClockAndRunEventLoop(ctx, SecondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
// Idle mode interval expired, ICDManager transitioned to the ActiveMode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);

Expand Down
2 changes: 1 addition & 1 deletion src/ble/tests/TestBleErrorStr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ int TestBleErrorStr()
// clang-format off
nlTestSuite theSuite =
{
"Ble-Error-Strings",
"Test BLE range error strings conversions",
&sTests[0],
nullptr,
nullptr
Expand Down
4 changes: 2 additions & 2 deletions src/controller/tests/TestReadChunking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ void TestReadChunking::TestDynamicEndpoint(nlTestSuite * apSuite, void * apConte
NL_TEST_ASSERT(apSuite, readCallback.mOnReportEnd);
}

chip::test_utils::SleepMillis(secondsToMilliseconds(2));
chip::test_utils::SleepMillis(SecondsToMilliseconds(2));

// Destroying the read client will terminate the subscription transaction.
ctx.DrainAndServiceIO();
Expand Down Expand Up @@ -1045,7 +1045,7 @@ void TestReadChunking::TestSetDirtyBetweenChunks(nlTestSuite * apSuite, void * a
}
}

chip::test_utils::SleepMillis(secondsToMilliseconds(3));
chip::test_utils::SleepMillis(SecondsToMilliseconds(3));

// Destroying the read client will terminate the subscription transaction.
ctx.DrainAndServiceIO();
Expand Down
2 changes: 1 addition & 1 deletion src/lib/core/tests/TestCHIPErrorStr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ int TestCHIPErrorStr()
// clang-format off
nlTestSuite theSuite =
{
"Core-Error-Strings",
"Test CHIP_ERROR string conversions",
&sTests[0],
nullptr,
nullptr
Expand Down
25 changes: 22 additions & 3 deletions src/lib/support/TimeUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,11 +563,30 @@ void ChipEpochToCalendarTime(uint32_t chipEpochTime, uint16_t & year, uint8_t &
dayOfMonth, hour, minute, second);
}

bool UnixEpochToChipEpochTime(uint32_t unixEpochTime, uint32_t & chipEpochTime)
bool ChipEpochToUnixEpochMicros(uint64_t chipEpochTimeMicros, uint64_t & outUnixEpochTimeMicros)
{
VerifyOrReturnError(unixEpochTime >= kChipEpochSecondsSinceUnixEpoch, false);
if ((chipEpochTimeMicros + kChipEpochUsSinceUnixEpoch) < kChipEpochUsSinceUnixEpoch)
{
return false;
}

outUnixEpochTimeMicros = chipEpochTimeMicros + kChipEpochUsSinceUnixEpoch;
return true;
}

bool UnixEpochToChipEpochMicros(uint64_t unixEpochTimeMicros, uint64_t & outChipEpochTimeMicros)
{
VerifyOrReturnValue(unixEpochTimeMicros >= kChipEpochUsSinceUnixEpoch, false);
outChipEpochTimeMicros = unixEpochTimeMicros - kChipEpochUsSinceUnixEpoch;

return true;
}

bool UnixEpochToChipEpochTime(uint32_t unixEpochTimeSeconds, uint32_t & outChipEpochTimeSeconds)
{
VerifyOrReturnError(unixEpochTimeSeconds >= kChipEpochSecondsSinceUnixEpoch, false);

chipEpochTime = unixEpochTime - kChipEpochSecondsSinceUnixEpoch;
outChipEpochTimeSeconds = unixEpochTimeSeconds - kChipEpochSecondsSinceUnixEpoch;

return true;
}
Expand Down
78 changes: 56 additions & 22 deletions src/lib/support/TimeUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,18 +111,22 @@ enum
kChipEpochSecondsSinceUnixEpoch = kChipEpochDaysSinceUnixEpoch * kSecondsPerDay,
};

extern bool IsLeapYear(uint16_t year);
extern uint8_t DaysInMonth(uint16_t year, uint8_t month);
extern uint8_t FirstWeekdayOfYear(uint16_t year);
extern void OrdinalDateToCalendarDate(uint16_t year, uint16_t dayOfYear, uint8_t & month, uint8_t & dayOfMonth);
extern void CalendarDateToOrdinalDate(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint16_t & dayOfYear);
extern bool CalendarDateToDaysSinceUnixEpoch(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint32_t & daysSinceEpoch);
extern bool DaysSinceUnixEpochToCalendarDate(uint32_t daysSinceEpoch, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth);
extern bool AdjustCalendarDate(uint16_t & year, uint8_t & month, uint8_t & dayOfMonth, int32_t relativeDays);
extern bool CalendarTimeToSecondsSinceUnixEpoch(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint8_t hour, uint8_t minute,
uint8_t second, uint32_t & secondsSinceEpoch);
extern void SecondsSinceUnixEpochToCalendarTime(uint32_t secondsSinceEpoch, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth,
uint8_t & hour, uint8_t & minute, uint8_t & second);
// Difference in microseconds between Unix epoch (Jan 1 1970 00:00:00) and CHIP Epoch (Jan 1 2000 00:00:00).
constexpr uint64_t kChipEpochUsSinceUnixEpoch =
static_cast<uint64_t>(kChipEpochSecondsSinceUnixEpoch) * chip::kMicrosecondsPerSecond;

bool IsLeapYear(uint16_t year);
uint8_t DaysInMonth(uint16_t year, uint8_t month);
uint8_t FirstWeekdayOfYear(uint16_t year);
void OrdinalDateToCalendarDate(uint16_t year, uint16_t dayOfYear, uint8_t & month, uint8_t & dayOfMonth);
void CalendarDateToOrdinalDate(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint16_t & dayOfYear);
bool CalendarDateToDaysSinceUnixEpoch(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint32_t & daysSinceEpoch);
bool DaysSinceUnixEpochToCalendarDate(uint32_t daysSinceEpoch, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth);
bool AdjustCalendarDate(uint16_t & year, uint8_t & month, uint8_t & dayOfMonth, int32_t relativeDays);
bool CalendarTimeToSecondsSinceUnixEpoch(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint8_t hour, uint8_t minute,
uint8_t second, uint32_t & secondsSinceEpoch);
void SecondsSinceUnixEpochToCalendarTime(uint32_t secondsSinceEpoch, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth,
uint8_t & hour, uint8_t & minute, uint8_t & second);

/**
* @brief Convert a calendar date and time to the number of seconds since CHIP Epoch (2000-01-01 00:00:00 UTC).
Expand All @@ -141,8 +145,8 @@ extern void SecondsSinceUnixEpochToCalendarTime(uint32_t secondsSinceEpoch, uint
* @return True if the date/time was converted successfully. False if the given year falls outside the
* representable range.
*/
extern bool CalendarToChipEpochTime(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint8_t hour, uint8_t minute, uint8_t second,
uint32_t & chipEpochTime);
bool CalendarToChipEpochTime(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint8_t hour, uint8_t minute, uint8_t second,
uint32_t & chipEpochTime);

/**
* @brief Convert the number of seconds since CHIP Epoch (2000-01-01 00:00:00 UTC) to a calendar date and time.
Expand All @@ -158,34 +162,64 @@ extern bool CalendarToChipEpochTime(uint16_t year, uint8_t month, uint8_t dayOfM
* @param minute Minute (0-59).
* @param second Second (0-59).
*/
extern void ChipEpochToCalendarTime(uint32_t chipEpochTime, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth, uint8_t & hour,
uint8_t & minute, uint8_t & second);
void ChipEpochToCalendarTime(uint32_t chipEpochTime, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth, uint8_t & hour,
uint8_t & minute, uint8_t & second);

/**
* @brief Convert the number of seconds since Unix Epoch (1970-01-01 00:00:00 UTC) to
* @brief Convert the number of seconds since Unix Epoch (1970-01-01 00:00:00 GMT TAI) to
* CHIP Epoch (2000-01-01 00:00:00 UTC).
*
* @details Input time values are limited to positive values up to 2^32-1. This limits the
* representable date range to the year 2135.
*
* @param unixEpochTime Number of seconds since 1970-01-01 00:00:00 UTC.
* @param chipEpochTime Number of seconds since 2000-01-01 00:00:00 UTC.
* @param[in] unixEpochTimeSeconds Number of seconds since 1970-01-01 00:00:00 GMT TAI.
* @param[out] outChipEpochTimeSeconds Number of seconds since 2000-01-01 00:00:00 UTC.
*
* @return True if the time was converted successfully. False if the given Unix epoch time
* falls outside the representable range.
*/
bool UnixEpochToChipEpochTime(uint32_t unixEpochTimeSeconds, uint32_t & outChipEpochTimeSeconds);

/**
* @brief Convert the number of microseconds since CHIP Epoch (2000-01-01 00:00:00 UTC) to
* Unix Epoch (1970-01-01 00:00:00 GMT TAI).
*
* @param[in] chipEpochTimeMicros Number of microseconds since 2000-01-01 00:00:00 UTC.
* @param[out] outUnixEpochTimeMicros Number of microseconds since 1970-01-01 00:00:00 GMT TAI.
*
* @return True if the time was converted successfully. False if the given CHIP epoch time
* falls outside the representable range.
*/
bool ChipEpochToUnixEpochMicros(uint64_t chipEpochTimeMicros, uint64_t & outUnixEpochTimeMicros);

/**
* @brief Convert the number of microseconds since Unix Epoch (1970-01-01 00:00:00 GMT TAI) to
* CHIP Epoch (2000-01-01 00:00:00 UTC).
*
* @param[in] unixEpochTimeMicros Number of microseconds since 1970-01-01 00:00:00 GMT TAI.
* @param[out] outChipEpochTimeMicros Number of microseconds since 2000-01-01 00:00:00 UTC.
*
* @return True if the time was converted successfully. False if the given Unix epoch time
* falls outside the representable range.
*/
extern bool UnixEpochToChipEpochTime(uint32_t unixEpochTime, uint32_t & chipEpochTime);
bool UnixEpochToChipEpochMicros(uint64_t unixEpochTimeMicros, uint64_t & outChipEpochTimeMicros);

/**
* @def secondsToMilliseconds
* @def SecondsToMilliseconds
*
* @brief
* Convert integer seconds to milliseconds.
*
*/
inline uint32_t secondsToMilliseconds(uint32_t seconds)
inline uint64_t SecondsToMilliseconds(uint32_t seconds)
{
return (seconds * kMillisecondsPerSecond);
}

// For backwards-compatibility of public API.
[[deprecated("Use SecondsToMilliseconds")]] inline uint64_t secondsToMilliseconds(uint32_t seconds)
{
return SecondsToMilliseconds(seconds);
}

} // namespace chip
2 changes: 1 addition & 1 deletion src/lib/support/tests/TestErrorStr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ int TestErrorStr()
// clang-format off
nlTestSuite theSuite =
{
"-Error-Strings",
"Test the ErrorStr primitives",
&sTests[0],
nullptr,
nullptr
Expand Down
Loading

0 comments on commit 93be827

Please sign in to comment.