summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-06-18 22:53:24 +0200
committerBram Moolenaar <Bram@vim.org>2019-06-18 22:53:24 +0200
commitdb51730df1817fc4b6ecf5a065c69fac518ad821 (patch)
treed7966f2f74b8f11cb6bb228a422bf4650e9bc7d9
parent517f71ab17c265602465eaee3775dcfe8afe0478 (diff)
downloadvim-git-db51730df1817fc4b6ecf5a065c69fac518ad821.tar.gz
patch 8.1.1567: localtime_r() does not respond to $TZ changesv8.1.1567
Problem: Localtime_r() does not respond to $TZ changes. Solution: If $TZ changes then call tzset(). (Tom Ryder)
-rwxr-xr-xsrc/auto/configure2
-rw-r--r--src/config.h.in1
-rw-r--r--src/configure.ac2
-rw-r--r--src/evalfunc.c8
-rw-r--r--src/memline.c50
-rw-r--r--src/proto/memline.pro1
-rw-r--r--src/testdir/test_functions.vim24
-rw-r--r--src/undo.c8
-rw-r--r--src/version.c2
9 files changed, 75 insertions, 23 deletions
diff --git a/src/auto/configure b/src/auto/configure
index c326abf16..8dcad0bb8 100755
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -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`
diff --git a/src/config.h.in b/src/config.h.in
index c1ced6fe9..f9ae0bbf3 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -217,6 +217,7 @@
#undef HAVE_TOWLOWER
#undef HAVE_TOWUPPER
#undef HAVE_ISWUPPER
+#undef HAVE_TZSET
#undef HAVE_UNSETENV
#undef HAVE_USLEEP
#undef HAVE_UTIME
diff --git a/src/configure.ac b/src/configure.ac
index dbf4f42b8..f7a71d079 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -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
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 66612e8c8..5c9400e17 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -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)"));
diff --git a/src/memline.c b/src/memline.c
index e7ae6dfb5..0e3bdb8b6 100644
--- a/src/memline.c
+++ b/src/memline.c
@@ -2082,6 +2082,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)".
* If "thetime" is invalid returns "(invalid)". Never returns NULL.
@@ -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);
diff --git a/src/proto/memline.pro b/src/proto/memline.pro
index 3199ba98e..663930dab 100644
--- a/src/proto/memline.pro
+++ b/src/proto/memline.pro
@@ -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);
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index a2e4da6c7..f71717f73 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -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()
diff --git a/src/undo.c b/src/undo.c
index dd4667001..4a4a33cc1 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -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);
diff --git a/src/version.c b/src/version.c
index 86ccdf46e..677279b24 100644
--- a/src/version.c
+++ b/src/version.c
@@ -778,6 +778,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1567,
+/**/
1566,
/**/
1565,