【问题标题】:Format specifier to print time(0) in C在 C 中打印时间(0)的格式说明符
【发布时间】:2014-11-21 09:09:53
【问题描述】:

我们可以声明一个变量来保存系统的当前时间:

time_t now = time(0);

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

#define SEED time(0);
srand((unsigned int )SEED);

我的问题是:time(0) 到底是什么。默认情况下它以毫秒为单位,类型为long?如果我想打印出time(0)的值,可以用"%d"打印出来吗? (Sicne printf() 要求我们传入它需要的类型,但我怎么知道time(0) 的类型是什么?)。

如果是time_t类型,我应该在printf中使用什么格式说明符?

【问题讨论】:

标签: c time types


【解决方案1】:

time_t 由 C 标准、C11、7.27.1/3 定义为:

[...] 是能够表示时间的实数类型;

这意味着它可以是 int、long、unsigned long、double 或任何其他实数类型。基本上这是一个实现的选择。同样,它也没有定义 time_t 返回什么单位,C11, 7.27.2.4/3:

时间函数返回实现的最佳近似值 到当前日历时间。返回值 (time_t)(-1) 如果日历时间不可用。如果计时器不为空 指针,返回值也赋值给它所指向的对象。

您必须阅读您的实现内容。我在我的 Linux 上的 glibc 实现说, time_t 返回的单位是秒。因此,您可以将其转换为 uintmax_t 并打印:

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

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

【讨论】:

  • 我会使用intmax_t 而不是uintmax_t
【解决方案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 或更高版本的实现。如果您遇到不支持&lt;stdint.h&gt; 和/或%ju%jd 格式(您可以通过测试__STDC_VERSION__ 来检测)的C99 之前的实现,您可以转换为@987654329 @ 或 unsigned long 而不是 [u]intmax_t。 (MinGW 可能无法正确处理打印 long double 值,但 MinGW 对 time_t 使用有符号整数类型,所以这不是问题。)

这将打印一个没有意义的原始值,除非您碰巧知道time_t 是如何表示的(它是什么类型它如何表示当前时间)。在我的系统上,当前时间是 1416589039,这是自 1970-01-01 00:00:00 UTC 以来的秒数(一种非常常见的表示)。

如果您想知道现在是几点,而不是time() 函数返回的原始值,您应该使用&lt;time.h&gt; 中的函数来生成人类可读的表示当前时间。例如:

#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

【讨论】:

  • 只是在考虑这种if ((time_t) ... 方法,然后你会弹出很好的答案。顺便说一句:如果没有((time_t)-1 &gt; 0) 中的第二次演员,就无法编码工作?
  • @chux:可能,但恕我直言,演员阵容更清晰。这意味着我不必考虑time_tint 窄的情况(不确定这是否重要,但正如我所说,我不必考虑)。
【解决方案3】:

在大多数系统中,time_t 定义的位数与整数相同,并且始终为正数。

在这种情况下,您可以使用:

 printf("time: %u", (unsigned int) time(0));

【讨论】:

  • 不要使用%dprintfunsigned int,这是未定义的行为。 %u 是正确的。
  • 不正确。 time(0) 如果由于某种原因失败,则返回 (time_t)-1,标准规定 only time_t 是一种能够表示时间的算术类型。在我正在输入此评论的系统上,sizeof (int) == 4sizeof (time_t) == 8
  • printf 的行为是明确定义的 if time_t 是一个整数类型。如果该值为负数或超过UINT_MAX,则输出不会是实际值,但会不断调整。如果time_t 为浮点类型且值为负数或超过UINT_MAX,则转换的行为未定义。
【解决方案4】:

查看时间函数的头文件, 您将看到该参数可以是 NULL 或 time_t 变量的地址两件事之一。

参数和返回值的 time() 函数结果都是自纪元以来经过的时间,以秒为单位(当前为 1970 年 1 月 1 日 00:00 am UTC)。值的大小(在旧系统上) ) 为 4 个字节(32 位)。使用最近可用的 RTC (RealTimeClock) 硬件,值“可能”为 8 个字节(64 位)。在任何情况下,in 的值都是自纪元以来的秒数。

4 字节的时间值将在 2038 年溢出,因此正在努力 1)使时间值更大或 2) 将纪元时间向前移动到 Jan 1 2000 00:00 am。 (使用旧硬件时的临时修复)

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

这是来自谷歌的具体报价:

“在 2038 年 1 月 19 日 03:14:08 UTC,32 位版本的 Unix 时间戳将停止工作”以附加引用的其余部分:32 位值将溢出。

处理时间的函数,例如 localtime() strtotime()。 etc 和相关的结构,如 tm,设置为使用它们运行的​​系统从 time() 函数返回的任何内容。

所有对时间函数值的处理都应该使用可用的函数来完成,而不是处理原始值。

【讨论】:

    【解决方案5】:

    如前所述,标准没有指定 time_t 是什么。如果您想确保以便携的方式使用它,请先将其转换为long,然后将其打印为long。这样,即使 32 位架构当时仍然可用,您的程序也不会遇到 2038 年的问题 :-) ...

    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)&gt;=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 :因为至少在MSVC上,当我直接将负short转换为unsigned long时,实际转换是short -> long(扩展符号)-> unsigned long,并且都是高位当我希望它们保持值 0 时,位会得到值 1。
    • 我想time_t 可能short - 从来没有遇到过。我在 2014 年遇到过 unsigned 是 16 位的,time_t 是 32 位的,所以 (unsigned int) t 会被截断。
    • @chux :你是对的,如果int 只有 16 位,我的公式就会中断 :-( 。当inttime_t 都是 32 位并且 long 是 64 时,我想通过位。
    • 也许long lt = (t &gt; 0) ? t : t * 1u; 可以避免截断?
    猜你喜欢
    • 2021-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多