Time_t cuối cùng là một typedef là gì?


Câu trả lời:


175

Các time_t Wikipedia bài viết bài viết làm sáng tỏ về vấn đề này. Điểm mấu chốt là loại time_tkhông được đảm bảo trong đặc tả C.

Kiểu time_tdữ liệu là kiểu dữ liệu trong thư viện ISO C được xác định để lưu trữ giá trị thời gian của hệ thống. Các giá trị này được trả về từ time() hàm thư viện chuẩn . Loại này là một typedef được xác định trong tiêu đề tiêu chuẩn. ISO C định nghĩa time_t là một loại số học, nhưng không chỉ định bất kỳ loại , phạm vi, độ phân giải hoặc mã hóa cụ thể nào cho nó. Cũng không xác định là ý nghĩa của các phép toán số học được áp dụng cho các giá trị thời gian.

Các hệ thống tuân thủ Unix và POSIX triển khai time_tloại dưới dạng signed integer(thường rộng 32 hoặc 64 bit) đại diện cho số giây kể từ khi bắt đầu thời kỳ Unix : nửa đêm UTC ngày 1 tháng 1 năm 1970 (không tính giây nhuận). Một số hệ thống xử lý chính xác các giá trị thời gian âm, trong khi các hệ thống khác thì không. Các hệ thống sử dụng loại 32 bit time_tdễ bị ảnh hưởng bởi sự cố Năm 2038 .


6
Tuy nhiên, lưu ý rằng các giá trị time_t thường chỉ được lưu trữ trong bộ nhớ, không phải trên đĩa. Thay vào đó, time_t được chuyển đổi thành văn bản hoặc một số định dạng di động khác để lưu trữ liên tục. Điều đó làm cho vấn đề Y2038 không thực sự là một vấn đề.

11
@ Bên dưới: trên một hệ thống cụ thể, nơi cùng một người tạo hệ điều hành và thư viện C, sử dụng time_ttrong cấu trúc dữ liệu trên đĩa có thể xảy ra. Tuy nhiên, vì các hệ thống tệp thường được các hệ điều hành khác đọc, nên thật ngu ngốc khi định nghĩa hệ thống tệp dựa trên các loại phụ thuộc thực hiện đó. Ví dụ: cùng một hệ thống tệp có thể được sử dụng trên cả hệ thống 32 bit và 64 bit và time_tcó thể thay đổi kích thước. Do đó, các hệ thống tập tin cần được xác định chính xác hơn ("số nguyên có chữ ký 32 bit cho số giây kể từ đầu năm 1970, trong UTC") chứ không chỉ như time_t.

1
Lưu ý: bài viết Wikipedia được liên kết đã bị xóa và hiện tại nó chuyển hướng đến danh sách time.hnội dung. Bài viết đó liên kết đến cppreference.com nhưng nội dung được trích dẫn không tìm thấy ở đâu
Michał Górny

3
@ MichałGórny: Đã sửa, miễn là bài viết không bị xóa, bạn luôn có thể xem lịch sử để tìm phiên bản chính xác.
Zeta

4
-1; khiếu nại được trích dẫn từ Wikipedia rằng POSIX đảm bảo time_tđược ký là không chính xác. pubs.opengroup.org/onlinepubs/9699919799/basedefs/... mệnh lệnh rằng mọi thứ khác nhau phải là một "ký kiểu số nguyên" hoặc "loại unsigned integer", nhưng time_tnó nói chỉ đơn thuần rằng nó "sẽ trở thành một loại nguyên" . Việc triển khai có thể làm cho time_tkhông dấu và vẫn tuân thủ 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;

Nó được định nghĩa $INCDIR/bits/types.hthông qua:

# 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
Tôi thấy cả hai typedef __int32_t __time_t;typedef __time_t time_t;trong mộ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. Kết quả của bạn được đặt rõ ràng như thế trong Linux (ít nhất là trên 2.6.32-5-xen-amd64 từ Debian).
ssice

