时间戳和时区是两个概念,互不影响。同一时刻在不同时区获取的时间戳是相同的。比如,同一时刻,北京时间(东 8 区)和东京时间(东 9 区)是不同的,东京时间比北京时间快 1 个小时,这里提到的北京时间和东京时间都是本地时间。在同一时刻,这些本地时间的时间戳是相同的(之前,我就错误的认为时间戳分本地时间戳和 UTC 时间戳,不同时区的时间戳是不同的,直到有次和技术部的平台组对需求后,才明白原来我一直搞错了,惭愧惭愧!)。
什么是时间戳以及如何获取时间戳呢,可以参考 cplusplus time 和 cppreference time 。
下面示例展示了将时间戳 time since epoch 分别转换成以 UTC 和本地时间表示的 calendar time 。由于我处于东 8 区,所以当前时间是 5 月 5 日,而 UTC 时间还是 5 月 4 日。
// gcc -std=gnu11 test.c
#include <stdio.h>
#include <time.h>
void print(struct tm *t, const char *desc)
{
printf("%s: %d-%02d-%02d %02d:%02d:%02d wday:%d yday:%d isdst:%d\n",
desc, 1900+t->tm_year, t->tm_mon, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec,
t->tm_wday, t->tm_yday, t->tm_isdst
);
}
int main() {
time_t ts;
time(&ts);
struct tm utc_tm;
print(gmtime_r(&ts, &utc_tm), "UTC ");
printf("UTC : %s", asctime(gmtime_r(&ts, &utc_tm)));
struct tm local_tm;
print(localtime_r(&ts, &local_tm), "LOCAL");
printf("LOCAL: %s", asctime(localtime_r(&ts, &local_tm)));
// Output:
// UTC : 2020-04-04 16:26:52 wday:1 yday:124 isdst:0
// UTC : Mon May 4 16:26:52 2020
// LOCAL: 2020-04-05 00:26:52 wday:2 yday:125 isdst:0
// LOCAL: Tue May 5 00:26:52 2020
}
时间戳在日常应用中有如下形式:
- 调用
time
函数获取时间戳,然后根据使用 UTC 形式还是本地形式,调用gmtime_r
和localtime_r
进行转换。若要获取字符串形式,可调用asctime
函数或手动格式化(如上述print
函数)。 - 调用
mktime
将本地时间转换成时间戳。
这里有一点要小心,不要被绕进去,调用 time
获取时间戳,然后调用 gentime_r
产生 tm
,此时将 tm
再转成时间戳,这个时间戳和前面 time
返回的时间戳是不同的。如下示例。
// gcc -std=gnu11 test.c
#include <stdio.h>
#include <time.h>
void print(struct tm *t, const char *desc)
{
printf("%s: %d-%02d-%02d %02d:%02d:%02d wday:%d yday:%d isdst:%d\n",
desc, 1900+t->tm_year, t->tm_mon, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec,
t->tm_wday, t->tm_yday, t->tm_isdst
);
}
int main() {
time_t ts;
time(&ts);
struct tm utc_tm;
struct tm local_tm;
print(gmtime_r(&ts, &utc_tm), "BEFORE UTC ");
printf("BEFORE LOCAL:%s", asctime(localtime_r(&ts, &local_tm)));
// 调用 mktime 此时 utc_tm 被认为表示的是本地时间
time_t ts2 = mktime(&utc_tm);
print(gmtime_r(&ts2, &utc_tm), "AFTER UTC ");
printf("AFTER LOCAL :%s", asctime(localtime_r(&ts2, &local_tm)));
return 0;
// Output:
// BEFORE UTC : 2020-04-04 16:48:03 wday:1 yday:124 isdst:0
// BEFORE LOCAL:Tue May 5 00:48:03 2020
// AFTER UTC : 2020-04-04 08:48:03 wday:1 yday:124 isdst:0
// AFTER LOCAL :Mon May 4 16:48:03 2020
}
由此可见,C 标准库其实是把本地时间转换成时间戳。项目中某个这种转换函数,由于没有文档,我一开始不了解函数参数是本地时间,错误传递了 UTC 时间,还产生了 BUG 。下次碰见类似的接口,即使没有文档,也可以根据 C 标准库假定传递的是本地时间。
了解了基本概念后,回头开头所说的时间戳与时区无关。下面通过示例来验证一下。
// gcc -std=gnu11 test.c
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main() {
time_t ts;
time(&ts);
printf("UTC : %s", asctime(gmtime(&ts)));
printf("LOCAL: %s", asctime(localtime(&ts)));
printf("Epoch: %ld\n", ts);
struct tm local_tm = {
.tm_year = 120, .tm_mon = 5, .tm_mday = 5,
.tm_hour = 10, .tm_min = 0, .tm_sec = 0,
.tm_isdst = 0,
};
time_t convert_local_ts = mktime(&local_tm);
printf("CONVERT Epoch: %ld\n", convert_local_ts);
printf("CONVERT UTC : %s", asctime(gmtime(&convert_local_ts)));
printf("\nchange timezone to tokyo:\n");
putenv("TZ=Asia/Tokyo");
printf("UTC : %s", asctime(gmtime(&ts)));
printf("Tokyo: %s", asctime(localtime(&ts)));
time_t tokyo_ts;
time(&tokyo_ts);
printf("UTC : %s", asctime(gmtime(&tokyo_ts)));
printf("Tokyo: %s", asctime(localtime(&tokyo_ts)));
printf("Epoch: %ld\n", tokyo_ts);
struct tm tokyo_tm = {
.tm_year = 120, .tm_mon = 5, .tm_mday = 5,
.tm_hour = 11, .tm_min = 0, .tm_sec = 0,
.tm_isdst = 0,
};
time_t convert_tokyo_ts = mktime(&tokyo_tm);
printf("CONVERT Epoch: %ld\n", convert_tokyo_ts);
printf("CONVERT UTC : %s", asctime(gmtime(&convert_tokyo_ts)));
return 0;
// Output:
// UTC : Tue May 5 01:55:14 2020
// LOCAL: Tue May 5 09:55:14 2020
// Epoch: 1588643714
// CONVERT Epoch: 1591322400
// CONVERT UTC : Fri Jun 5 02:00:00 2020
//
// change timezone to tokyo:
// UTC : Tue May 5 01:55:14 2020
// Tokyo: Tue May 5 10:55:14 2020
// UTC : Tue May 5 01:55:14 2020
// Tokyo: Tue May 5 10:55:14 2020
// Epoch: 1588643714
// CONVERT Epoch: 1591322400
// CONVERT UTC : Fri Jun 5 02:00:00 2020
}
- 观察输出,在
putenv("TZ=Asia/Tokyo");
前的当前时间是Tue May 5 09:55:14 2020
,而之后的当前时间是Tue May 5 10:55:14 2020
,由于东京处于东九区,比北京时间快 1 个小时,所以可以确认切换时区成功。 - 观察
ts
与tokyo_ts
值相同,可以认为时间戳在切换时区前后是相同的。 - 观察
convert_local_ts
与convert_tokyo_ts
值相同,同样验证了时间戳在不同时区是一致的。不同时区的本地时间不同,但在同一时刻的 UTC 时间是一致的,也即时间戳是一致的。
- 什么是闰秒 leaping second ?
- 什么是夏令时 daylight saving time ?
来源:oschina
链接:https://my.oschina.net/iirecord/blog/4266924