C 如何撤消由TZ环境变量和tzset()函数引起的更改?
我有适用于特定时区的逻辑单元测试代码。理想情况下,单元测试可执行文件将始终以相同的行为运行,并与系统时区设置隔离 换句话说,如果系统时间是美国中部,则单元测试通过,但如果是亚洲/东京(例如),则单元测试失败,目标是使单元测试始终通过 我知道我可以使用C 如何撤消由TZ环境变量和tzset()函数引起的更改?,c,C,我有适用于特定时区的逻辑单元测试代码。理想情况下,单元测试可执行文件将始终以相同的行为运行,并与系统时区设置隔离 换句话说,如果系统时间是美国中部,则单元测试通过,但如果是亚洲/东京(例如),则单元测试失败,目标是使单元测试始终通过 我知道我可以使用setenv()或putenv()设置TZ环境变量,然后调用tzset()函数,这将根据新时区影响mktime()和localtime()的结果。您可以设置的示例如下: TZ的一般格式如下所述: 因此,可以使单元测试可执行文件具有mktime()
setenv()
或putenv()
设置TZ
环境变量,然后调用tzset()
函数,这将根据新时区影响mktime()
和localtime()
的结果。您可以设置的示例如下:
TZ
的一般格式如下所述:
因此,可以使单元测试可执行文件具有mktime()
和localtime()
的行为,就像系统是America/Chicago
,具有:TZ=CST+6CDT,M3.2.0,M11.1.0
(DST开始于3月的第二个星期日,结束于11月的第一个星期日,标准偏移量为UTC-6)
同样的代码可以在Windows和Linux上运行
但是,当您在可执行文件中执行此操作时,它会影响整个代码中的所有mktime()
和localtime()
调用,以完成剩余的执行(而不仅仅是特定的测试)
我的问题是如何撤消更改,以便稍后执行的其他使用time.h local time函数的代码使用系统时间设置(例如日志消息中的时间戳)
互联网上有一些例子,可以围绕测试代码保存/恢复TZ
环境变量。但是,如果TZ
环境变量最初为空,该怎么办
有没有办法用当前系统时间设置的正确值填充TZ
变量?或者更直接地说,是否有方法恢复由TZ
和tzset()引起的任何更改代码>,然后返回到进程启动时的状态
如果没有POSIX/ANSIC方法,是否有任何特定于Windows和/或Linux的API来撤消更改
这是一个测试程序。它只需将TZ
设置为空字符串,然后调用tzset()
即可撤消Windows上的更改,但Linux上则不行
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#ifdef _WIN32
#include <Windows.h>
#endif
const char *Europe_Istanbul = "EET-2EEST,M3.5.0,M10.5.0";
const char *original_tz;
int set_env(const char *name, const char *value)
{
#ifdef _WIN32
return _putenv_s(name, value == NULL ? "" : value);
#else
if (value == NULL)
return unsetenv(name);
else
return setenv(name, value, 1);
#endif
}
void log_time(time_t t)
{
struct tm T;
char timestamp[128] = "";
T = *localtime(&t);
strftime(timestamp, sizeof(timestamp), "%FT%T%z", &T);
printf("%s\n", timestamp);
}
int main(void)
{
time_t current_time = time(NULL);
/* save TZ */
original_tz = getenv("TZ");
printf("TZ=%s\n", original_tz);
log_time(current_time);
set_env("TZ", Europe_Istanbul);
tzset();
log_time(current_time);
/* restore TZ */
set_env("TZ", original_tz);
tzset();
log_time(current_time);
return 0;
}
Windows上的输出(带TZ)
Linux上的输出(无TZ):
Linux上的输出(带有TZ):
传统的C/Unix时区API有一个非常严重的基本限制。不,除了保存和恢复TZ并再次调用tzset
之外,没有更好的方法了。您真正想要的是BSDtzalloc
、localtime\u rz
和mktime\u z
函数。您可以fork()
进行测试,但这有点极端,在不支持fork()的系统上不起作用
。保存和恢复TZ的最大问题似乎是TZ最初可能根本没有设置。我认为(但我还没有证实)将TZ设置为“”等于根本没有设置TZ。或者,至少在Unix下,我非常确定有一种方法(尽管它可能不推荐使用/不标准)可以重置整个环境和/或删除/取消设置环境变量。您不能old timezone=getenv()
+setenv(新时区)
+执行此任务+setenv(旧时区)
?TZ可以是空的吗?您说过将TZ
设置为空字符串会撤消Windows上的更改,但我认为这是错误的。它试图将时区设置为操作系统设置的时区。在Linux上,它将时区设置为UTC。传统的C/Unix时区API存在一个非常严重的基本限制。不,除了保存和恢复TZ并再次调用tzset
之外,没有更好的方法了。您真正想要的是BSDtzalloc
、localtime\u rz
和mktime\u z
函数。您可以fork()
进行测试,但这有点极端,在不支持fork()的系统上不起作用
。保存和恢复TZ的最大问题似乎是TZ最初可能根本没有设置。我认为(但我还没有证实)将TZ设置为“”等于根本没有设置TZ。或者,至少在Unix下,我非常确定有一种方法(尽管它可能不推荐使用/不标准)可以重置整个环境和/或删除/取消设置环境变量。您不能old timezone=getenv()
+setenv(新时区)
+执行此任务+setenv(旧时区)
?TZ可以是空的吗?您说过将TZ
设置为空字符串会撤消Windows上的更改,但我认为这是错误的。它试图将时区设置为操作系统设置的时区。在Linux上,它将时区设置为UTC。
D:\>echo %TZ%
%TZ%
D:\>test_tz
TZ=(null)
2019-11-26T11:16:58-0600
2019-11-26T19:16:58+0200
2019-11-26T11:16:58-0600
D:\>set TZ=EST+5EDT,M3.2.0,M11.1.0
D:\>echo %TZ%
EST+5EDT,M3.2.0,M11.1.0
D:\>test_tz
TZ=EST+5EDT,M3.2.0,M11.1.0
2019-11-26T12:17:59-0500
2019-11-26T19:17:59+0200
2019-11-26T12:17:59-0500
[user@host dir]$ echo $TZ
[user@host dir]$ ./test_tz
TZ=(null)
2019-11-26T11:23:18-0600
2019-11-26T19:23:18+0200
2019-11-26T11:23:18-0600
[user@host dir]$ export TZ=EST+5EDT,M3.2.0,M11.1.0
[user@host dir]$ echo $TZ
EST+5EDT,M3.2.0,M11.1.0
[user@host dir]$ ./test_tz
TZ=EST+5EDT,M3.2.0,M11.1.0
2019-11-26T12:25:20-0500
2019-11-26T19:25:20+0200
2019-11-26T12:25:20-0500