]> granicus.if.org Git - vim/commitdiff
patch 8.1.1567: localtime_r() does not respond to $TZ changes v8.1.1567
authorBram Moolenaar <Bram@vim.org>
Tue, 18 Jun 2019 20:53:24 +0000 (22:53 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 18 Jun 2019 20:53:24 +0000 (22:53 +0200)
Problem:    Localtime_r() does not respond to $TZ changes.
Solution:   If $TZ changes then call tzset(). (Tom Ryder)

src/auto/configure
src/config.h.in
src/configure.ac
src/evalfunc.c
src/memline.c
src/proto/memline.pro
src/testdir/test_functions.vim
src/undo.c
src/version.c

index c326abf16c4ad3182b2ada6be0c131d153c57835..8dcad0bb8efb30a9751c89e4fe00e678cbe3e6e4 100755 (executable)
@@ -12569,7 +12569,7 @@ for ac_func in fchdir fchown fchmod fsync getcwd getpseudotty \
        memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \
        getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \
        sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \
-       strnicmp strpbrk strtol tgetent towlower towupper iswupper \
+       strnicmp strpbrk strtol tgetent towlower towupper iswupper tzset \
        usleep utime utimes mblen ftruncate unsetenv posix_openpt
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
index c1ced6fe9c533e678c8479cacfef0dfed319c371..f9ae0bbf3d51f9ee2ced85134dead03d3036e35d 100644 (file)
 #undef HAVE_TOWLOWER
 #undef HAVE_TOWUPPER
 #undef HAVE_ISWUPPER
+#undef HAVE_TZSET
 #undef HAVE_UNSETENV
 #undef HAVE_USLEEP
 #undef HAVE_UTIME
index dbf4f42b866e7b73bf136a529370462e023bf76c..f7a71d079d9a5b43ef580af1a9a755d1817919f3 100644 (file)
@@ -3742,7 +3742,7 @@ AC_CHECK_FUNCS(fchdir fchown fchmod fsync getcwd getpseudotty \
        memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \
        getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \
        sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \
-       strnicmp strpbrk strtol tgetent towlower towupper iswupper \
+       strnicmp strpbrk strtol tgetent towlower towupper iswupper tzset \
        usleep utime utimes mblen ftruncate unsetenv posix_openpt)
 AC_FUNC_SELECT_ARGTYPES
 AC_FUNC_FSEEKO
index 66612e8c83f1feaa4250d5e8f09f74c68758dd1d..5c9400e1741ae6432e0a4f89c321478a156965e6 100644 (file)
@@ -13188,9 +13188,7 @@ f_str2nr(typval_T *argvars, typval_T *rettv)
 f_strftime(typval_T *argvars, typval_T *rettv)
 {
     char_u     result_buf[256];
-# ifdef HAVE_LOCALTIME_R
     struct tm  tmval;
-# endif
     struct tm  *curtime;
     time_t     seconds;
     char_u     *p;
@@ -13202,11 +13200,7 @@ f_strftime(typval_T *argvars, typval_T *rettv)
        seconds = time(NULL);
     else
        seconds = (time_t)tv_get_number(&argvars[1]);
-# ifdef HAVE_LOCALTIME_R
-    curtime = localtime_r(&seconds, &tmval);
-# else
-    curtime = localtime(&seconds);
-# endif
+    curtime = vim_localtime(&seconds, &tmval);
     /* MSVC returns NULL for an invalid value of seconds. */
     if (curtime == NULL)
        rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
index e7ae6dfb5b941e3a019f67110c5d1a3d6ad84265..0e3bdb8b6b324132348f5ef5bd370b25a1c3c07e 100644 (file)
@@ -2081,6 +2081,48 @@ get_b0_dict(char_u *fname, dict_T *d)
 }
 #endif
 
+/*
+ * Cache of the current timezone name as retrieved from TZ, or an empty string
+ * where unset, up to 64 octets long including trailing null byte.
+ */
+#if defined(HAVE_LOCALTIME_R) && defined(HAVE_TZSET)
+static char    tz_cache[64];
+#endif
+
+/*
+ * Call either localtime(3) or localtime_r(3) from POSIX libc time.h, with the
+ * latter version preferred for reentrancy.
+ *
+ * If we use localtime_r(3) and we have tzset(3) available, check to see if the
+ * environment variable TZ has changed since the last run, and call tzset(3) to
+ * update the global timezone variables if it has.  This is because the POSIX
+ * standard doesn't require localtime_r(3) implementations to do that as it
+ * does with localtime(3), and we don't want to call tzset(3) every time.
+ */
+    struct tm *
+vim_localtime(
+    const time_t       *timep,         // timestamp for local representation
+    struct tm          *result)        // pointer to caller return buffer
+{
+#ifdef HAVE_LOCALTIME_R
+# ifdef HAVE_TZSET
+    char               *tz;            // pointer for TZ environment var
+
+    tz = (char *)mch_getenv((char_u *)"TZ");
+    if (tz == NULL)
+       tz = "";
+    if (STRNCMP(tz_cache, tz, sizeof(tz_cache) - 1) != 0)
+    {
+       tzset();
+       vim_strncpy((char_u *)tz_cache, (char_u *)tz, sizeof(tz_cache) - 1);
+    }
+# endif        // HAVE_TZSET
+    return localtime_r(timep, result);
+#else
+    return localtime(timep);
+#endif // HAVE_LOCALTIME_R
+}
+
 /*
  * Replacement for ctime(), which is not safe to use.
  * Requires strftime(), otherwise returns "(unknown)".
@@ -2093,16 +2135,10 @@ get_ctime(time_t thetime, int add_newline)
 {
     static char buf[50];
 #ifdef HAVE_STRFTIME
-# ifdef HAVE_LOCALTIME_R
     struct tm  tmval;
-# endif
     struct tm  *curtime;
 
-# ifdef HAVE_LOCALTIME_R
-    curtime = localtime_r(&thetime, &tmval);
-# else
-    curtime = localtime(&thetime);
-# endif
+    curtime = vim_localtime(&thetime, &tmval);
     /* MSVC returns NULL for an invalid value of seconds. */
     if (curtime == NULL)
        vim_strncpy((char_u *)buf, (char_u *)_("(Invalid)"), sizeof(buf) - 1);
index 3199ba98ed8ffa7fde8a44b6c3b8d34c33d178a9..663930dabdf3953bc60ae28d4d56ca80e4abc424 100644 (file)
@@ -13,6 +13,7 @@ void ml_recover(int checkext);
 int recover_names(char_u *fname, int list, int nr, char_u **fname_out);
 char_u *make_percent_swname(char_u *dir, char_u *name);
 void get_b0_dict(char_u *fname, dict_T *d);
+struct tm *vim_localtime(const time_t *timep, struct tm *result);
 char *get_ctime(time_t thetime, int add_newline);
 void ml_sync_all(int check_file, int check_char);
 void ml_preserve(buf_T *buf, int message);
index a2e4da6c76237568f48eaaadfdbe823800dbb540..f71717f73e0952788086a483e6b3cdfbed642216 100644 (file)
@@ -187,6 +187,30 @@ func Test_strftime()
 
   call assert_fails('call strftime([])', 'E730:')
   call assert_fails('call strftime("%Y", [])', 'E745:')
+
+  " Check that the time changes after we change the timezone
+  " Save previous timezone value, if any
+  if exists('$TZ')
+    let tz = $TZ
+  endif
+
+  " Force EST and then UTC, save the current hour (24-hour clock) for each
+  let $TZ = 'EST' | let est = strftime('%H')
+  let $TZ = 'UTC' | let utc = strftime('%H')
+
+  " Those hours should be two bytes long, and should not be the same; if they
+  " are, a tzset(3) call may have failed somewhere
+  call assert_equal(strlen(est), 2)
+  call assert_equal(strlen(utc), 2)
+  call assert_notequal(est, utc)
+
+  " If we cached a timezone value, put it back, otherwise clear it
+  if exists('tz')
+    let $TZ = tz
+  else
+    unlet $TZ
+  endif
+
 endfunc
 
 func Test_resolve_unix()
index dd46670013c9bab439809864b7f52565a7b8e04c..4a4a33cc1b3675fc63b205072bfe4ebf431d97d6 100644 (file)
@@ -3111,18 +3111,12 @@ ex_undolist(exarg_T *eap UNUSED)
 u_add_time(char_u *buf, size_t buflen, time_t tt)
 {
 #ifdef HAVE_STRFTIME
-# ifdef HAVE_LOCALTIME_R
     struct tm  tmval;
-# endif
     struct tm  *curtime;
 
     if (vim_time() - tt >= 100)
     {
-# ifdef HAVE_LOCALTIME_R
-       curtime = localtime_r(&tt, &tmval);
-# else
-       curtime = localtime(&tt);
-# endif
+       curtime = vim_localtime(&tt, &tmval);
        if (vim_time() - tt < (60L * 60L * 12L))
            /* within 12 hours */
            (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
index 86ccdf46e87d3d2a659419d374b60bf404dbe9af..677279b243a3d304144ddc61e4e24f959a2c69d2 100644 (file)
@@ -777,6 +777,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1567,
 /**/
     1566,
 /**/