在C语言中打印time(0)的格式说明符

3

我们可以使用以下方式声明一个变量来存储系统当前时间:

time_t now = time(0);

time(0)也可以用于生成随机值:

#define SEED time(0);
srand((unsigned int )SEED);
我的问题是:time(0)到底是什么?默认情况下,它是以毫秒为单位的,类型为long吗?如果我想要打印出time(0)的值,我能使用"%d"来打印吗?(因为printf()要求我们传入它所需的类型,但我如何知道time(0)的类型是什么?)
如果它是time_t类型,那么在printf中应该使用什么格式说明符?

1
请参考以下链接:https://dev59.com/anRB5IYBdhLWcg3w6LV3 - Dan D.
这个页面还解释了如何处理返回值:http://www.cplusplus.com/reference/ctime/time/ 它归结为需要使用函数将其转换为tm结构。 - MicroVirus
阅读有关localtime(3)strftime(3)time(7)的内容。 - Basile Starynkevitch
5个回答

5

time_t是由C标准(C11, 7.27.1/3)定义的,它是一种能够表示时间的实数类型。这意味着它可以是int、long、unsigned long、double或任何其他实数类型。基本上这是实现的选择。同样,它也没有定义time_t返回的单位是什么,C11, 7.27.2.4/3:

time函数返回实现对当前日历时间的最佳近似值。如果日历时间不可用,则返回值(time_t)(-1)。如果timer不是空指针,则返回值也分配给它所指向的对象。

您需要查看您的实现文档。我在我的Linux上使用的glibc实现显示,time_t返回的单位是秒。因此,您可以将其转换为uintmax_t并打印:

time_t tvalue = time(0);
printf("%ju", (uintmax_t)tvalue);

或者您可以使用difftime(),它返回一个double作为两个time_t值之间的差异,这样您就不必担心time_t的底层类型。


2
我会使用intmax_t而不是uintmax_t - Keith Thompson

2
标准没有定义time_t的类型;它只需要是一个能够表示时间的真实类型。它可以是整数类型或浮点类型(幸运的是,它不能是复数)。它不一定是秒数、毫秒数或任何简单单位。原则上,它可以使用位范围以二进制编码十进制的顺序表示月份、秒数、天数、小时数、分钟数和年份。最常见的表示是一个32位或64位有符号整数,表示自1970-01-01 00:00:00 UTC以来的秒数。
如果您想要一种完全可移植的打印time_t值的方法,您可以检测time_t的类型是什么:
#include <stdio.h>
#include <time.h>
#include <stdint.h>
int main(void) {
    time_t now = time(NULL);

    printf("At the sound of the tone, the time will be ... \a");

    if ((time_t)1 / 2 != 0) {
        // time_t is a floating-point type; convert to long double
        printf("%Lf\n", (long double)now);
    }
    else if ((time_t)-1 > (time_t)0) {
        // time_t is an unsigned integer type
        printf("%ju\n", (uintmax_t)now);
    }
    else {
        // time_t is a signed integer type
        printf("%jd\n", (intmax_t)now);
    }
}

这假设使用C99或更高版本的实现。如果你被困在不支持<stdint.h>和/或%ju%jd格式的早期C99实现中(你可以通过测试__STDC_VERSION__来检测这一点),你可以将longunsigned long转换而不是[u]intmax_t。(MinGW可能无法正确处理打印long double值,但MinGW对于time_t使用了一个有符号整数类型,因此这不是问题。)
这将打印一个原始值,除非你恰好知道time_t如何表示(包括它是什么类型和如何表示当前时间),否则它没有意义。在我的系统上,当前时间是1416589039,这是自1970-01-01 00:00:00 UTC以来的秒数(一种非常常见的表示方法)。
如果你想知道现在是什么时间而不是time()函数返回的原始值,你应该使用<time.h>中的函数生成当前时间的可读表示。例如:
#include <stdio.h>
#include <time.h>
#include <stdint.h>
int main(void) {
    time_t now = time(NULL);
    char s[100];
    strftime(s, sizeof s, "%F %H:%M:%S %Z", localtime(&now));
    printf("The time is now %s\n", s);
}

目前在我的系统上打印出:

The time is now 2014-11-21 08:57:19 PST

刚刚在思考这个 (time_t) ... 方法,然后你的很好的答案就出现了。顺便说一下:没有第二个转换 ((time_t)-1 > 0) 代码会无法工作吗? - chux - Reinstate Monica
@chux:可能吧,但我个人认为使用强制类型转换更清晰。这样做也意味着我不必考虑time_tint窄的情况(虽然我不确定这是否重要,但正如我所说,我不必考虑它)。 - Keith Thompson