1
@Viet cũng có thể với một lớp lót mà không cần tạo tệp: stackoverflow.com/a/36096104/895245
Ciro Santilli 冠状 病 六四 法轮功

1
Tại sao grep cho __time_tvà không time_ttìm thấy loại cơ bản của time_t? Bỏ qua một bước?
chux - Phục hồi Monica

@ chux-ReinstateMonica - OP nói rằng anh ta đã tìm thấy typedef từ time_t đến __time_t. Câu trả lời này chỉ là giải quyết câu hỏi được hỏi về cái gì __time_t được định nghĩa là. Nhưng tôi đồng ý rằng đối với trường hợp chung (trong đó time_t có thể không được nhập vào __time_t), trước tiên bạn cần phải grep cho time_t, và sau đó có thể grep lại cho những gì trả về
Michael Firth

@MichaelFirth Đủ công bằng. Tôi nhớ lại mối quan tâm của mình vì mặc dù OP đã tìm thấy typedef __time_t time_t;, việc kiểm tra mã xung quanh cũng là cần thiết để đảm bảo rằng typedef thực tế đã được sử dụng và không chỉ là một phần của một biên dịch có điều kiện. typedef long time_t;có thể đã được tìm thấy quá.
chux - Tái lập lại

30

Tiêu chuẩn

William Brendel trích dẫn Wikipedia, nhưng tôi thích nó từ miệng ngựa.

Dự thảo tiêu chuẩn C99 N1256 7.23.1 / 3 "Thành phần thời gian" cho biết:

Các loại được khai báo là size_t (được mô tả trong 7.17) clock_t và time_t là các loại số học có khả năng biểu diễn thời gian

6.2.5 / 18 "Loại" cho biết:

Số nguyên và kiểu nổi được gọi chung là kiểu số học.

POSIX 7 sys_types.h nói:

[CX] time_t sẽ là một kiểu số nguyên.

nơi [CX]được định nghĩa là :

[CX] Mở rộng theo tiêu chuẩn ISO C.

Nó là một phần mở rộng vì nó làm cho một sự đảm bảo mạnh mẽ hơn: các điểm nổi bị loại bỏ.

gcc một lớp lót

Không cần tạo tệp như Quassnoi đã đề cập :

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

Trên Ubuntu 15.10 GCC 5.2, hai dòng trên cùng là:

typedef long int __time_t;
typedef __time_t time_t;

Phân tích lệnh với một số trích dẫn từ man gcc:

  • -E: "Dừng sau giai đoạn tiền xử lý; không chạy trình biên dịch thích hợp."
  • -xc: Chỉ định ngôn ngữ C, vì đầu vào đến từ stdin không có phần mở rộng tệp.
  • -include file: "Tệp xử lý như thể" #include "tệp" "xuất hiện dưới dạng dòng đầu tiên của tệp nguồn chính."
  • -: đầu vào từ stdin

1
Không cần đường ống từ tiếng vang:gcc -E -xc -include time.h /dev/null | grep time_t
rvighne

12

Câu trả lời chắc chắn là cụ thể thực hiện. Để tìm hiểu dứt khoát cho nền tảng / trình biên dịch của bạn, chỉ cần thêm đầu ra này vào đâu đó trong mã của bạn:

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

Nếu câu trả lời là 4 (32 bit) và dữ liệu của bạn có nghĩa là vượt quá 2038 , thì bạn có 25 năm để di chuyển mã của mình.

Dữ liệu của bạn sẽ ổn nếu bạn lưu trữ dữ liệu của mình dưới dạng chuỗi, ngay cả khi đó là một thứ đơn giản như:

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

Sau đó, chỉ cần đọc lại theo cùng một cách (fread, fscanf, v.v. vào một int), và bạn có thời gian bù epoch của bạn. Một cách giải quyết tương tự tồn tại trong .Net. Tôi vượt qua các số kỷ nguyên 64 bit giữa các hệ thống Win và Linux mà không gặp vấn đề gì (qua kênh truyền thông). Điều đó mang đến các vấn đề đặt hàng byte, nhưng đó là một chủ đề khác.

