From fad346a071323d3a9f5b595b966dabed4fc8c8ba Mon Sep 17 00:00:00 2001 From: Pierre Kancir Date: Tue, 25 Aug 2026 18:01:16 +0200 Subject: [PATCH] AP_Common: update ap_mktime --- libraries/AP_Common/tests/test_time.cpp | 25 ++++++++++ libraries/AP_Common/time.cpp | 62 +++++++++++++------------ 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/libraries/AP_Common/tests/test_time.cpp b/libraries/AP_Common/tests/test_time.cpp index 2abd3d4a6b1..56ab63dc92e 100644 --- a/libraries/AP_Common/tests/test_time.cpp +++ b/libraries/AP_Common/tests/test_time.cpp @@ -111,6 +111,31 @@ TEST(ap_mktime, MonthLoopRollover) struct tm t = make_tm(1970, 1, 1, 0, 0, 0); t.tm_mon = 12; // 13 iterations: Dec wraps m→0, y→1971, then Jan added EXPECT_EQ((time_t)(365 * 86400), ap_mktime(&t)); + + // tm_mon=14 is the largest a caller can produce: AP_Filesystem_FATFS + // reads a 4-bit month field and subtracts one, so 0x0f arrives here + // as 14. Fourteen months on from 1 January 1970 is 1 March 1971: + // all of 1970, then January and February. + t.tm_mon = 14; + EXPECT_EQ((time_t)((365 + 31 + 28) * 86400), ap_mktime(&t)); +} + +// Every caller computes tm_mon as "month - 1" from an external date +// field, so a source reporting month 0 arrives here as -1. Such a +// month must contribute nothing rather than index off the front of the +// month table. +TEST(ap_mktime, NegativeMonth) +{ + struct tm jan1 = make_tm(1970, 1, 1, 0, 0, 0); + struct tm neg = jan1; + neg.tm_mon = -1; + EXPECT_EQ(ap_mktime(&jan1), ap_mktime(&neg)); + + // and it must not disturb the rest of the fields + struct tm neg_later = make_tm(2024, 1, 15, 6, 7, 8); + neg_later.tm_mon = -1; + struct tm jan15 = make_tm(2024, 1, 15, 6, 7, 8); + EXPECT_EQ(ap_mktime(&jan15), ap_mktime(&neg_later)); } AP_GTEST_MAIN() diff --git a/libraries/AP_Common/time.cpp b/libraries/AP_Common/time.cpp index 81f81b0cfdc..35589de247b 100644 --- a/libraries/AP_Common/time.cpp +++ b/libraries/AP_Common/time.cpp @@ -1,44 +1,48 @@ #include "time.h" +#include + /* - mktime replacement from Samba + mktime replacement, originally from Samba */ time_t ap_mktime(const struct tm *t) { - time_t epoch = 0; - int n; - int mon [] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, y, m, i; - const unsigned MINUTE = 60; - const unsigned HOUR = 60*MINUTE; - const unsigned DAY = 24*HOUR; - const unsigned YEAR = 365*DAY; + // days elapsed from 1 January to the 1st of each month, common year + static const uint16_t cumdays[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; if (t->tm_year < 70) { return (time_t)-1; } - n = t->tm_year + 1900 - 1; - epoch = (t->tm_year - 70) * YEAR + - ((n / 4 - n / 100 + n / 400) - (1969 / 4 - 1969 / 100 + 1969 / 400)) * DAY; - - y = t->tm_year + 1900; - m = 0; - - for (i = 0; i < t->tm_mon; i++) { - epoch += mon [m] * DAY; - if (m == 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) { - epoch += DAY; - } - - if (++m > 11) { - m = 0; - y++; - } + /* + every caller derives tm_mon from an external date field as + "month - 1", so it can land outside 0-11 when the source reports + a zero or out-of-range month. Both cases are kept as the month + loop this replaced behaved: it walked its own month and year + counters forward, so months past December rolled into the + following years, and a negative month added nothing because the + loop simply never ran. + */ + int mon = t->tm_mon; + int year_off = t->tm_year; + if (mon > 11) { + year_off += mon / 12; + mon %= 12; + } else if (mon < 0) { + mon = 0; } - epoch += (t->tm_mday - 1) * DAY; - epoch += t->tm_hour * HOUR + t->tm_min * MINUTE + t->tm_sec; + const uint32_t year = (uint32_t)year_off + 1900U; + const uint32_t n = year - 1U; + const bool leap = (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); - return epoch; + // leap days since the epoch, as a closed form rather than a + // per-year loop; 477 is the same count taken at 1969 + const uint32_t days = (uint32_t)(year_off - 70) * 365U + + (n/4U - n/100U + n/400U) - 477U + + cumdays[mon] + (leap && mon > 1) + + (uint32_t)t->tm_mday - 1U; + + return (time_t)days * 86400 + t->tm_hour*3600 + t->tm_min*60 + t->tm_sec; } -