time_t คืออะไรในที่สุด typedef ไป?


คำตอบ:


175

time_t วิกิพีเดียบทความบทความหายไฟบางส่วนเกี่ยวกับเรื่องนี้ บรรทัดล่างคือชนิดของtime_tไม่รับประกันในข้อกำหนด C

time_tประเภทข้อมูลเป็นชนิดข้อมูลในห้องสมุด ISO C ที่กำหนดไว้สำหรับการจัดเก็บค่าเวลาของระบบ ค่าดังกล่าวถูกส่งคืนจากtime() ฟังก์ชันไลบรารีมาตรฐาน ประเภทนี้เป็น typedef ที่กำหนดไว้ในส่วนหัวมาตรฐาน ISO C กำหนด time_t เป็นประเภทคณิตศาสตร์ แต่ไม่ได้ระบุประเภทช่วงความละเอียดหรือการเข้ารหัสเฉพาะใด ๆ ยังไม่ได้ระบุความหมายของการดำเนินการทางคณิตศาสตร์ที่ใช้กับค่าเวลา

ระบบที่สอดคล้องกับ Unix และ POSIX ใช้time_tชนิดเป็นsigned integer(กว้าง 32 หรือ 64 บิต) ซึ่งแสดงถึงจำนวนวินาทีตั้งแต่เริ่มต้นของยุค Unix : เที่ยงคืน UTC วันที่ 1 มกราคม 1970 (ไม่นับวินาทีกระโดด) บางระบบจัดการค่าเวลาเชิงลบได้อย่างถูกต้องในขณะที่ระบบอื่นไม่ทำ โดยใช้ระบบ 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 แต่เนื้อหาที่อ้างถึงไม่มีที่ไหนเลยที่จะพบ ...
MichałGórny

3
@ MichałGórny: แก้ไขตราบใดที่บทความไม่ถูกลบคุณสามารถดูประวัติเพื่อค้นหารุ่นที่ถูกต้องได้เสมอ
ซีตา

4
-1; การอ้างสิทธิ์ที่ยกมาจากวิกิพีเดียว่า 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 (อย่างน้อย 2.6.32-5-xen-amd64 จาก Debian)
ssice

1
@Viet ยังเป็นไปได้ด้วยหนึ่งซับโดยไม่ต้องสร้างไฟล์: stackoverflow.com/a/36096104/895245
Ciro Santilli 郝海东冠状病病六四法轮功法轮功

1
ทำไม grep สำหรับ__time_tและไม่time_tพบประเภทพื้นฐานtime_t? หากข้ามขั้นตอนหนึ่งไป
chux - Reinstate Monica

@ chux-ReinstateMonica - OP กล่าวว่าเขาได้พบ typedef จาก time_t ถึง __time_t คำตอบนี้เป็นเพียงการตอบคำถามที่ถามถึงสิ่งที่ __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 - Reinstate Monica

31

มาตรฐาน

William Brendelอ้างถึง Wikipedia แต่ฉันชอบมันมากกว่าจากปากม้า

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

มันเป็นส่วนขยายเพราะมันทำให้การรับประกันที่แข็งแกร่ง: จุดลอยออก

gcc หนึ่งซับ

ไม่จำเป็นต้องสร้างไฟล์ตามที่ 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
rvighne

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 ฉันผ่านหมายเลข epoch 64 บิตระหว่างระบบ Win และ Linux โดยไม่มีปัญหา (ผ่านช่องทางการสื่อสาร) นั่นทำให้เกิดปัญหาการสั่งซื้อแบบไบต์ แต่นั่นเป็นอีกเรื่อง

เพื่อตอบคำถามของ 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คำสั่งพิมพ์สตริงคงที่ "ปีคือ 19" ตามด้วยสตริงศูนย์เบาะกับ "ปีนับตั้งแต่ปี 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) ·
Adrian Günter

... หรือใช้printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));และหลีกเลี่ยงzปัญหา
chux - Reinstate Monica

6

ภายใต้ Visual Studio 2008 เริ่มต้นไปยังเว้นแต่คุณกำหนด__int64 _USE_32BIT_TIME_Tคุณดีกว่าแค่ทำท่าว่าคุณไม่รู้ว่ามันนิยามอะไรเพราะมันสามารถ (และจะ) เปลี่ยนจากแพลตฟอร์มเป็นแพลตฟอร์ม


2
ปกติแล้วจะใช้งานได้ แต่ถ้าโปรแกรมของคุณตั้งใจติดตามสิ่งต่าง ๆ ที่จะเกิดขึ้น 30 ปีนับจากนี้มันเป็นเรื่องสำคัญที่คุณจะต้องไม่มี time_t แบบ 32 บิตที่ลงชื่อแล้ว
46490 Rob Kennedy

4
@ Rob, บา, ทิ้งไว้! เราจะเริ่มวิ่งไปรอบ ๆ เหมือนไก่หัวขาดในปี 2036 เช่นเดียวกับที่เราทำกับ Y2K พวกเราบางคนจะสร้างรายได้จำนวนมากจากการเป็นที่ปรึกษา Y2k38, Leonard Nimoy จะนำหนังสือเฮฮาอีกเล่มเกี่ยวกับวิธีที่เราทุกคนควรไปและซ่อนตัวอยู่ในป่า ...
paxdiablo

1
... และมันก็จะระเบิดออกไปผู้คนต่างก็สงสัยว่าเอะอะทั้งหมดเกี่ยวกับอะไร ฉันอาจออกจากการเกษียณอายุเพื่อหารายได้สำหรับมรดกของเด็ก ๆ :-)
paxdiablo

