From e929f7e6388817e27c3fb3c51277ebe90fd0cad8 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Sat, 1 Nov 2025 10:21:30 +0100 Subject: [PATCH] time_utc: Fix bug in time format + add functions to read time --- common/time_utc.h | 70 +++++++++++++++++++++++++++++++++++++++++++++- tests/TimeTest.cpp | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tests/TimeTest.cpp diff --git a/common/time_utc.h b/common/time_utc.h index d544be8c..0f22fa9a 100644 --- a/common/time_utc.h +++ b/common/time_utc.h @@ -6,6 +6,8 @@ #include #include +#include +#include inline std::string time_UTC(const std::chrono::time_point &input) { auto time_ms = std::chrono::duration_cast(input.time_since_epoch()).count(); @@ -13,8 +15,74 @@ inline std::string time_UTC(const std::chrono::time_point 3) { + // Truncate beyond milliseconds + frac = frac.substr(0, 3); + } else if (frac.size() < 3) { + // Pad with zeros if less than 3 digits + while (frac.size() < 3) frac.push_back('0'); + } + + milliseconds = std::stoi(frac); + } + + time_t t = timegm(&tm); + return static_cast(t) * 1000ULL + milliseconds; +} + + +// -- new function that reuses parse_UTC_to_ms() -- +inline std::string utc_to_local_human_readable(const std::string& utc_string) { + using namespace std::chrono; + + uint64_t ms_since_epoch = parse_UTC_to_ms(utc_string); + auto tp = system_clock::time_point(milliseconds(ms_since_epoch)); + time_t t = system_clock::to_time_t(tp); + + // Convert to local time + std::tm tm_local = *std::localtime(&t); + + static const char* months[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }; + + std::ostringstream out; + out << months[tm_local.tm_mon] << " " + << std::setw(2) << std::setfill('0') << tm_local.tm_mday << ", " + << (tm_local.tm_year + 1900) << " " + << std::setw(2) << std::setfill('0') << tm_local.tm_hour << ":" + << std::setw(2) << std::setfill('0') << tm_local.tm_min; + + return out.str(); +} + #endif //JUNGFRAUJOCH_TIME_UTC_H diff --git a/tests/TimeTest.cpp b/tests/TimeTest.cpp new file mode 100644 index 00000000..545f8d43 --- /dev/null +++ b/tests/TimeTest.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute +// SPDX-License-Identifier: GPL-3.0-only + +#include +#include "../common/time_utc.h" + +TEST_CASE("Time_round trip", "[time_UTC][parse_UTC_to_ms]") { + // Current time → string → ms → back to string + auto now = std::chrono::system_clock::now(); + std::string utc = time_UTC(now); + uint64_t ms = parse_UTC_to_ms(utc); + + // Allow small rounding differences due to truncation + auto back_tp = std::chrono::system_clock::time_point(std::chrono::milliseconds(ms)); + auto diff = duration_cast(now - back_tp).count(); + + INFO("UTC: " << utc); + CHECK(std::abs(diff) < 2); // within 2ms round-trip tolerance +} + +TEST_CASE("Time_parse_UTC_to_ms", "[parse_UTC_to_ms]") { + uint64_t ms = parse_UTC_to_ms("1970-01-01T00:00:00.000Z"); + CHECK(ms == 0); + + ms = parse_UTC_to_ms("1970-01-01T00:00:00.001Z"); + CHECK(ms == 1); + + ms = parse_UTC_to_ms("1970-01-01T00:00:00.530Z"); + CHECK(ms == 530); + + ms = parse_UTC_to_ms("1970-01-01T00:00:01.000Z"); + CHECK(ms == 1000); + + ms = parse_UTC_to_ms("1972-01-01T00:00:00.006Z"); + CHECK(ms == 2 * 365 * 24 * 60 * 60 * 1000UL + 6); + + ms = parse_UTC_to_ms("2025-11-01T14:32:15.123Z"); + CHECK(ms > 0); +} + +TEST_CASE("Time_to_UTC", "[time_UTC]") { + using namespace std::chrono; + auto tp = system_clock::time_point(milliseconds(0)); + std::string s = time_UTC(tp); + CHECK(s == "1970-01-01T00:00:00.000Z"); + + // Regex to validate ISO format + std::regex iso(R"(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$)"); + CHECK(std::regex_match(s, iso)); +} \ No newline at end of file