diff --git a/src/Utils.cpp b/src/Utils.cpp index bd540955..ee18d4e7 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -563,7 +563,9 @@ CString CUtils::FormatTime(const timeval& tv, const CString& sFormat, sFormat2 += sFormat.substr(uLastCopied, uFormatStart - uLastCopied); CString sVal = CString(iVal); - sFormat2 += CString(iDigits - sVal.length(), '0'); + if (static_cast(iDigits) > + sVal.length()) + sFormat2 += CString(iDigits - sVal.length(), '0'); sFormat2 += sVal; uLastCopied = i + 1; bInFormat = false; diff --git a/test/UtilsTest.cpp b/test/UtilsTest.cpp index 816ac786..607f72ad 100644 --- a/test/UtilsTest.cpp +++ b/test/UtilsTest.cpp @@ -307,4 +307,16 @@ TEST(UtilsTest, FormatTime) { // Test suffix CString str3 = CUtils::FormatTime(tv2, "a%fb", "UTC"); EXPECT_EQ(str3, "a123b"); + + // The zero-pad width in the %f handler is (iDigits - sVal.length()) + // computed in size_t. A zero-width %0f field, and a negative tv_usec + // (which ParseServerTime() hands back for a pre-1970 IRCv3 @time tag), + // both make sVal at least as long as iDigits and used to underflow that + // width, aborting via std::length_error. + EXPECT_EQ(CUtils::FormatTime(timeval{42, 123456}, "%s.%0f", "UTC"), "42.0"); + + timeval tvNeg; + tvNeg.tv_sec = 0; + tvNeg.tv_usec = -500000; + EXPECT_NO_THROW(CUtils::FormatTime(tvNeg, "[%H:%M:%S.%f]", "UTC")); }