最终typedef的time_t是什么?


Answers:


175

维基百科time_t文章对此有所启发。底线是time_tC规范中不能保证的类型。

time_t数据类型是用于存储系统时间值所定义的ISO C库中的数据类型。这些值是从标准time() 库函数返回的。此类型是标准标头中定义的typedef。ISO C将time_t定义为算术类型,但未指定任何特定类型,范围,分辨率或编码。还没有指定应用于时间值的算术运算的含义。

兼容Unix和POSIX的系统将time_t类型实现为signed integer(通常为32或64位宽),它表示自Unix纪元开始以来的秒数:1970年1月1日午夜UTC(不计算seconds秒)。一些系统正确处理负时间值,而其他系统则不能。使用32位time_t类型的系统容易受到2038年问题的影响


6
但是请注意,time_t值通常仅存储在内存中,而不存储在磁盘上。而是将time_t转换为文本或其他可移植格式以进行持久存储。这使得Y2038问题并不是真正的问题。

11
@Heath:在特定的系统上,同一个人在其中创建操作系统和C库,time_t可能会在磁盘上使用数据结构。但是,由于文件系统经常被其他操作系统读取,因此基于此类依赖于实现的类型来定义文件系统是很愚蠢的。例如,同一文件系统可能同时在32位和64位系统上使用,并且time_t可能会更改大小。因此,需要更精确地定义文件系统(“ 32位有符号整数给出自1970年初以来的秒数,以UTC开头”),而不是仅仅定义为time_t

1
注意:链接的Wikipedia文章已被删除,现在重定向到目录列表time.h。那篇文章的链接cppreference.com但引用内容无处可寻...
米哈尔戈尔诺-

3
@MichałGórny:固定,只要不删除文章,您始终可以查看历史记录以查找正确的版本。
Zeta 2013年

4
-1; Wikipedia引用的POSIX保证time_t已签名的说法不正确。pubs.opengroup.org/onlinepubs/9699919799/basedefs/…规定各种事物必须是“有符号整数类型”或“无符号整数类型”,但time_t其中仅表示“必须是整数类型”。一个实现可以使time_t未签名,并且仍然符合POSIX。
Mark Amery

112

[root]# cat time.c

#include <time.h>

int main(int argc, char** argv)
{
        time_t test;
        return 0;
}

[root]# gcc -E time.c | grep __time_t

typedef long int __time_t;

$INCDIR/bits/types.h通过以下方式定义:

# 131 "/usr/include/bits/types.h" 3 4
# 1 "/usr/include/bits/typesizes.h" 1 3 4
# 132 "/usr/include/bits/types.h" 2 3 4

1
我看到了typedef __int32_t __time_t;typedef __time_t time_t;FreeBSD freebsd-test 8.2-RELEASE-p2 FreeBSD 8.2-RELEASE-p2 #8: Sun Aug 7 18:23:48 UTC 2011 root@freebsd-test:/usr/obj/usr/src/sys/MYXEN i386。您的结果将像在Linux中那样显式设置(至少在Debian的2.6.32-5-xen-amd64上)。
ssice 2012年

1
@Viet也可以用一行代码,而无需创建一个文件:stackoverflow.com/a/36096104/895245
郝海东冠状病六四事件法轮功西罗桑蒂利

1
为什么要使用grep __time_t而不要time_t查找的底层类型time_t?省略了一步?
chux-恢复莫妮卡

@ chux-ReinstateMonica-OP表示他找到了从time_t到__time_t的typedef。这个答案只是解决了__time_t被定义为的问题。但我同意,对于一般情况(可能无法将time_t键入到__time_t的情况),您需要先grep表示time_t,然后可能再次grep表示返回的内容
Michael Firth,

@MichaelFirth足够公平。我记得我的担心,即使OP找到了typedef __time_t time_t;,也需要检查周围的代码以确保实际上使用了typedef,而不仅仅是条件编译的一部分。 typedef long time_t;可能也找到了。
chux-恢复莫妮卡

30

标准品

威廉·布伦德尔William Brendel)引用了维基百科,但从马的嘴里我更喜欢它。

C99 N1256标准草案 7.23.1 / 3“时间的组成部分”表示:

声明的类型为size_t(在7.17中描述)clock_t和time_t,它们是能够表示时间的算术类型

