Ver Fonte

string(TIMESTAMP): Add %z and %Z for adding time zone string

Fixes: #24056
Vasiliy Koyrev há 3 anos atrás
pai
commit
9da542d5c1

+ 11 - 0
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.
 

+ 5 - 0
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.

+ 60 - 3
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":

+ 2 - 2
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;
 };

+ 22 - 0
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()

+ 3 - 0
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
   )
 

+ 1 - 1
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$

+ 1 - 1
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}")