Skip to content

Commit db51730

Browse files
committed
patch 8.1.1567: localtime_r() does not respond to $TZ changes
Problem: Localtime_r() does not respond to $TZ changes. Solution: If $TZ changes then call tzset(). (Tom Ryder)
1 parent 517f71a commit db51730

9 files changed

Lines changed: 75 additions & 23 deletions

File tree

src/auto/configure

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12569,7 +12569,7 @@ for ac_func in fchdir fchown fchmod fsync getcwd getpseudotty \
1256912569
memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \
1257012570
getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \
1257112571
sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \
12572-
strnicmp strpbrk strtol tgetent towlower towupper iswupper \
12572+
strnicmp strpbrk strtol tgetent towlower towupper iswupper tzset \
1257312573
usleep utime utimes mblen ftruncate unsetenv posix_openpt
1257412574
do :
1257512575
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`

src/config.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@
217217
#undef HAVE_TOWLOWER
218218
#undef HAVE_TOWUPPER
219219
#undef HAVE_ISWUPPER
220+
#undef HAVE_TZSET
220221
#undef HAVE_UNSETENV
221222
#undef HAVE_USLEEP
222223
#undef HAVE_UTIME

src/configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3742,7 +3742,7 @@ AC_CHECK_FUNCS(fchdir fchown fchmod fsync getcwd getpseudotty \
37423742
memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \
37433743
getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \
37443744
sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \
3745-
strnicmp strpbrk strtol tgetent towlower towupper iswupper \
3745+
strnicmp strpbrk strtol tgetent towlower towupper iswupper tzset \
37463746
usleep utime utimes mblen ftruncate unsetenv posix_openpt)
37473747
AC_FUNC_SELECT_ARGTYPES
37483748
AC_FUNC_FSEEKO

src/evalfunc.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13188,9 +13188,7 @@ f_str2nr(typval_T *argvars, typval_T *rettv)
1318813188
f_strftime(typval_T *argvars, typval_T *rettv)
1318913189
{
1319013190
char_u result_buf[256];
13191-
# ifdef HAVE_LOCALTIME_R
1319213191
struct tm tmval;
13193-
# endif
1319413192
struct tm *curtime;
1319513193
time_t seconds;
1319613194
char_u *p;
@@ -13202,11 +13200,7 @@ f_strftime(typval_T *argvars, typval_T *rettv)
1320213200
seconds = time(NULL);
1320313201
else
1320413202
seconds = (time_t)tv_get_number(&argvars[1]);
13205-
# ifdef HAVE_LOCALTIME_R
13206-
curtime = localtime_r(&seconds, &tmval);
13207-
# else
13208-
curtime = localtime(&seconds);
13209-
# endif
13203+
curtime = vim_localtime(&seconds, &tmval);
1321013204
/* MSVC returns NULL for an invalid value of seconds. */
1321113205
if (curtime == NULL)
1321213206
rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));

src/memline.c

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,6 +2081,48 @@ get_b0_dict(char_u *fname, dict_T *d)
20812081
}
20822082
#endif
20832083

2084+
/*
2085+
* Cache of the current timezone name as retrieved from TZ, or an empty string
2086+
* where unset, up to 64 octets long including trailing null byte.
2087+
*/
2088+
#if defined(HAVE_LOCALTIME_R) && defined(HAVE_TZSET)
2089+
static char tz_cache[64];
2090+
#endif
2091+
2092+
/*
2093+
* Call either localtime(3) or localtime_r(3) from POSIX libc time.h, with the
2094+
* latter version preferred for reentrancy.
2095+
*
2096+
* If we use localtime_r(3) and we have tzset(3) available, check to see if the
2097+
* environment variable TZ has changed since the last run, and call tzset(3) to
2098+
* update the global timezone variables if it has. This is because the POSIX
2099+
* standard doesn't require localtime_r(3) implementations to do that as it
2100+
* does with localtime(3), and we don't want to call tzset(3) every time.
2101+
*/
2102+
struct tm *
2103+
vim_localtime(
2104+
const time_t *timep, // timestamp for local representation
2105+
struct tm *result) // pointer to caller return buffer
2106+
{
2107+
#ifdef HAVE_LOCALTIME_R
2108+
# ifdef HAVE_TZSET
2109+
char *tz; // pointer for TZ environment var
2110+
2111+
tz = (char *)mch_getenv((char_u *)"TZ");
2112+
if (tz == NULL)
2113+
tz = "";
2114+
if (STRNCMP(tz_cache, tz, sizeof(tz_cache) - 1) != 0)
2115+
{
2116+
tzset();
2117+
vim_strncpy((char_u *)tz_cache, (char_u *)tz, sizeof(tz_cache) - 1);
2118+
}
2119+
# endif // HAVE_TZSET
2120+
return localtime_r(timep, result);
2121+
#else
2122+
return localtime(timep);
2123+
#endif // HAVE_LOCALTIME_R
2124+
}
2125+
20842126
/*
20852127
* Replacement for ctime(), which is not safe to use.
20862128
* Requires strftime(), otherwise returns "(unknown)".
@@ -2093,16 +2135,10 @@ get_ctime(time_t thetime, int add_newline)
20932135
{
20942136
static char buf[50];
20952137
#ifdef HAVE_STRFTIME
2096-
# ifdef HAVE_LOCALTIME_R
20972138
struct tm tmval;
2098-
# endif
20992139
struct tm *curtime;
21002140

2101-
# ifdef HAVE_LOCALTIME_R
2102-
curtime = localtime_r(&thetime, &tmval);
2103-
# else
2104-
curtime = localtime(&thetime);
2105-
# endif
2141+
curtime = vim_localtime(&thetime, &tmval);
21062142
/* MSVC returns NULL for an invalid value of seconds. */
21072143
if (curtime == NULL)
21082144
vim_strncpy((char_u *)buf, (char_u *)_("(Invalid)"), sizeof(buf) - 1);

src/proto/memline.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ void ml_recover(int checkext);
1313
int recover_names(char_u *fname, int list, int nr, char_u **fname_out);
1414
char_u *make_percent_swname(char_u *dir, char_u *name);
1515
void get_b0_dict(char_u *fname, dict_T *d);
16+
struct tm *vim_localtime(const time_t *timep, struct tm *result);
1617
char *get_ctime(time_t thetime, int add_newline);
1718
void ml_sync_all(int check_file, int check_char);
1819
void ml_preserve(buf_T *buf, int message);

src/testdir/test_functions.vim

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,30 @@ func Test_strftime()
187187

188188
call assert_fails('call strftime([])', 'E730:')
189189
call assert_fails('call strftime("%Y", [])', 'E745:')
190+
191+
" Check that the time changes after we change the timezone
192+
" Save previous timezone value, if any
193+
if exists('$TZ')
194+
let tz = $TZ
195+
endif
196+
197+
" Force EST and then UTC, save the current hour (24-hour clock) for each
198+
let $TZ = 'EST' | let est = strftime('%H')
199+
let $TZ = 'UTC' | let utc = strftime('%H')
200+
201+
" Those hours should be two bytes long, and should not be the same; if they
202+
" are, a tzset(3) call may have failed somewhere
203+
call assert_equal(strlen(est), 2)
204+
call assert_equal(strlen(utc), 2)
205+
call assert_notequal(est, utc)
206+
207+
" If we cached a timezone value, put it back, otherwise clear it
208+
if exists('tz')
209+
let $TZ = tz
210+
else
211+
unlet $TZ
212+
endif
213+
190214
endfunc
191215

192216
func Test_resolve_unix()

src/undo.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3111,18 +3111,12 @@ ex_undolist(exarg_T *eap UNUSED)
31113111
u_add_time(char_u *buf, size_t buflen, time_t tt)
31123112
{
31133113
#ifdef HAVE_STRFTIME
3114-
# ifdef HAVE_LOCALTIME_R
31153114
struct tm tmval;
3116-
# endif
31173115
struct tm *curtime;
31183116

31193117
if (vim_time() - tt >= 100)
31203118
{
3121-
# ifdef HAVE_LOCALTIME_R
3122-
curtime = localtime_r(&tt, &tmval);
3123-
# else
3124-
curtime = localtime(&tt);
3125-
# endif
3119+
curtime = vim_localtime(&tt, &tmval);
31263120
if (vim_time() - tt < (60L * 60L * 12L))
31273121
/* within 12 hours */
31283122
(void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,8 @@ static char *(features[]) =
777777

778778
static int included_patches[] =
779779
{ /* Add new patch number below this line */
780+
/**/
781+
1567,
780782
/**/
781783
1566,
782784
/**/

0 commit comments

Comments
 (0)