6.2.5 / 18“类型”说:

整数和浮点类型统称为算术类型。

POSIX 7 sys_types.h说:

[CX] time_t应为整数类型。

其中[CX]定义为

[CX]对ISO C标准的扩展。

这是一个扩展,因为它可以提供更强的保证:浮点数已经存在。

海湾合作委员会一线

无需创建Quassnoi提到的文件:

echo | gcc -E -xc -include 'time.h' - | grep time_t

在Ubuntu 15.10 GCC 5.2上,前两行是:

typedef long int __time_t;
typedef __time_t time_t;

命令分解,并带有以下引号man gcc

  • -E:“在预处理阶段之后停止;请不要正确运行编译器。”
  • -xc:指定C语言,因为输入来自没有文件扩展名的stdin。
  • -include file:“处理文件,就像” #include“ file”“一样显示为主要源文件的第一行。
  • -:来自stdin的输入

1
也不需要回声gcc -E -xc -include time.h /dev/null | grep time_t
传递

12

答案肯定是特定于实现的。要明确地为您的平台/编译器找到信息,只需将以下输出添加到代码中:

printf ("sizeof time_t is: %d\n", sizeof(time_t));

如果答案是4(32位),并且您的数据将超过2038年,那么您有25年的时间来移植代码。

如果将数据存储为字符串,则数据就可以了,即使它很简单,例如:

FILE *stream = [stream file pointer that you've opened correctly];
fprintf (stream, "%d\n", (int)time_t);

然后以相同的方式将其读回(将fread,fscanf等转换为int),就可以得到时间偏移时间。.Net中存在类似的解决方法。我在Win和Linux系统之间毫无问题地(通过通信通道)传递了64位纪元数。这带来了字节顺序问题,但这是另一个主题。

为了回答paxdiablo的查询,我想说它打印为“ 19100”,因为该程序是用这种方式编写的(我承认我是在80年代自己做的):

time_t now;
struct tm local_date_time;
now = time(NULL);
// convert, then copy internal object to our object
memcpy (&local_date_time, localtime(&now), sizeof(local_date_time));
printf ("Year is: 19%02d\n", local_date_time.tm_year);

printf语句显示固定字符串“ Year is:19”,后跟零填充字符串,并带有“ years since 1900”(的定义tm->tm_year)。显然,在2000年,该值为100。"%02d"填充两个零,但如果长度超过两位,则不会截断。

正确的方法是(仅更改为最后一行):

printf ("Year is: %d\n", local_date_time.tm_year + 1900);

新问题:这种想法的依据是什么?


2
您可能应该使用%zu格式说明符来格式化size_t值(由产生sizeof),因为它们是无符号(u)且长度为size_t(z)·
AdrianGünter2014年

...或使用printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));并避免该z问题。
chux-恢复莫妮卡

6

在Visual Studio 2008下,__int64除非您定义,否则默认为_USE_32BIT_TIME_T。您最好假装不知道其定义是什么,因为它可以(并将)在不同平台之间变化。


2
通常这是可行的,但是如果您的程序是要跟踪从现在起30年后将发生的事情,那么没有签名的32位time_t 非常重要。
罗伯·肯尼迪

4
@Rob,ba,离开!我们将在2036年像无头鸡一样开始四处奔走,就像我们在Y2K中一样。我们中的某些人将成为Y2k38顾问,从而赚到一大笔钱,伦纳德·尼莫伊(Leonard Nimoy)将带出另一本有趣的书,讲述我们应该如何去躲藏在森林里……
paxdiablo

1
...这一切都会吹过来,公众想知道这些大惊小怪是什么。我什至可能是退休后为孩子们的遗产赚些钱:-)。
paxdiablo

2
顺便说一句,我们只发现了一个Y2K错误,那是一个网页,其中列出了日期为19100年1月1日的内容。请读者练习为什么...
paxdiablo

9
如果30年后发生的事件是“使此备份过期”,那么您现在可能会遇到麻烦,而不是2038年。在今天的32位time_t中加上30年,您会得到过去的日期。您的程序查找要处理的事件,找到过期的事件(到100年!),然后执行它。糟糕,没有更多备份。
罗布·肯尼迪

5

