summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Help/command/string.rst11
-rw-r--r--Help/release/dev/timestamp-timezone.rst5
-rw-r--r--Source/cmTimestamp.cxx63
-rw-r--r--Source/cmTimestamp.h4
-rw-r--r--Tests/CMakeTests/String-TIMESTAMP-TimeZone.cmake22
-rw-r--r--Tests/CMakeTests/StringTest.cmake.in3
-rw-r--r--Tests/RunCMake/string/Timestamp-stderr.txt2
-rw-r--r--Tests/RunCMake/string/Timestamp.cmake2
8 files changed, 105 insertions, 7 deletions
diff --git a/Help/command/string.rst b/Help/command/string.rst
index 86cbd2ece2..217157cd64 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 0000000000..178fa9ae05
--- /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 677fdb658e..7e47b4e51c 100644
--- a/Source/cmTimestamp.cxx
+++ b/Source/cmTimestamp.cxx
@@ -128,8 +128,8 @@ std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT,
: static_cast<char>(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<void>(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<void>(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<void>(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 ada5006d45..05c6342cb2 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 0000000000..eb2eb424d7
--- /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 154afa7f83..5f8b1117d2 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 f162f52992..c57bba6165 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 531a237bdc..3d68b1a587 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}")