Để trả lời truy vấn của paxdiablo, tôi đã nói rằng nó đã in "19100" vì chương trình được viết theo cách này (và tôi thừa nhận tôi đã tự làm điều này trong thập niên 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);

Câu printflệnh in chuỗi cố định "Năm là: 19", theo sau là chuỗi không đệm với "năm kể từ năm 1900" (định nghĩa tm->tm_year). Năm 2000, giá trị đó là 100, rõ ràng. "%02d"miếng đệm có hai số không nhưng không cắt bớt nếu dài hơn hai chữ số.

Cách chính xác là (chỉ thay đổi dòng cuối cùng):

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

Câu hỏi mới: lý do cho suy nghĩ đó là gì?


2
Bạn có thể nên sử dụng trình %zuxác định định dạng để định dạng size_tcác giá trị (như được tạo ra bởi sizeof), vì chúng không dấu ( u) và có độ dài size_t ( z) ·
Adrian Günter

... hoặc sử dụng printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));và tránh zvấn đề này.
chux - Tái lập Monica

6

Trong Visual Studio 2008, nó mặc định là __int64trừ khi bạn xác định _USE_32BIT_TIME_T. Tốt hơn hết là bạn nên giả vờ rằng bạn không biết nó được định nghĩa là gì, vì nó có thể (và sẽ) thay đổi từ nền tảng này sang nền tảng khác.


2
Điều đó thường hoạt động, nhưng nếu chương trình của bạn có nghĩa là theo dõi những điều sẽ xảy ra sau 30 năm kể từ bây giờ, thì điều quan trọng là bạn không có time_t 32 bit đã ký.
Rob Kennedy

4
@Rob, bah, bỏ nó đi! Chúng ta sẽ bắt đầu chạy xung quanh như những con gà không đầu vào năm 2036, giống như chúng ta đã làm cho Y2K. Một số người trong chúng ta sẽ kiếm được rất nhiều tiền từ việc trở thành cố vấn của Y2k38, Leonard Nimoy sẽ đưa ra một cuốn sách vui nhộn khác về cách tất cả chúng ta nên đi và trốn trong rừng ...
paxdiablo

1
... Và tất cả sẽ nổ tung, công chúng tự hỏi tất cả những gì ồn ào. Tôi thậm chí có thể nghỉ hưu để kiếm tiền cho gia tài của những đứa trẻ :-).
paxdiablo

2
BTW, chúng tôi chỉ tìm thấy một lỗi Y2K và đó là một trang web liệt kê ngày là ngày 1 tháng 1 năm 19100. Tập thể dục cho người đọc về lý do tại sao ...
paxdiablo

9
Nếu sự kiện xảy ra trong 30 năm là "hết hạn sao lưu này", thì bạn có thể gặp rắc rối NGAY BÂY GIỜ, không phải vào năm 2038. Thêm 30 năm vào time_t 32 bit ngày nay và bạn sẽ có được một ngày trong quá khứ. Chương trình của bạn tìm kiếm các sự kiện để xử lý, tìm một sự kiện đã quá hạn (trước 100 năm!) Và thực hiện nó. Rất tiếc, không có thêm bản sao lưu.
Rob Kennedy

5

time_tlà loại long inttrên máy 64 bit, khác long long int.

Bạn có thể xác minh điều này trong các tệp tiêu đề này:

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

(Các câu lệnh bên dưới không phải là câu lệnh khác. Chúng có thể được tìm thấy trong tệp tiêu đề resp. Sử dụng tìm kiếm Ctrl + f.)

1 trong time.h

typedef __time_t time_t;

2 trong types.h

# define __STD_TYPE     typedef  
__STD_TYPE __TIME_T_TYPE __time_t;  

3 trong 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) Một lần nữa trong 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;

