Câu trả lời:
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_t
không được đảm bảo trong đặc tả C.
Kiểu
time_t
dữ 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_t
loại dưới dạngsigned 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 bittime_t
dễ bị ảnh hưởng bởi sự cố Năm 2038 .
time_t
trong 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_t
có 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
.
time.h
nộ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
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_t
nó 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_t
không dấu và vẫn tuân thủ POSIX.
[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.h
thô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
typedef __int32_t __time_t;
và 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).
__time_t
và không time_t
tìm thấy loại cơ bản của time_t
? Bỏ qua một bước?
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á.
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
và 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ừ stdingcc -E -xc -include time.h /dev/null | grep time_t
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 printf
lệ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ì?
%zu
xác định định dạng để định dạng size_t
cá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
) ·
printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));
và tránh z
vấn đề này.
Trong Visual Studio 2008, nó mặc định là __int64
trừ 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.
time_t
là loại long int
trê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.h
Và typesizes.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;
long int
ở khắp mọi nơi. Xem stackoverflow.com/questions/384502/
Đâ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.
Thông thường, bạn sẽ tìm thấy các typedefs cụ thể triển khai cơ bản cho gcc trong thư mục bits
hoặc asm
tiêu đề. Đối với tôi, nó là /usr/include/x86_64-linux-gnu/bits/types.h
.
Bạn chỉ có thể grep hoặc sử dụng một lời gọi tiền xử lý như được Quassnoi đề xuất để xem tiêu đề cụ thể nào.
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_t
là một loại thực sự như double, long long, int64_t, int
, vv
Nó thậm chí có thể unsigned
là 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 double
hoặc long double
cũ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);
double difftime(time_t time1, time_t time0)
cho một cách tiếp cận trừ đồng nhất.
time_t
chỉ typedef
dà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_t
trong crtdefs.h
bạ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
.
long int
.