time: implement and test RFC1123 timestamp string
What changed, and why it matters
This commit adds a new helper function that converts a timestamp into the RFC1123 date format used in HTTP headers. It is purely additive: it introduces a formatting routine and unit tests, with no changes to existing behavior or network-facing code. There is no security issue visible in the change itself.
No security action required. Treat as normal code-quality/test addition. If this function is later used for HTTP header generation, ensure callers handle the empty-string out-of-range return value appropriately.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch implements FormatRFC1123DateTime(int64_t) in src/util/time.cpp and exposes it in src/util/time.h, plus adds BOOST_AUTO_TEST_CASE coverage in src/test/util_tests.cpp. The function bounds-checks the input to years 0-9999, computes weekday/month/day/time using std::chrono, and returns an empty string for out-of-range inputs. It does not modify any existing call sites, protocol logic, or parsing paths.
Changed components
src/util/time.cppsrc/util/time.hsrc/test/util_tests.cppInspect captured patch +43 / −0
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index daa5b8b3..eb6057a6 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -385,6 +385,21 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
BOOST_CHECK_EQUAL(FormatISO8601Date(1317425777), "2011-09-30");
}
+
+BOOST_AUTO_TEST_CASE(util_FormatRFC1123DateTime)
+{
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(std::numeric_limits<int64_t>::max()), "");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(253402300800), "");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(253402300799), "Fri, 31 Dec 9999 23:59:59 GMT");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(253402214400), "Fri, 31 Dec 9999 00:00:00 GMT");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(1717429609), "Mon, 03 Jun 2024 15:46:49 GMT");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(0), "Thu, 01 Jan 1970 00:00:00 GMT");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(-1), "Wed, 31 Dec 1969 23:59:59 GMT");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(-1717429609), "Sat, 31 Jul 1915 08:13:11 GMT");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(-62167219200), "Sat, 01 Jan 0000 00:00:00 GMT");
+ BOOST_CHECK_EQUAL(FormatRFC1123DateTime(-62167219201), "");
+}
+
BOOST_AUTO_TEST_CASE(util_FormatMoney)
{
BOOST_CHECK_EQUAL(FormatMoney(0), "0.00");
diff --git a/src/util/time.cpp b/src/util/time.cpp
index 68af84a8..9e0715e5 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -10,6 +10,7 @@
#include <util/check.h>
#include <util/strencodings.h>
+#include <array>
#include <atomic>
#include <chrono>
#include <optional>
@@ -17,6 +18,9 @@
#include <string_view>
#include <thread>
+static constexpr std::array<std::string_view, 7> weekdays{"Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed"}; // 1970-01-01 was a Thursday.
+static constexpr std::array<std::string_view, 12> months{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
static std::atomic<std::chrono::seconds> g_mock_time{}; //!< For testing
@@ -117,6 +121,24 @@ std::optional<int64_t> ParseISO8601DateTime(std::string_view str)
return int64_t{TicksSinceEpoch<std::chrono::seconds>(tp)};
}
+std::string FormatRFC1123DateTime(int64_t time)
+{
+ if (time < -62167219200 || 253402300799 < time) {
+ // 4-digit year, so only support years 0 to 9999
+ return "";
+ }
+ const std::chrono::sys_seconds secs{std::chrono::seconds{time}};
+ const auto days{std::chrono::floor<std::chrono::days>(secs)};
+ const auto w{days.time_since_epoch().count() % 7}; // will be in the range [-6, 6]
+ std::string_view weekday{weekdays.at(w >= 0 ? w : w + 7)};
+ const std::chrono::year_month_day ymd{days};
+ std::string_view month{months.at(unsigned{ymd.month()} - 1)};
+ const std::chrono::hh_mm_ss hms{secs - days};
+ // examples: Mon, 27 Jul 2009 12:28:53 GMT
+ // Fri, 31 May 2024 19:18:04 GMT
+ return strprintf("%03s, %02u %03s %04i %02i:%02i:%02i GMT", weekday, unsigned{ymd.day()}, month, signed{ymd.year()}, hms.hours().count(), hms.minutes().count(), hms.seconds().count());
+}
+
struct timeval MillisToTimeval(int64_t nTimeout)
{
struct timeval timeout;
diff --git a/src/util/time.h b/src/util/time.h
index 655db447..0f98726d 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -136,6 +136,12 @@ std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
std::optional<int64_t> ParseISO8601DateTime(std::string_view str);
+/**
+ * RFC1123 formatting https://www.rfc-editor.org/rfc/rfc1123#section-5.2.14
+ * Used in HTTP/1.1 responses
+ */
+std::string FormatRFC1123DateTime(int64_t nTime);
+
/**
* Convert milliseconds to a struct timeval for e.g. select.
*/
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.