Що таке 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: у певній системі, де ті самі люди створюють операційну систему та бібліотеку С, використання time_tв структурі даних на диску може статися. Однак, оскільки файлові системи часто читаються іншими операційними системами, було б нерозумно визначити файлову систему на основі таких типів, що залежать від реалізації. Наприклад, одна і та ж файлова система може бути використана як для 32-бітної, так і для 64-бітової систем і time_tможе змінювати розмір. Таким чином, файлові системи потрібно визначити точніше ("32-бітове підписане ціле число, даючи кількість секунд з початку 1970 року в UTC"), а не як time_t.

1
Як зауваження: пов’язана стаття у Вікіпедії була видалена, і тепер вона переспрямовується до списку 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.
Марк Амері

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
Чіро Сантіллі郝海东冠状病六四事件法轮功

1
Чому гребіть за, __time_tа не time_tзнаходити базовий тип time_t? Пропустіть крок?
chux

@ chux-ReinstateMonica - ОП заявив, що знайшов typedef від time_t до __time_t. Ця відповідь якраз стосується запитання про те, що __time_t визначено як. Але я погоджуюсь, що для загального випадку (де time_t не може бути набрано значення __time_t), вам потрібно спочатку простукати за time_t, а потім, можливо, ще раз простукувати за те, що повертається
Майкл Фірт

@MichaelFirth Ярмарок досить. Я пам'ятаю свою стурбованість тим, що навіть незважаючи на те, що ОП виявив typedef __time_t time_t;, необхідне також вивчення навколишнього коду, щоб гарантувати, що typedef насправді використовувався, а не є лише частиною умовної компіляції. typedef long time_t;можливо, їх теж знайшли.
chux

31

Стандарти

Вільям Брендль цитував Вікіпедію, але я віддаю перевагу це з вуст коня.

Стандартний проект 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 "файл" "з'явився як перший рядок файлу первинного джерела."
  • -: вхід від 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. Я без проблем передаю 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) ·
Адріан Гюнтер

... або використовувати printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));та уникати zпроблеми.
chux

6

У візуальній студії 2008 вона встановлюється за замовчуванням, __int64якщо ви не визначите _USE_32BIT_TIME_T. Вам краще просто зробити вигляд, що ви не знаєте, як це визначено, оскільки він може (і буде) змінюватися від платформи до платформи.


2
Зазвичай це працює, але якщо ваша програма призначена для відстеження того, що відбуватиметься через 30 років, досить важливо, щоб у вас не було 32-бітового time_t підписаного підпису.
Роб Кеннеді

4
@Rob, ба, залиш! Ми просто почнемо бігати навколо, як безголові кури в 2036 році, те саме, що ми робили для Y2K. Деякі з нас зароблять купу грошей від того, щоб бути консультантами Y2k38, Леонард Німой виведе ще одну веселу книгу про те, як нам усім їхати та сховатися в лісі ...
paxdiablo

1
... і це все подує, громадськість цікавиться, про що це суєта. Я навіть можу вийти на пенсію, щоб заробити трохи грошей на спадщину дітей :-).
paxdiablo

2
До речі, ми знайшли лише одну помилку Y2K, і це була веб-сторінка, яка вказала дату як 1 січня 19100 року. Вправа для читача, чому ...
paxdiablo

9
Якщо подія, яка відбудеться через 30 років, "закінчується ця резервна копія", то, можливо, у вас виникнуть проблеми ЗАРАЗ, а не в 2038 році. Додайте 30 років до сьогоднішнього 32-бітного time_t, і ви отримаєте дату в минулому. Ваша програма шукає події для обробки, знаходить ту, яка прострочена (на 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

(Викладені нижче твердження не є одна за одною. Їх можна знайти у відповідному файлі заголовка за допомогою Ctrl + f search.)

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 郝海东 冠状 病 六四 事件 法轮功

3
Це не long intскрізь. Дивіться stackoverflow.com/questions/384502/…
Zan Lynx

4

Це 32-бітний цілочисельний підписаний тип на більшості застарілих платформ. Однак це призводить до того, що ваш код страждає від 2038 року помилки . Тож сучасні бібліотеки С повинні визначати, що це замість цього 64-бітний підписаний int, що є безпечним протягом декількох мільярдів років.


3

Як правило, ви знайдете ці основні типи для реалізації, що стосуються gcc, у каталозі bitsабо asmзаголовку. Для мене це /usr/include/x86_64-linux-gnu/bits/types.h.

Ви можете просто зіткнути або використати виклик препроцесора, як запропоновано Quassnoi щоб побачити, який конкретний заголовок.


1

Що в кінцевому підсумку є type_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 ... але по-перше, мені потрібно знати, я хочу щоб з'ясувати основний тип;)
Сова

Ще один випадок "потрібно знати", принаймні для підписаних проти неподписаних - це чи потрібно дбати під час віднімання разів. Якщо ви просто "віднімаєте та друкуєте результат", то, можливо, ви отримаєте те, що очікуєте в системі з підписаним time_t, але не з неподписаним time_t.
Майкл Фірт

@MichaelFirth Випадки існують як для цілого підписаного time_t, так і для непідписаного time_t, коли необмежене віднімання призведе до несподіваних результатів. C передбачає double difftime(time_t time1, time_t time0)рівномірний підхід віднімання.
chux

-3

time_t- це лише typedef8 байт ( long long/__int64), які розуміють усі компілятори та ОС. Ще в ті часи це було лише для long int(4 байти), але не зараз. Якщо ви подивитесь на time_tв, crtdefs.hви знайдете обидві реалізації, але ОС використовуватиме long long.


5
всі компілятори та ОС? Ні. У моїй системі Linux компілятор приймає 4 байти підписаної реалізації.
Вінсент

У системах Zynq 7010 time_t становить 4 байти.
Сова

1
У вбудованих системах я працюю над time_t майже завжди 32-бітним або 4 байтовим. У стандарті конкретно зазначено, що саме ця реалізація робить цю відповідь просто неправильною.
Cobusve
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.