time_tlong int64位计算机上的类型,否则为long long int

您可以在以下头文件中对此进行验证:

time.h/usr/include
types.htypesizes.h/usr/include/x86_64-linux-gnu/bits

(下面的语句不是一个接一个的。可以使用Ctrl + f搜索在相应的头文件中找到它们。)

1)在 time.h

typedef __time_t time_t;

2)在 types.h

# define __STD_TYPE     typedef  
__STD_TYPE __TIME_T_TYPE __time_t;  

3)在 typesizes.h

#define __TIME_T_TYPE       __SYSCALL_SLONG_TYPE  
#if defined __x86_64__ && defined __ILP32__  
# define __SYSCALL_SLONG_TYPE   __SQUAD_TYPE  
#else
# define __SYSCALL_SLONG_TYPE   __SLONGWORD_TYPE
#endif  

4)再次在 types.h

#define __SLONGWORD_TYPE    long int
#if __WORDSIZE == 32
# define __SQUAD_TYPE       __quad_t
#elif __WORDSIZE == 64
# define __SQUAD_TYPE       long int  

#if __WORDSIZE == 64
typedef long int __quad_t;  
#else
__extension__ typedef long long int __quad_t;

这些文件由Ubuntu 15.10 BTW上的glibc提供。
西罗Santilli郝海东冠状病六四事件法轮功

3
long int到处都不是。见stackoverflow.com/questions/384502/...
咱山猫

4

在大多数旧版平台上,这是32位有符号整数类型。但是,这会使您的代码遭受2038年bug的困扰。因此,现代C库应该将其定义为带符号的64位int,这可以安全使用数十亿年。


3

通常,您会在bitsasm标头目录中找到gcc的这些特定于底层实现的特定typedef 。对我来说,这是/usr/include/x86_64-linux-gnu/bits/types.h

您可以只是grep,也可以使用Quassnoi建议预处理程序调用来查看哪个特定的标头。


1

最终time_t typedef到底要做什么?

健壮的代码不在乎类型是什么。

C种time_t是像等的真实类型double, long long, int64_t, int

它甚至可能是unsigned来自多个时间函数的返回值,表明错误不是-1,而是(time_t)(-1)-此实现选择并不常见。

关键是这种“需要知道”的类型很少见。应该编写代码以避免这种需要。


但是,当代码想要打印原始文件时,就会发生一种常见的“需要知道”的事情time_t。转换为最宽的整数类型将适应大多数现代情况。

time_t now = 0;
time(&now);
printf("%jd", (intmax_t) now);
// or 
printf("%lld", (long long) now);

强制转换为doublelong double也可以,但可能会提供不精确的十进制输出

printf("%.16e", (double) now);

我需要了解情况,因为我需要将时间从ARM系统转移到AMD64系统。time_t在臂上是32位,在服务器上是64位。如果我将时间转换成一种格式,然后发送一个字符串,则效率低下且速度缓慢。因此,最好只发送整个time_t并在服务器端将其整理出来。但是,我需要更多地了解类型,因为我不希望数字因系统之间不同的字节序而混乱,因此我需要使用htonl ...但是首先,在需要了解的基础上,我想找出底层类型;)
猫头鹰

另一个“需要知道”的情况(至少对于带符号的还是无符号的)是在减去时间时是否需要注意。如果只是“减去并打印结果”,则可能会在带签名的time_t而不带未签名的time_t的系统上获得期望的结果。
Michael Firth

@MichaelFirth整数有符号的time_t和无符号的time_t都存在情况,其中原始减法会导致意外结果。C提供double difftime(time_t time1, time_t time0)了统一的减法方法。
chux-恢复莫妮卡

-3

time_ttypedef用于long long/__int64所有编译器和OS都理解的8个字节()。过去,它过去只是用于long int(4个字节),但现在不是。如果您在time_tcrtdefs.h找到,则会找到这两种实现,但OS会使用long long


5
所有的编译器和操作系统?否。在我的Linux系统上,编译器采用4字节带符号的实现。
文森特

在Zynq 7010系统上,time_t为4字节。
猫头鹰

1
在嵌入式系统上,我使用的time_t几乎总是32位或4字节。该标准特别指出,它是特定于实现的,这使得该答案是错误的。
Cobusve
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.