From 9da542d5c1c983b9ea608b7fc04d75826278564f Mon Sep 17 00:00:00 2001 From: Vasiliy Koyrev Date: Thu, 13 Oct 2022 11:55:54 +0300 Subject: string(TIMESTAMP): Add %z and %Z for adding time zone string Fixes: #24056 --- Help/command/string.rst | 11 +++++ Help/release/dev/timestamp-timezone.rst | 5 ++ Source/cmTimestamp.cxx | 63 ++++++++++++++++++++++-- Source/cmTimestamp.h | 4 +- Tests/CMakeTests/String-TIMESTAMP-TimeZone.cmake | 22 +++++++++ Tests/CMakeTests/StringTest.cmake.in | 3 ++ Tests/RunCMake/string/Timestamp-stderr.txt | 2 +- Tests/RunCMake/string/Timestamp.cmake | 2 +- 8 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 Help/release/dev/timestamp-timezone.rst create mode 100644 Tests/CMakeTests/String-TIMESTAMP-TimeZone.cmake diff --git a/Help/command/string.rst b/Help/command/string.rst index 86cbd2e..217157c 100644 --- a/Help/command/string.rst +++ b/Help/command/string.rst @@ -522,6 +522,17 @@ specifiers: ``%Y`` The current year. +``%z`` + .. versionadded:: 3.26 + + The offset of the time zone from UTC, in hours and minutes, + with format ``+hhmm`` or ``-hhmm``. + +``%Z`` + .. versionadded:: 3.26 + + The time zone name. + Unknown format specifiers will be ignored and copied to the output as-is. diff --git a/Help/release/dev/timestamp-timezone.rst b/Help/release/dev/timestamp-timezone.rst new file mode 100644 index 0000000..178fa9a --- /dev/null +++ b/Help/release/dev/timestamp-timezone.rst @@ -0,0 +1,5 @@ +timestamp-timezone +------------------ + +* The :command:`string(TIMESTAMP)` and :command:`file(TIMESTAMP)` commands + now support the ``%z`` and ``%Z`` specifiers for the time zone. diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx index 677fdb6..7e47b4e 100644 --- a/Source/cmTimestamp.cxx +++ b/Source/cmTimestamp.cxx @@ -128,8 +128,8 @@ std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT, : static_cast(0); if (c1 == '%' && c2 != 0) { - result += - this->AddTimestampComponent(c2, timeStruct, timeT, microseconds); + result += this->AddTimestampComponent(c2, timeStruct, timeT, utcFlag, + microseconds); ++i; } else { result += c1; @@ -179,7 +179,7 @@ time_t cmTimestamp::CreateUtcTimeTFromTm(struct tm& tm) const } std::string cmTimestamp::AddTimestampComponent( - char flag, struct tm& timeStruct, const time_t timeT, + char flag, struct tm& timeStruct, const time_t timeT, const bool utcFlag, const uint32_t microseconds) const { std::string formatString = cmStrCat('%', flag); @@ -203,6 +203,63 @@ std::string cmTimestamp::AddTimestampComponent( case 'Y': case '%': break; + case 'Z': +#if defined(__GLIBC__) + // 'struct tm' has the time zone, so strftime can honor UTC. + static_cast(utcFlag); +#else + // 'struct tm' may not have the time zone, so strftime may + // use local time. Hard-code the UTC result. + if (utcFlag) { + return std::string("GMT"); + } +#endif + break; + case 'z': { +#if defined(__GLIBC__) + // 'struct tm' has the time zone, so strftime can honor UTC. + static_cast(utcFlag); +#else + // 'struct tm' may not have the time zone, so strftime may + // use local time. Hard-code the UTC result. + if (utcFlag) { + return std::string("+0000"); + } +#endif +#ifndef _AIX + break; +#else + std::string xpg_sus_old; + bool const xpg_sus_was_set = + cmSystemTools::GetEnv("XPG_SUS_ENV", xpg_sus_old); + if (xpg_sus_was_set && xpg_sus_old == "ON") { + break; + } + xpg_sus_old = "XPG_SUS_ENV=" + xpg_sus_old; + + // On AIX systems, %z requires XPG_SUS_ENV=ON to work as desired. + cmSystemTools::PutEnv("XPG_SUS_ENV=ON"); + tzset(); + + char buffer[16]; + size_t size = strftime(buffer, sizeof(buffer), "%z", &timeStruct); + +# ifndef CMAKE_BOOTSTRAP + if (xpg_sus_was_set) { + cmSystemTools::PutEnv(xpg_sus_old); + } else { + cmSystemTools::UnsetEnv("XPG_SUS_ENV"); + } +# else + // No UnsetEnv during bootstrap. This is good enough for CMake itself. + cmSystemTools::PutEnv(xpg_sus_old); + static_cast(xpg_sus_was_set); +# endif + tzset(); + + return std::string(buffer, size); +#endif + } case 's': // Seconds since UNIX epoch (midnight 1-jan-1970) { // Build a time_t for UNIX epoch and subtract from the input "timeT": diff --git a/Source/cmTimestamp.h b/Source/cmTimestamp.h index ada5006..05c6342 100644 --- a/Source/cmTimestamp.h +++ b/Source/cmTimestamp.h @@ -32,6 +32,6 @@ private: time_t CreateUtcTimeTFromTm(struct tm& timeStruct) const; std::string AddTimestampComponent(char flag, struct tm& timeStruct, - time_t timeT, - uint32_t microseconds = 0) const; + time_t timeT, bool utcFlag, + uint32_t microseconds) const; }; diff --git a/Tests/CMakeTests/String-TIMESTAMP-TimeZone.cmake b/Tests/CMakeTests/String-TIMESTAMP-TimeZone.cmake new file mode 100644 index 0000000..eb2eb42 --- /dev/null +++ b/Tests/CMakeTests/String-TIMESTAMP-TimeZone.cmake @@ -0,0 +1,22 @@ +string(TIMESTAMP output "%z") + +STRING(LENGTH output output_length) + +message("~${output}~") + +set(expected_output_length 6) + +if(NOT(${output_length} EQUAL ${expected_output_length})) + message(FATAL_ERROR "expected ${expected_output_length} entries in output with all specifiers; found ${output_length}") +endif() + +string(SUBSTRING ${output} 0 1 output0) +string(SUBSTRING ${output} 4 1 output4) + +if(NOT((${output0} STREQUAL "-") OR (${output0} STREQUAL "+"))) + message(FATAL_ERROR "expected output[0] equ '+' or '-'; found: '${output0}'") +endif() + +if(NOT((${output4} STREQUAL "0") OR (${output4} STREQUAL "5"))) + message(FATAL_ERROR "expected output[4] equ '0' or '5'; found: '${output4}'") +endif() diff --git a/Tests/CMakeTests/StringTest.cmake.in b/Tests/CMakeTests/StringTest.cmake.in index 154afa7..5f8b111 100644 --- a/Tests/CMakeTests/StringTest.cmake.in +++ b/Tests/CMakeTests/StringTest.cmake.in @@ -44,6 +44,8 @@ set(TIMESTAMP-IncompleteSpecifier-RESULT 0) set(TIMESTAMP-IncompleteSpecifier-STDERR "~foobar%~") set(TIMESTAMP-AllSpecifiers-RESULT 0) set(TIMESTAMP-AllSpecifiers-STDERR "~[0-9]+(;[0-9]+)*~") +set(TIMESTAMP-TimeZone-RESULT 0) +set(TIMESTAMP-TimeZone-STDERR "~[-,+][0-9][0-9][0-9][0-9]~") set(TIMESTAMP-MonthWeekNames-RESULT 0) set(TIMESTAMP-MonthWeekNames-STDERR "~[^%]+;[^%]+~") set(TIMESTAMP-UnixTime-RESULT 0) @@ -75,6 +77,7 @@ check_cmake_test(String TIMESTAMP-IncompleteSpecifier TIMESTAMP-AllSpecifiers TIMESTAMP-MonthWeekNames + TIMESTAMP-TimeZone TIMESTAMP-UnixTime ) diff --git a/Tests/RunCMake/string/Timestamp-stderr.txt b/Tests/RunCMake/string/Timestamp-stderr.txt index f162f52..c57bba6 100644 --- a/Tests/RunCMake/string/Timestamp-stderr.txt +++ b/Tests/RunCMake/string/Timestamp-stderr.txt @@ -1 +1 @@ -RESULT=2005-08-07 23:19:49.000000 Sunday=Sun August=Aug 05 day=219 wd=0 week=32 w_iso=31 %I=11 epoch=1123456789 +^RESULT=2005-08-07 23:19:49.000000 Sunday=Sun August=Aug 05 day=219 wd=0 week=32 w_iso=31 %I=11 epoch=1123456789 TZ=GMT tz=\+0000$ diff --git a/Tests/RunCMake/string/Timestamp.cmake b/Tests/RunCMake/string/Timestamp.cmake index 531a237..3d68b1a 100644 --- a/Tests/RunCMake/string/Timestamp.cmake +++ b/Tests/RunCMake/string/Timestamp.cmake @@ -1,3 +1,3 @@ set(ENV{SOURCE_DATE_EPOCH} "1123456789") -string(TIMESTAMP RESULT "%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s" UTC) +string(TIMESTAMP RESULT "%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z" UTC) message("RESULT=${RESULT}") -- cgit v0.12