Những tệp này được cung cấp bởi glibc trên Ubuntu 15.10 BTW.
Ciro Santilli 郝海东 冠状 病 事件 法轮功

3
Nó không phải long intở khắp mọi nơi. Xem stackoverflow.com/questions/384502/
Zan Lynx

4

Đây là loại số nguyên có chữ ký 32 bit trên hầu hết các nền tảng cũ. Tuy nhiên, điều đó khiến mã của bạn bị lỗi từ năm 2038 . Vì vậy, các thư viện C hiện đại nên xác định nó là int 64 bit đã ký thay vào đó, an toàn trong vài tỷ năm.



1

Cuối cùng, một time_t typedef là gì?

Mã mạnh mẽ không quan tâm loại đó là gì.

C time_tlà một loại thực sự như double, long long, int64_t, int, vv

Nó thậm chí có thể unsignedlà giá trị trả về từ nhiều hàm thời gian chỉ ra lỗi là không -1, nhưng (time_t)(-1)- Lựa chọn triển khai này không phổ biến.

Vấn đề là loại "cần biết" là rất hiếm. Mã nên được viết để tránh sự cần thiết.


Tuy nhiên, một "điều cần biết" phổ biến xảy ra khi mã muốn in phần thô time_t. Đúc đến loại số nguyên rộng nhất sẽ phù hợp với hầu hết các trường hợp hiện đại.

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

Truyền tới một doublehoặc long doublecũng sẽ hoạt động, nhưng có thể cung cấp đầu ra thập phân không chính xác

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

Tôi đang cần biết tình huống vì tôi cần chuyển thời gian từ hệ thống ARM sang hệ thống AMD64. time_t là 32 bit trên cánh tay và 64 bit trên máy chủ. nếu tôi dịch thời gian thành một định dạng và gửi một chuỗi, nó không hiệu quả và chậm. Do đó, tốt hơn hết là chỉ gửi toàn bộ time_t và sắp xếp nó ở cuối máy chủ. Tuy nhiên, tôi cần hiểu loại này nhiều hơn một chút vì tôi không muốn số bị xáo trộn bởi sự khác biệt giữa các hệ thống, vì vậy tôi cần sử dụng htonl ... nhưng trước tiên, trên cơ sở cần biết, tôi muốn để tìm ra loại cơ bản;)
Owl

Một trường hợp "cần biết" khác, ít nhất là đối với chữ ký và không dấu là bạn có cần cẩn thận khi trừ đi thời gian không. Nếu bạn chỉ "trừ và in kết quả", thì bạn có thể sẽ nhận được những gì bạn mong đợi trên một hệ thống có dấu thời gian đã ký, nhưng không phải với dấu thời gian không dấu.
Michael Firth

Các trường hợp @MichaelFirth tồn tại cho cả hai số nguyên đã ký và time_t không dấu trong đó phép trừ thô sẽ dẫn đến kết quả không mong muốn. C cung cấp double difftime(time_t time1, time_t time0)cho một cách tiếp cận trừ đồng nhất.
chux - Tái lập lại

-3

time_tchỉ typedefdành cho 8 byte ( long long/__int64) mà tất cả các trình biên dịch và hệ điều hành đều hiểu. Trước đây, nó chỉ dành cho long int(4 byte) nhưng không phải bây giờ. Nếu bạn nhìn vào time_ttrong crtdefs.hbạn sẽ tìm thấy cả hai hiện thực nhưng hệ điều hành sẽ sử dụng long long.


5
tất cả các trình biên dịch và hệ điều hành? Không. Trên hệ thống linux của tôi, trình biên dịch sẽ thực hiện 4 byte đã ký.
Vincent

Trên hệ thống Zynq 7010 time_t là 4byte.

1
Trên các hệ thống nhúng, tôi làm việc trên time_t hầu như luôn luôn là 32 bit hoặc 4 byte. Tiêu chuẩn quy định cụ thể rằng đó là việc thực hiện cụ thể khiến câu trả lời này chỉ sai.
Cobusve
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.