mktime() 用于非本地时区
mktime() for non-local timezone
在 C 中,函数 mktime() returns 根据本地时区的纪元时间(输入结构是本地格式化的)。
函数 timegm() returns 根据 UTC 时间的纪元时间(输入结构基于 UTC 时间格式化)。
函数 localtime_r () 接收纪元时间和 returns 本地时区格式的结构。
函数gmtime_r () 接收纪元时间和returns UTC 格式的结构。
我需要查明非本地时区当前是否为夏令时,如果它是本地时区,则可以使用 localtime_r() 函数,但如果不是本地时区怎么办?
gmtime_r() 函数总是将 tm_isdst 字段设置为零,这在此处不起作用。
也许还有其他一些我不知道的功能。不确定。
I need to find out if a non-local timezone is currently daylight savings time or not
- 从
time
. 获取当前时间作为 time_t
- 使用
putenv
. 将环境变量 TZ
设置为目标时区
- 呼叫
tzset
。 (我不知道这是否是必需的,但调用它肯定没有坏处。)
- 根据(修改后的)当地时区,使用
localtime
将time_t
转换为struct tm
。
- 检查
struct tm
的 tm_isdst
字段。
这是我十年前写的一些代码,它可以完成 ikegami suggests in their :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static void time_convert(time_t t0, char const *tz_value)
{
char old_tz[64] = "-none-";
char *tz = getenv("TZ");
if (tz != 0)
strcpy(old_tz, tz);
setenv("TZ", tz_value, 1);
tzset();
char new_tz[64];
strcpy(new_tz, getenv("TZ"));
char buffer[64];
struct tm *lt = localtime(&t0);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
if (strcmp(old_tz, "-none-") == 0)
unsetenv("TZ");
else
setenv("TZ", old_tz, 1);
tzset();
printf("%lld = %s (TZ=%s, DST = %d)\n",
(long long)t0, buffer, new_tz, lt->tm_isdst);
}
int main(void)
{
time_t t0 = time(0);
char *tz = getenv("TZ");
if (tz != 0)
time_convert(t0, tz);
time_convert(t0, "UTC0");
time_convert(t0, "IST-5:30");
time_convert(t0, "EST5");
time_convert(t0, "EST5EDT");
time_convert(t0, "PST8");
time_convert(t0, "PST8PDT");
}
我得到的输出是:
1650647033 = 2022-04-22 17:03:53 (TZ=UTC0, DST = 0)
1650647033 = 2022-04-22 22:33:53 (TZ=IST-5:30, DST = 0)
1650647033 = 2022-04-22 12:03:53 (TZ=EST5, DST = 0)
1650647033 = 2022-04-22 13:03:53 (TZ=EST5EDT, DST = 1)
1650647033 = 2022-04-22 09:03:53 (TZ=PST8, DST = 0)
1650647033 = 2022-04-22 10:03:53 (TZ=PST8PDT, DST = 1)
请注意,某些时区没有指定夏令时,代码报告这些时区 DST = 0
。
小心在 multi-threaded 应用程序中像这样更改时区。并且在重置环境时要小心,以防你 fork()
和 exec()
其他程序的 TZ 环境变量具有意外值。
注意:我已经将代码修改为:
- 使用
long long
而不是 long
来打印 time_t
值。令人恼火的事情之一是没有标准格式字符串用于将 time_t
类型打印为整数(事实上,C 标准甚至不保证类型是整数,但它实际上在大多数系统上都是,并且 POSIX 要求它是整数类型 — 请参阅 <sys/types.h>
)。此更改应允许 32 位系统在 2038 年工作。假设 time_t
是 64 位值,即使它是 32 位系统;如果系统仍然使用 32 位 time_t
,当 time_t
环绕到 -231 时,它最终会被破坏——但那不应该仍然是我的那么问题来了。
- 打印
lt->tm_isdst
这是问题中想要的信息。
如果您 (a) 不想弄乱全局环境变量并且 (b) 有“受 NetBSD 启发”的时间函数可供您使用,还有一种可能性:mktime_z()
和 localtime_rz()
,它让您明确指定要使用的区域。因此,您不限于默认本地区域或 UTC。
这是一个例子:
int main(int argc, char **argv)
{
timezone_t tzp = tzalloc(argv[1]);
if(tzp == NULL) return 1;
time_t now = time(NULL);
struct tm tm;
struct tm *tmp = localtime_rz(tzp, &now, &tm);
char tmpbuf[20];
strftime(tmpbuf, sizeof(tmpbuf), "%H:%M:%S", tmp);
printf("right now in zone %s is %s\n", argv[1], tmpbuf);
tm.tm_year = 1976 - 1900;
tm.tm_mon = 7 - 1;
tm.tm_mday = 4;
tm.tm_hour = 12;
tm.tm_min = tm.tm_sec = 0;
tm.tm_isdst = -1;
time_t t = mktime_z(tzp, &tm);
printf("in zone %s, %d-%02d-%02d %d:%02d was %ld\n", argv[1],
1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, t);
}
当我调用 tzt America/New_York
时,我看到
right now in zone America/New_York is 11:58:23
in zone America/New_York, 1976-07-04 12:00 was 205344000
当我调用 tzt America/Los_Angeles
时,我看到了
right now in zone America/Los_Angeles is 08:58:49
in zone America/Los_Angeles, 1976-07-04 12:00 was 205354800
现在,话虽如此,还有两条评论与我的开头“如果”相关:
一个。如果您不想弄乱全局环境变量,我一点也不会责怪您。我肯定 讨厌 使用全局变量(更不用说环境变量)来影响像 mktime
或 localtime
这样的函数的行为。然而不幸的是,在这种情况下,在 C 语言中,这是推荐的方式 — 有关详细信息,请参阅此问题的其他答案 , 。
b。不幸的是,您 没有 的可能性很大,事实上,“您可以使用受 NetBSD 启发的时间函数”。它们是非标准的,甚至不是很受欢迎。我能够编译上面的测试程序只是因为我手边有一份 IANA tz database 及其代码,其中包括那些函数 if 你还定义了 NETBSD_INSPIRED
. (这就是为什么我违反了规则,没有展示一个完整的例子,所有 #include
行,因为我的很奇怪和特殊。)
在 C 中,函数 mktime() returns 根据本地时区的纪元时间(输入结构是本地格式化的)。
函数 timegm() returns 根据 UTC 时间的纪元时间(输入结构基于 UTC 时间格式化)。
函数 localtime_r () 接收纪元时间和 returns 本地时区格式的结构。
函数gmtime_r () 接收纪元时间和returns UTC 格式的结构。
我需要查明非本地时区当前是否为夏令时,如果它是本地时区,则可以使用 localtime_r() 函数,但如果不是本地时区怎么办?
gmtime_r() 函数总是将 tm_isdst 字段设置为零,这在此处不起作用。
也许还有其他一些我不知道的功能。不确定。
I need to find out if a non-local timezone is currently daylight savings time or not
- 从
time
. 获取当前时间作为 - 使用
putenv
. 将环境变量 - 呼叫
tzset
。 (我不知道这是否是必需的,但调用它肯定没有坏处。) - 根据(修改后的)当地时区,使用
localtime
将time_t
转换为struct tm
。 - 检查
struct tm
的tm_isdst
字段。
time_t
TZ
设置为目标时区
这是我十年前写的一些代码,它可以完成 ikegami suggests in their
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static void time_convert(time_t t0, char const *tz_value)
{
char old_tz[64] = "-none-";
char *tz = getenv("TZ");
if (tz != 0)
strcpy(old_tz, tz);
setenv("TZ", tz_value, 1);
tzset();
char new_tz[64];
strcpy(new_tz, getenv("TZ"));
char buffer[64];
struct tm *lt = localtime(&t0);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
if (strcmp(old_tz, "-none-") == 0)
unsetenv("TZ");
else
setenv("TZ", old_tz, 1);
tzset();
printf("%lld = %s (TZ=%s, DST = %d)\n",
(long long)t0, buffer, new_tz, lt->tm_isdst);
}
int main(void)
{
time_t t0 = time(0);
char *tz = getenv("TZ");
if (tz != 0)
time_convert(t0, tz);
time_convert(t0, "UTC0");
time_convert(t0, "IST-5:30");
time_convert(t0, "EST5");
time_convert(t0, "EST5EDT");
time_convert(t0, "PST8");
time_convert(t0, "PST8PDT");
}
我得到的输出是:
1650647033 = 2022-04-22 17:03:53 (TZ=UTC0, DST = 0)
1650647033 = 2022-04-22 22:33:53 (TZ=IST-5:30, DST = 0)
1650647033 = 2022-04-22 12:03:53 (TZ=EST5, DST = 0)
1650647033 = 2022-04-22 13:03:53 (TZ=EST5EDT, DST = 1)
1650647033 = 2022-04-22 09:03:53 (TZ=PST8, DST = 0)
1650647033 = 2022-04-22 10:03:53 (TZ=PST8PDT, DST = 1)
请注意,某些时区没有指定夏令时,代码报告这些时区 DST = 0
。
小心在 multi-threaded 应用程序中像这样更改时区。并且在重置环境时要小心,以防你 fork()
和 exec()
其他程序的 TZ 环境变量具有意外值。
注意:我已经将代码修改为:
- 使用
long long
而不是long
来打印time_t
值。令人恼火的事情之一是没有标准格式字符串用于将time_t
类型打印为整数(事实上,C 标准甚至不保证类型是整数,但它实际上在大多数系统上都是,并且 POSIX 要求它是整数类型 — 请参阅<sys/types.h>
)。此更改应允许 32 位系统在 2038 年工作。假设time_t
是 64 位值,即使它是 32 位系统;如果系统仍然使用 32 位time_t
,当time_t
环绕到 -231 时,它最终会被破坏——但那不应该仍然是我的那么问题来了。 - 打印
lt->tm_isdst
这是问题中想要的信息。
如果您 (a) 不想弄乱全局环境变量并且 (b) 有“受 NetBSD 启发”的时间函数可供您使用,还有一种可能性:mktime_z()
和 localtime_rz()
,它让您明确指定要使用的区域。因此,您不限于默认本地区域或 UTC。
这是一个例子:
int main(int argc, char **argv)
{
timezone_t tzp = tzalloc(argv[1]);
if(tzp == NULL) return 1;
time_t now = time(NULL);
struct tm tm;
struct tm *tmp = localtime_rz(tzp, &now, &tm);
char tmpbuf[20];
strftime(tmpbuf, sizeof(tmpbuf), "%H:%M:%S", tmp);
printf("right now in zone %s is %s\n", argv[1], tmpbuf);
tm.tm_year = 1976 - 1900;
tm.tm_mon = 7 - 1;
tm.tm_mday = 4;
tm.tm_hour = 12;
tm.tm_min = tm.tm_sec = 0;
tm.tm_isdst = -1;
time_t t = mktime_z(tzp, &tm);
printf("in zone %s, %d-%02d-%02d %d:%02d was %ld\n", argv[1],
1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, t);
}
当我调用 tzt America/New_York
时,我看到
right now in zone America/New_York is 11:58:23
in zone America/New_York, 1976-07-04 12:00 was 205344000
当我调用 tzt America/Los_Angeles
时,我看到了
right now in zone America/Los_Angeles is 08:58:49
in zone America/Los_Angeles, 1976-07-04 12:00 was 205354800
现在,话虽如此,还有两条评论与我的开头“如果”相关:
一个。如果您不想弄乱全局环境变量,我一点也不会责怪您。我肯定 讨厌 使用全局变量(更不用说环境变量)来影响像 mktime
或 localtime
这样的函数的行为。然而不幸的是,在这种情况下,在 C 语言中,这是推荐的方式 — 有关详细信息,请参阅此问题的其他答案
b。不幸的是,您 没有 的可能性很大,事实上,“您可以使用受 NetBSD 启发的时间函数”。它们是非标准的,甚至不是很受欢迎。我能够编译上面的测试程序只是因为我手边有一份 IANA tz database 及其代码,其中包括那些函数 if 你还定义了 NETBSD_INSPIRED
. (这就是为什么我违反了规则,没有展示一个完整的例子,所有 #include
行,因为我的很奇怪和特殊。)