From 771b13794f5f4d7f7140b28a1faf5a162132b818 Mon Sep 17 00:00:00 2001 From: danipiza Date: Thu, 19 Mar 2026 09:11:45 +0100 Subject: [PATCH 1/3] [#24281] Normalized '%Z' formatting to UTC offset across platforms Signed-off-by: danipiza --- cpp_utils/src/cpp/time/time_utils.cpp | 51 ++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/cpp_utils/src/cpp/time/time_utils.cpp b/cpp_utils/src/cpp/time/time_utils.cpp index 77a5e02..a65edeb 100644 --- a/cpp_utils/src/cpp/time/time_utils.cpp +++ b/cpp_utils/src/cpp/time/time_utils.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -108,7 +109,55 @@ std::string timestamp_to_string( if (tm) { - ss << std::put_time(tm, format.c_str()); + // Replace %Z with a cross-platform "UTC+HH" / "UTC+HH:MM" offset string. + // Standard %Z is platform-dependent: on Windows it outputs full localized names, + // while on Linux it outputs short abbreviations. + std::string processed_format; + processed_format.reserve(format.size() + 16); + for (std::size_t i = 0; i < format.size(); ++i) + { + if (format[i] == '%' && i + 1 < format.size() && format[i + 1] == 'Z') + { + long offset_sec = 0; + if (local_time) + { +#if _EPROSIMA_WINDOWS_PLATFORM + // _timezone: seconds west of UTC for standard time (negative when east of UTC) + offset_sec = -(long)_timezone; + if (tm->tm_isdst > 0) + { + // _dstbias: adjustment in seconds when DST is active (typically -3600) + offset_sec -= (long)_dstbias; + } +#else + // tm_gmtoff: seconds east of UTC, set by localtime() + offset_sec = tm->tm_gmtoff; +#endif // _EPROSIMA_WINDOWS_PLATFORM + } + + const char sign = (offset_sec >= 0) ? '+' : '-'; + const long abs_offset = (offset_sec >= 0) ? offset_sec : -offset_sec; + const long hours = abs_offset / 3600; + const long minutes = (abs_offset % 3600) / 60; + + char buf[16]; + if (minutes != 0) + { + snprintf(buf, sizeof(buf), "UTC%c%02ld:%02ld", sign, hours, minutes); + } + else + { + snprintf(buf, sizeof(buf), "UTC%c%02ld", sign, hours); + } + processed_format += buf; + ++i; + } + else + { + processed_format += format[i]; + } + } + ss << std::put_time(tm, processed_format.c_str()); } else { From 911d628f44cf8d012188a5d3c154f89f5b8db394 Mon Sep 17 00:00:00 2001 From: danipiza Date: Mon, 30 Mar 2026 07:36:24 +0200 Subject: [PATCH 2/3] [#24281] Fixed 'timestamp_to_string_format' test Signed-off-by: danipiza --- cpp_utils/src/cpp/time/time_utils.cpp | 9 +++++---- cpp_utils/test/unittest/time/time_utils_test.cpp | 6 +----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cpp_utils/src/cpp/time/time_utils.cpp b/cpp_utils/src/cpp/time/time_utils.cpp index a65edeb..d1b48e8 100644 --- a/cpp_utils/src/cpp/time/time_utils.cpp +++ b/cpp_utils/src/cpp/time/time_utils.cpp @@ -178,8 +178,8 @@ Timestamp string_to_timestamp( if (ss.fail()) { throw PreconditionNotMet( - STR_ENTRY << "Format <" << format << "> to convert string to Timestamp is not valid for timestamp " << timestamp << - "."); + STR_ENTRY << "Format <" << format << "> to convert string to Timestamp is not valid for timestamp " + << timestamp << "."); } std::time_t utc_time; @@ -238,8 +238,9 @@ time_t normalize( if (0 > time || time > max_value) { EPROSIMA_LOG_WARNING(TIME_UTILS, - "Timestamp value: " << time << " is out of range for Windows, clamping to 0 and " << - max_value); + "Timestamp value: " + << time << " is out of range for Windows, clamping to 0 and " + << max_value); normalized_time = std::max((time_t) 0, std::min(max_value, time)); } #endif // if _EPROSIMA_WINDOWS_PLATFORM diff --git a/cpp_utils/test/unittest/time/time_utils_test.cpp b/cpp_utils/test/unittest/time/time_utils_test.cpp index 0121c0f..9da6cc5 100644 --- a/cpp_utils/test/unittest/time/time_utils_test.cpp +++ b/cpp_utils/test/unittest/time/time_utils_test.cpp @@ -377,11 +377,7 @@ TEST(time_utils_test, timestamp_to_string_format) // time zone { // String for Time Zone is different in Windows or Linux -#if _EPROSIMA_WINDOWS_PLATFORM - std::string expected_str = "Coordinated Universal Time"; -#else - std::string expected_str = "GMT"; -#endif // _EPROSIMA_WINDOWS_PLATFORM + std::string expected_str = "UTC+00"; std::string date_str = timestamp_to_string(date, "%Z"); ASSERT_EQ(date_str, expected_str); From c29e24369db25ab724fa183f0fdbdb4aaa51f9e5 Mon Sep 17 00:00:00 2001 From: danipiza Date: Tue, 31 Mar 2026 15:07:51 +0200 Subject: [PATCH 3/3] [#24281] Simplified UTC offset conversion for %Z Signed-off-by: danipiza --- cpp_utils/src/cpp/time/time_utils.cpp | 89 +++++++++++++++++---------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/cpp_utils/src/cpp/time/time_utils.cpp b/cpp_utils/src/cpp/time/time_utils.cpp index d1b48e8..2fb199e 100644 --- a/cpp_utils/src/cpp/time/time_utils.cpp +++ b/cpp_utils/src/cpp/time/time_utils.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -36,6 +35,61 @@ namespace eprosima { namespace utils { +static std::string utc_offset_string( + const std::tm& tm, + bool local_time) +{ + if (!local_time) + { + return "UTC+00"; + } + + std::ostringstream os; + os << std::put_time(&tm, "%z"); + const std::string z = os.str(); + + if (z.empty() || (z[0] != '+' && z[0] != '-')) + { + return "UTC+00"; + } + + std::string hh; + std::string mm = "00"; + + // +HH + if (z.size() == 3) + { + hh = z.substr(1, 2); + } + // +HHMM + else if (z.size() == 5) + { + hh = z.substr(1, 2); + mm = z.substr(3, 2); + } + // +HH:MM + else if (z.size() == 6 && z[3] == ':') + { + hh = z.substr(1, 2); + mm = z.substr(4, 2); + } + else + { + return "UTC+00"; + } + + std::string ret = "UTC"; + ret += z[0]; + ret += hh; + if (mm != "00") + { + ret += ":"; + ret += mm; + } + + return ret; +} + Timestamp now() noexcept { return Timeclock::now(); @@ -118,38 +172,7 @@ std::string timestamp_to_string( { if (format[i] == '%' && i + 1 < format.size() && format[i + 1] == 'Z') { - long offset_sec = 0; - if (local_time) - { -#if _EPROSIMA_WINDOWS_PLATFORM - // _timezone: seconds west of UTC for standard time (negative when east of UTC) - offset_sec = -(long)_timezone; - if (tm->tm_isdst > 0) - { - // _dstbias: adjustment in seconds when DST is active (typically -3600) - offset_sec -= (long)_dstbias; - } -#else - // tm_gmtoff: seconds east of UTC, set by localtime() - offset_sec = tm->tm_gmtoff; -#endif // _EPROSIMA_WINDOWS_PLATFORM - } - - const char sign = (offset_sec >= 0) ? '+' : '-'; - const long abs_offset = (offset_sec >= 0) ? offset_sec : -offset_sec; - const long hours = abs_offset / 3600; - const long minutes = (abs_offset % 3600) / 60; - - char buf[16]; - if (minutes != 0) - { - snprintf(buf, sizeof(buf), "UTC%c%02ld:%02ld", sign, hours, minutes); - } - else - { - snprintf(buf, sizeof(buf), "UTC%c%02ld", sign, hours); - } - processed_format += buf; + processed_format += utc_offset_string(*tm, local_time); ++i; } else