From 612663c32cf25d116291a7c6b078de4748a4eb57 Mon Sep 17 00:00:00 2001 From: Erik Sohns Date: Sat, 22 Nov 2025 12:41:52 +0100 Subject: [PATCH 1/4] fix a bug in the ACE log mechanism when using a '%#T' format string to print time periods. These logging calls invoke ACE::timestamp, which in turn (erroneously) invokes localtime_r (instead of gmtime_r). Localtime(_r) always adds the timezone offset (if any) to the hour field, which results in a wrong result string (when you live anywhere outside of the GMT time zone) --- ACE/ace/ACE.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ACE/ace/ACE.cpp b/ACE/ace/ACE.cpp index 6926a52338e2d..2cde731b189c8 100644 --- a/ACE/ace/ACE.cpp +++ b/ACE/ace/ACE.cpp @@ -2415,7 +2415,10 @@ ACE::timestamp (const ACE_Time_Value& time_value, ACE_Time_Value (ACE_OS::gettimeofday ()) : time_value; time_t secs = cur_time.sec (); struct tm tms; - ACE_OS::localtime_r (&secs, &tms); + if (time_value == ACE_Time_Value::zero) + ACE_OS::localtime_r (&secs, &tms); + else + ACE_OS::gmtime_r (&secs, &tms); ACE_OS::snprintf (date_and_time, date_and_timelen, ACE_TEXT ("%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%06ld"), From 78bcf26ffa0b4b9ce1cd5a4c0acc2283c2a726bf Mon Sep 17 00:00:00 2001 From: Erik Sohns Date: Sat, 22 Nov 2025 17:31:00 +0100 Subject: [PATCH 2/4] incorporated AI review comments --- ACE/ace/ACE.cpp | 40 +++++++++++++++++++++++++++++++++++----- ACE/ace/ACE.h | 21 ++++++++++++++++++++- ACE/ace/Log_Msg.cpp | 38 ++++++++++++++++++++++++++++++++++++++ ACE/ace/Log_Msg.h | 6 +++++- 4 files changed, 98 insertions(+), 7 deletions(-) diff --git a/ACE/ace/ACE.cpp b/ACE/ace/ACE.cpp index 2cde731b189c8..b3c98d5937a5e 100644 --- a/ACE/ace/ACE.cpp +++ b/ACE/ace/ACE.cpp @@ -2389,7 +2389,7 @@ ACE::timestamp (ACE_TCHAR date_and_time[], } /// Returns the given timestamp in the form -/// "hour:minute:second:microsecond." The month, day, and year are +/// "hour:minute:second.microsecond." The month, day, and year are /// also stored in the beginning of the date_and_time array /// using ISO-8601 format. /// 012345678901234567890123456 @@ -2415,10 +2415,7 @@ ACE::timestamp (const ACE_Time_Value& time_value, ACE_Time_Value (ACE_OS::gettimeofday ()) : time_value; time_t secs = cur_time.sec (); struct tm tms; - if (time_value == ACE_Time_Value::zero) - ACE_OS::localtime_r (&secs, &tms); - else - ACE_OS::gmtime_r (&secs, &tms); + ACE_OS::localtime_r (&secs, &tms); ACE_OS::snprintf (date_and_time, date_and_timelen, ACE_TEXT ("%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%06ld"), @@ -2433,6 +2430,39 @@ ACE::timestamp (const ACE_Time_Value& time_value, return &date_and_time[10 + (return_pointer_to_first_digit != 0)]; } +/// Returns the given duration in the form +/// "hour:minute:second.microsecond." +/// 0123456789012345 +/// 12:56:00.123456 +ACE_TCHAR * +ACE::duration (const ACE_Time_Value& duration_value, + ACE_TCHAR duration[], + size_t duration_len) +{ + //ACE_TRACE ("ACE::duration"); + + // This magic number is from the formatting statement + // farther down this routine. + if (duration_len < 16) + { + errno = EINVAL; + return 0; + } + + time_t secs = duration_value.sec (); + struct tm tms; + ACE_OS::gmtime_r (&secs, &tms); + ACE_OS::snprintf (duration, + duration_len, + ACE_TEXT ("%2.2d:%2.2d:%2.2d.%06ld"), + tms.tm_hour, + tms.tm_min, + tms.tm_sec, + static_cast (duration_value.usec ())); + duration[duration_len - 1] = '\0'; + return &duration[0]; +} + /// This function rounds the request to a multiple of the page size. size_t ACE::round_to_pagesize (size_t len) diff --git a/ACE/ace/ACE.h b/ACE/ace/ACE.h index b705e092d9556..2ebba0b66f707 100644 --- a/ACE/ace/ACE.h +++ b/ACE/ace/ACE.h @@ -478,7 +478,8 @@ namespace ACE * Translate the given timestamp to ISO-8601 format. * * @param time_value ACE_Time_Value to format. This is assumed to be - * an absolute time value. + * an absolute time value. Passing ACE_Time_Value::zero + uses gettimeofday(). * @param date_and_time Array to hold the timestamp. * @param time_len Size of @a date_and_time in ACE_TCHARs. * Must be greater than or equal to 27. @@ -498,6 +499,24 @@ namespace ACE size_t time_len, bool return_pointer_to_first_digit = false); + /** + * Translate the given duration to ISO-8601 format. Note that the current + * implementation cannot handle durations larger or equal to 24 hours. + * + * @param duration_value ACE_Time_Value to format. This is assumed to be + * a time period. + * @param duration Array to hold the duration. + * @param duration_len Size of @a duration in ACE_TCHARs. + * Must be greater than or equal to 16. + * + * @retval 0 if unsuccessful, with errno set. If @a duration_len is less than + * 16 errno will be EINVAL. + * @retval If successful, pointer to beginning of @a duration. + */ + extern ACE_Export ACE_TCHAR *duration (const ACE_Time_Value& duration_value, + ACE_TCHAR duration[], + size_t duration_len); + /** * Translate the current time to ISO-8601 timestamp format. * diff --git a/ACE/ace/Log_Msg.cpp b/ACE/ace/Log_Msg.cpp index 1abe5c7e0b3c8..c0764e97bd45c 100644 --- a/ACE/ace/Log_Msg.cpp +++ b/ACE/ace/Log_Msg.cpp @@ -1726,6 +1726,37 @@ ACE_Log_Msg::log (const ACE_TCHAR *format_str, break; } + case 'Y': // Format the duration in hour:minute:sec.usec format. + // Note that this is currently overflows if the + // provided duration is larger or equal to 24 hours. + { + ACE_TCHAR duration[27]; + ACE_OS::strcpy (fp, ACE_TEXT_PRIs); + // Did we find the flag indicating a time value argument + if (format[1] == ACE_TEXT('#')) + { + ACE_Time_Value* duration_value = va_arg (argp, ACE_Time_Value*); + if (can_check) + this_len = ACE_OS::snprintf + (bp, bspace, format, + ACE::duration (*duration_value, + duration, + sizeof duration / sizeof (ACE_TCHAR))); + else + this_len = ACE_OS::sprintf + (bp, format, ACE::duration (*duration_value, + duration, + sizeof duration / sizeof (ACE_TCHAR))); + } + else + { + ACE_OS::fprintf (stderr, + "error: %%Y requires # modifier\n"); + } + ACE_UPDATE_COUNT (bspace, this_len); + break; + } + case 't': // Format thread id. #if defined (ACE_WIN32) ACE_OS::strcpy (fp, ACE_TEXT ("u")); @@ -2415,6 +2446,13 @@ bool ACE_Log_Formatter::process_conversion () this->bp_ += len; } break; + // %Y with # in the conversion spec takes an arg (ACE_Time_Value*) + case 'Y': + ACE_OS::strcpy (this->fp_, "s"); + if (ACE_OS::memchr (this->fmt_out_, '#', this->fp_ - this->fmt_out_)) + return false; + ACE_ASSERT (false); // %Y without # is currently unspecified + break; case '{': this->logger_->inc (); diff --git a/ACE/ace/Log_Msg.h b/ACE/ace/Log_Msg.h index ebbac05573a03..ac48b5ce9cc70 100644 --- a/ACE/ace/Log_Msg.h +++ b/ACE/ace/Log_Msg.h @@ -542,7 +542,7 @@ class ACE_Export ACE_Log_Msg * - 'S': print out the appropriate signal message corresponding * to var-argument, e.g., as done by strsignal() * - 's': prints a ACE_TCHAR* character string (also see C and W) - * - 'T': print timestamp in hour:minute:sec:usec format (plain option, + * - 'T': print timestamp in hour:minute:sec.usec format (plain option, * i.e. without any flags, prints system supplied timestamp; * with '#' flag added expects ACE_Time_Value* in argument list) * - 'D': print timestamp as Weekday Month day year hour:minute:sec.usec @@ -555,6 +555,10 @@ class ACE_Export ACE_Log_Msg * - 'W': prints a wchar_t* character string (also see C and s) * - 'x': print as a hex number * - 'X': print as a hex number + * - 'Y': print duration in hour:minute:sec.usec format (plain option, + * i.e. without any flags, is currently unspecified; + * with '#' flag added expects ACE_Time_Value* in argument list) + * Note that durations >= 24 hours will currently overflow * - 'z': print an ACE_OS::WChar character * - 'Z': print an ACE_OS::WChar character string * - ':': print a time_t value as an integral number From 3bbf8f361936e788bda1e9512fced9de9d4f0b17 Mon Sep 17 00:00:00 2001 From: Erik Sohns Date: Sat, 22 Nov 2025 18:01:55 +0100 Subject: [PATCH 3/4] minor update --- ACE/ace/Log_Msg.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ACE/ace/Log_Msg.cpp b/ACE/ace/Log_Msg.cpp index c0764e97bd45c..2048be1f52f20 100644 --- a/ACE/ace/Log_Msg.cpp +++ b/ACE/ace/Log_Msg.cpp @@ -1727,10 +1727,10 @@ ACE_Log_Msg::log (const ACE_TCHAR *format_str, } case 'Y': // Format the duration in hour:minute:sec.usec format. - // Note that this is currently overflows if the - // provided duration is larger or equal to 24 hours. + // Note that this currently overflows if the provided + // duration is larger or equal to 24 hours. { - ACE_TCHAR duration[27]; + ACE_TCHAR duration[16]; ACE_OS::strcpy (fp, ACE_TEXT_PRIs); // Did we find the flag indicating a time value argument if (format[1] == ACE_TEXT('#')) From 1fdf1baf20e54aa25bfdb7af5aa08dba9d3d171e Mon Sep 17 00:00:00 2001 From: Erik Sohns Date: Mon, 24 Nov 2025 19:19:25 +0100 Subject: [PATCH 4/4] print message to stderr instead of asserting --- ACE/ace/Log_Msg.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ACE/ace/Log_Msg.cpp b/ACE/ace/Log_Msg.cpp index 2048be1f52f20..d94c31edb9c7a 100644 --- a/ACE/ace/Log_Msg.cpp +++ b/ACE/ace/Log_Msg.cpp @@ -2451,7 +2451,8 @@ bool ACE_Log_Formatter::process_conversion () ACE_OS::strcpy (this->fp_, "s"); if (ACE_OS::memchr (this->fmt_out_, '#', this->fp_ - this->fmt_out_)) return false; - ACE_ASSERT (false); // %Y without # is currently unspecified + ACE_OS::fprintf (stderr, + "error: %%Y requires # modifier\n"); break; case '{':