2
BTW เราพบข้อผิดพลาด Y2K เพียงข้อเดียวเท่านั้นและนั่นเป็นหน้าเว็บที่ระบุวันที่เป็น 1 มกราคม 19100 การออกกำลังกายสำหรับผู้อ่านถึงสาเหตุ ...
paxdiablo

9
หากเหตุการณ์ที่จะเกิดขึ้นใน 30 ปีคือ "หมดอายุการสำรองข้อมูลนี้" คุณอาจมีปัญหาในขณะนี้ไม่ใช่ในปี 2038 เพิ่ม 30 ปีในเวลา 32 บิตของวันนี้และคุณได้รับวันที่ผ่านมา โปรแกรมของคุณค้นหากิจกรรมที่ต้องดำเนินการค้นหารายการที่เกินกำหนด (100 ปี!) และดำเนินการ อุ๊ปส์ไม่มีการสำรองข้อมูลเพิ่มเติม
ร็อบเคนเนดี้

5

time_tเป็นประเภทlong intบนเครื่อง 64 บิตมิฉะนั้นจะเป็นlong long intเครื่องบิตอื่นมันคือ

คุณสามารถตรวจสอบได้ในไฟล์ส่วนหัวเหล่านี้:

time.h: /usr/include
types.hและtypesizes.h :/usr/include/x86_64-linux-gnu/bits

(คำสั่งด้านล่างไม่ใช่คำสั่งอื่น ๆ พบได้ในไฟล์ส่วนหัว resp. โดยใช้การค้นหา 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;

ไฟล์เหล่านี้จัดทำโดย glibc บน Ubuntu 15.10 BTW
Ciro Santilli 19 冠状病六四事件法轮功

3
มันไม่ใช่long intทุกที่ ดูstackoverflow.com/questions/384502/…
Zan Lynx

4

เป็นชนิดจำนวนเต็มแบบ 32 บิตที่ลงนามแล้วในแพลตฟอร์มแบบดั้งเดิมส่วนใหญ่ อย่างไรก็ตามนั่นเป็นสาเหตุให้รหัสของคุณต้องทนทุกข์ทรมานจากข้อผิดพลาดในปี 2038 ดังนั้นไลบรารี C ที่ทันสมัยควรกำหนดให้เป็น int 64- บิตที่มีการลงชื่อแทนซึ่งปลอดภัยสำหรับสองสามพันล้านปี


3

โดยทั่วไปแล้วคุณจะพบ typedef เฉพาะสำหรับการใช้งานพื้นฐานเหล่านี้สำหรับ gcc ใน bitsasmไดเรกทอรีหรือส่วนหัว สำหรับฉันมัน/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);

การส่งไปยังdoubleหรือlong doubleจะใช้งานได้ แต่ก็สามารถให้ผลลัพธ์ทศนิยมที่แน่นอนได้

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

ฉันต้องการทราบสถานการณ์เพราะฉันต้องการถ่ายโอนเวลาจากระบบ ARM ไปยังระบบ AMD64 time_t คือ 32 บิตที่แขนและ 64 บิตบนเซิร์ฟเวอร์ ถ้าฉันแปลเวลาเป็นรูปแบบและส่งสตริงมันไม่มีประสิทธิภาพและช้า ดังนั้นจึงเป็นการดีกว่าที่จะเพียงแค่ส่ง time_t ทั้งหมดและจัดเรียงในส่วนท้ายของเซิร์ฟเวอร์ อย่างไรก็ตามฉันจำเป็นต้องเข้าใจชนิดของมันให้มากขึ้นเพราะฉันไม่ต้องการให้ตัวเลขยุ่งเหยิงด้วยความแตกต่างของความแตกต่างระหว่างระบบดังนั้นฉันต้องใช้ htonl ... แต่ก่อนอื่นต้องรู้พื้นฐานฉันต้องการ เพื่อค้นหาประเภทพื้นฐาน;)
Owl

อีกกรณี "จำเป็นต้องรู้" อย่างน้อยสำหรับลงชื่อและไม่ได้ลงชื่อคือคุณต้องการความระมัดระวังเมื่อลบเวลา หากคุณเพียง "ลบและพิมพ์ผลลัพธ์" คุณอาจจะได้รับสิ่งที่คุณคาดหวังในระบบที่มี time_t ที่ลงนามแล้ว แต่ไม่ใช่กับ time_t ที่ไม่ได้ลงชื่อ
Michael Firth

@MichaelFirth คดีมีอยู่สำหรับทั้งจำนวนเต็มที่ลงนาม time_t และ time_t ไม่ได้ลงนามที่การลบดิบจะส่งผลให้ผลลัพธ์ที่ไม่คาดคิด C จัดให้double difftime(time_t time1, time_t time0)มีวิธีการลบอย่างสม่ำเสมอ
chux - Reinstate Monica

-3

time_tเป็นเพียงtypedef8 ไบต์ ( long long/__int64) ซึ่งคอมไพเลอร์และระบบปฏิบัติการทั้งหมดเข้าใจ ย้อนกลับไปในอดีตมันเคยเป็นเพียงlong int(4 ไบต์) แต่ไม่ใช่ตอนนี้ ถ้าคุณมองไปที่time_tในcrtdefs.hคุณจะพบทั้งการใช้งาน OS long longแต่จะใช้


5
คอมไพเลอร์และระบบปฏิบัติการทั้งหมดหรือไม่ ไม่บนระบบ linux ของฉันคอมไพเลอร์ใช้การลงนามขนาด 4 ไบต์
Vincent

บน Zynq 7010 ระบบ time_t คือ 4bytes
Owl

1
ในระบบฝังตัวฉันทำงานกับ time_t เกือบทุก 32- บิตหรือ 4 ไบต์ มาตรฐานระบุว่าเป็นการดำเนินการเฉพาะซึ่งทำให้คำตอบนี้ผิด
Cobusve
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.