1
在大多数系统中,time_t与整数具有相同的位数,并且始终为正数。
在这种情况下,您可以使用:
 printf("time: %u", (unsigned int) time(0));

1
不要使用%d来将unsigned int传递给printf,这是未定义的行为。正确的方式是使用%u - mch
2
不正确。如果由于某种原因失败,time(0)会返回(time_t)-1,而标准仅表示time_t是一种能够表示时间的算术类型。在我输入这个评论的系统上,sizeof (int) == 4sizeof (time_t) == 8 - Keith Thompson
如果time_t是整数类型,printf的行为是明确定义的。如果值为负数或超过UINT_MAX,输出将不是实际值,但会进行一致的调整。如果time_t是浮点类型且值为负数或超过UINT_MAX,则转换的行为是未定义的。 - Keith Thompson

0
如前所述,标准并未明确规定time_t是什么。如果您想以可移植的方式使用它,请先将其转换为long类型,然后再将其打印为long类型。这样,即使在2038年问题中,32位架构仍然可用,您的程序也不会受到影响 :-) ...
time_t t = time(NULL);
long lt = (t > 0) ? t : (unsigned int) t;
printf("%ld", lt);

对于time_t,它应该适用于32位有符号或无符号整数,或者64位整数。当time_t成为一个负的64位整数时,我的公式将会失效,但是在那个时候请与我联系,我应该足够聪明以找到更好的解决方案 :-)

编辑:

正如chux所指出的,如果int只有16位,这是不好的。这里有一种稳健但不太美观的方式。

union {
    long l;
    unsigned char[1] c;
} ul;
time_t t = time(NULL);
ul.l = 0;
ul.c[0] = 255;
if (ul.l < 0) { /* endianness test */
    ul.l = 0;
    memcpy(&ul.l, t&, sizeof(time_t));
}
else {
    ul.l = 0;
    memcpy(&ul.l + sizeof(long) - sizeof(time_t), &t, sizeof(time_t));
}
printf("%lu", (unsigned long) ul.l);

只要 sizeof(long)>=sizeof(time_t),它应该在任何配置中正常工作。

编辑 2:

chux 的建议似乎适用于任何合理的情况。当然,在某些特殊情况下可能会出现问题,但在正常的实现中不会。

time_t t = time(NULL);
long lt = (t > 0) ? t : t * 1u;
printf("%ld", lt);

应该返回正确的结果。

为什么将 t 强制转换为 unsigned 而不是 unsigned long - chux - Reinstate Monica
@chux:因为至少在MSVC上,当我直接将一个负的short转换为无符号long时,实际的转换是short -> long(扩展符号)-> 无符号long,而且所有高位比特都会被赋值为1,而我希望它们保持为0的值。 - Serge Ballesta
1
我猜 time_t 可能是一个 short - 从来没有遇到过这种情况。我在2014年遇到过一种情况,其中 unsigned 是16位的,而 time_t 是32位的,所以 (unsigned int) t 会截断。 - chux - Reinstate Monica
@chux :你说得对,我的公式在int只有16位时会出错 :-( 。我希望在inttime_t都是32位且long为64位时通过。 - Serge Ballesta
也许可以使用 long lt = (t > 0) ? t : t * 1u; 来避免截断? - chux - Reinstate Monica
显示剩余2条评论

0

查看时间函数的头文件,你会发现参数可以是NULL或者time_t变量的地址之一。

time()函数的参数和返回值都是自纪元(当前为1970年1月1日00:00 UTC)以来经过的秒数。在旧系统上,该值的大小为4字节(32位)。随着最近可用的RTC(实时时钟)硬件,该值“可能”为8字节(64位)。无论如何,该值都是自纪元以来的秒数。

4字节的时间值将在2038年溢出,因此正在努力采取以下措施之一: 1)将时间值扩大为更大的尺寸,或者 2)将纪元时间向前推迟到2000年1月1日00:00 am。(在使用旧硬件时的临时修复方法)

将时间值更改为8字节是当前首选的方法。

这里是谷歌的一个具体引用:

“在2038年1月19日03:14:08 UTC时,32位版本的Unix时间戳将停止工作。”简单概括剩下的引用内容:32位值将溢出。

处理时间的函数,如localtime()和strtotime()等,以及相关的结构体,如tm,都被设置为使用系统从time()函数返回的值。
所有对时间函数返回值的处理应该使用可用的函数而不是处理原始值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接