调用tzset依然无法刷新时区问题

本文最后更新于:2025年11月19日 下午

问题现象

一个程序运行时,时区信息是固定的。当外部修改时区信息后,程序并不能知道,还是用的缓存的时区。这时程序的时间就是错误的。
一般的解决办法是,进程内调用tzset函数,这可以刷新时区信息,使其得到正确的时间。

问题在于我在程序里面调用了tzset,时间还是没有刷新。

原因

结论:c库代码有点问题。

tzset的glibc实现里面,会先获取环境变量TZ,如果和缓存的没有区别,那么就直接返回了。关键代码(glibc2.3.3)如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static void
internal_function
tzset_internal (always)
int always;
{
static int is_initialized;
register const char *tz;
register size_t l;
char *tzbuf;
unsigned short int hh, mm, ss;
unsigned short int whichrule;

if (is_initialized && !always)
return;
is_initialized = 1;

/* Examine the TZ environment variable. */
tz = getenv ("TZ");
if (tz == NULL)
/* No user specification; use the site-wide default. */
tz = TZDEFAULT;
else if (*tz == '\0')
/* User specified the empty string; use UTC explicitly. */
tz = "Universal";

/* A leading colon means "implementation defined syntax".
We ignore the colon and always use the same algorithm:
try a data file, and if none exists parse the 1003.1 syntax. */
if (tz && *tz == ':')
++tz;

/* Check whether the value changes since the last run. */
if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
/* No change, simply return. */
return;
.............

因为我的进程没有这个TZ环境变量(应该大部分进程都没有,我看我的ubuntu下也没有),所以总是默认值,老的也是默认值。判断相等。直接返回了。

新一点的glibc库就没有这个问题了。根据看代码,glibc > 2.3.3就改了这里

如何解决

只能曲线救国。人为设置TZ环境变量。多调用两次tzset,如下:

1
2
3
4
setenv("TZ", "UTC", 1);
tzset();
unsetenv("TZ");
tzset();

这样就可以通过tzset里面的TZ环境变量判断。

参考

Why does my timezone not update in embedded linux?


人生苦短,远离bug Leon, 2025-01-09

调用tzset依然无法刷新时区问题
https://leon0625.github.io/2025/01/09/71895efc4d6d/
作者
leon.liu
发布于
2025年1月9日
许可协议