Artykuł w Wikipedii time_t rzuca na to trochę światła. Najważniejsze jest to, że typ time_tnie jest gwarantowany w specyfikacji C.
Typ time_tdanych to typ danych w bibliotece ISO C zdefiniowany do przechowywania wartości czasu systemowego. Takie wartości są zwracane ze standardowej time()
funkcji biblioteki. Ten typ jest typedef zdefiniowanym w standardowym nagłówku. ISO C definiuje time_t jako typ arytmetyczny, ale nie określa żadnego konkretnego typu , zakresu, rozdzielczości ani kodowania dla niego. Nieokreślone są również znaczenia operacji arytmetycznych zastosowanych do wartości czasu.
Systemy zgodne z Unix i POSIX implementują ten time_ttyp jako signed
integer(zwykle o szerokości 32 lub 64 bitów), który reprezentuje liczbę sekund od początku epoki Uniksa : północ UTC 1 stycznia 1970 r. (Nie licząc sekund przestępnych). Niektóre systemy poprawnie obsługują ujemne wartości czasu, podczas gdy inne nie. Systemy wykorzystujące time_ttyp 32-bitowy są podatne na problem z rokiem 2038 .
Należy jednak pamiętać, że wartości time_t są zwykle przechowywane tylko w pamięci, a nie na dysku. Zamiast tego time_t jest konwertowany na tekst lub inny przenośny format dla trwałego przechowywania. To sprawia, że problem Y2038 nie jest tak naprawdę problemem.
11
@Heath: w określonym systemie, w którym te same osoby tworzą system operacyjny i bibliotekę C, time_tmoże się zdarzyć przy użyciu struktury danych na dysku. Ponieważ jednak systemy plików są często odczytywane przez inne systemy operacyjne, głupio byłoby zdefiniować system plików na podstawie typów zależnych od implementacji. Na przykład ten sam system plików może być używany zarówno w systemach 32-bitowych, jak i 64-bitowych i time_tmoże zmieniać rozmiar. Dlatego systemy plików muszą być zdefiniowane bardziej precyzyjnie („32-bitowa liczba całkowita ze znakiem, dająca liczbę sekund od początku 1970 r. W UTC”), niż tylko tak time_t.
1
Uwaga: połączony artykuł z Wikipedii został usunięty, a teraz przekierowuje do listy time.htreści. Ten artykuł prowadzi do strony cppreference.com, ale nigdzie nie można znaleźć cytowanej treści…
Michał Górny,
3
@ MichałGórny: Naprawiono, dopóki artykuły nie są usuwane, zawsze możesz zajrzeć do historii, aby znaleźć właściwą wersję.
Zeta,
4
-1; cytowane z Wikipedii twierdzenie, że POSIX gwarantuje, że time_tjest podpisane, jest nieprawidłowe. pubs.opengroup.org/onlinepubs/9699919799/basedefs/… dyktuje, że różne rzeczy muszą być „typem całkowitym ze znakiem” lub „typem całkowitym bez znaku”, ale z time_ttego wynika jedynie, że „powinien być typem całkowitym” . Implementacja może sprawić, że będzie time_tbez znaku i nadal będzie zgodna z POSIX.
Widzę jak typedef __int32_t __time_t;i typedef __time_t time_t;w sposób 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. Twoje wyniki są jawnie ustawione w ten sposób w Linuksie (przynajmniej na 2.6.32-5-xen-amd64 z Debiana).
Po co grepować __time_ti nie time_tznajdować podstawowego typu time_t? Pomijasz krok?
chux - Przywróć Monikę
@ chux-ReinstateMonica - OP powiedział, że znalazł typedef od time_t do __time_t. Ta odpowiedź dotyczy tylko zadanego pytania o to, jak zdefiniowano __time_t. Ale zgadzam się, że w ogólnym przypadku (gdzie time_t nie może być wpisane jako __time_t), najpierw musisz grep dla time_t, a następnie prawdopodobnie grep dla tego, co zwraca
Michael Firth
@MichaelFirth Fair wystarczy. Przypominam sobie moją troskę, ponieważ chociaż OP znalazł typedef __time_t time_t;, badanie otaczającego kodu jest również potrzebne, aby upewnić się, że typedef faktycznie został użyty, a nie tylko część kompilacji warunkowej. typedef long time_t;też mógł zostać znaleziony.
chux - Przywróć Monikę
30
Standardy
William Brendel zacytował Wikipedię, ale wolę ją z pyska konia.
Nie ma potrzeby używania rury z echa:gcc -E -xc -include time.h /dev/null | grep time_t
rvighne
12
Odpowiedź jest zdecydowanie specyficzna dla implementacji. Aby ostatecznie dowiedzieć się o swojej platformie / kompilatorze, po prostu dodaj to wyjście gdzieś w kodzie:
printf ("sizeof time_t is: %d\n",sizeof(time_t));
Jeśli odpowiedź to 4 (32 bity), a dane mają wykraczać poza 2038 r. , Masz 25 lat na migrację kodu.
Twoje dane będą w porządku, jeśli przechowujesz je jako ciąg, nawet jeśli jest to coś takiego jak:
FILE*stream =[stream file pointer that you've opened correctly];
fprintf (stream,"%d\n",(int)time_t);
Następnie po prostu przeczytaj go w ten sam sposób (fread, fscanf itp.) Do int), a uzyskasz czas przesunięcia epoki. Podobne obejście istnieje w .Net. Bez problemu przekazuję 64-bitowe numery epoki między systemami Win i Linux (kanałem komunikacji). To powoduje problemy z porządkowaniem bajtów, ale to już inny temat.
Aby odpowiedzieć na zapytanie paxdiablo, powiedziałbym, że wypisał „19100”, ponieważ program został napisany w ten sposób (i przyznaję, że zrobiłem to sam w latach 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);
printfOświadczenie drukuje stały ciąg „rok wynosi: 19”, po której następuje zero-wyściełane ciąg z „lat od 1900 roku” (definicja tm->tm_year). Oczywiście w 2000 r. Ta wartość wynosi 100. "%02d"pad z dwoma zerami, ale nie obcina, jeśli jest dłuższy niż dwie cyfry.
Prawidłowy sposób to (zmień tylko na ostatni wiersz):
printf ("Year is: %d\n", local_date_time.tm_year +1900);
Nowe pytanie: Jakie jest uzasadnienie tego myślenia?
Prawdopodobnie powinieneś użyć specyfikatora %zuformatu do sformatowania size_twartości ( podanych przez sizeof), ponieważ są one bez znaku ( u) i długości size_t ( z) ·
Adrian Günter
... lub użyj printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));i uniknij zproblemu.
chux - Przywróć Monikę
6
W Visual Studio 2008 domyślnie jest to __int64chyba, że zdefiniujesz _USE_32BIT_TIME_T. Lepiej udawaj, że nie wiesz, jak to jest zdefiniowane, ponieważ może (i będzie) zmieniać się z platformy na platformę.
Zwykle to działa, ale jeśli twój program ma śledzić wydarzenia, które będą miały miejsce za 30 lat, bardzo ważne jest, aby nie mieć podpisanego 32-bitowego time_t.
Rob Kennedy
4
@ Rob, bah, zostaw to! Zaczniemy biegać jak bezgłowe kurczaki w 2036 roku, tak samo jak w przypadku Y2K. Niektórzy z nas zarobią mnóstwo pieniędzy, będąc konsultantami Y2k38, Leonard Nimoy wyda kolejną zabawną książkę o tym, jak wszyscy powinniśmy iść i ukryć się w lesie ...
paxdiablo
1
... i wszystko się rozwali, publiczność zastanawia się, o co tyle zamieszania. Mogę nawet wyjść z emerytury, aby zarobić trochę pieniędzy na dziedzictwo dzieci :-).
paxdiablo
2
BTW, znaleźliśmy tylko jeden błąd Y2K i była to strona internetowa z datą 1 stycznia 19100. Ćwicz dla czytelnika, dlaczego ...
paxdiablo
9
Jeśli zdarzeniem, które nastąpi za 30 lat, jest „wygasanie tej kopii zapasowej”, możesz mieć kłopoty TERAZ, a nie w 2038 r. Dodaj 30 lat do dzisiejszego 32-bitowego time_t, a otrzymasz datę w przeszłości. Twój program szuka zdarzeń do przetworzenia, znajduje takie, które są opóźnione (o 100 lat!), I wykonuje je. Ups, nie ma już kopii zapasowej.
Rob Kennedy
5
time_tjest typu long intna maszynach 64-bitowych, w przeciwnym razie jest long long int.
Jest to 32-bitowa liczba całkowita ze znakiem na większości starszych platform. Jednak powoduje to, że Twój kod cierpi na błąd z roku 2038 . Dlatego współczesne biblioteki C powinny definiować ją jako 64-bitową int, która jest bezpieczna przez kilka miliardów lat.
Zazwyczaj te leżące u podstaw implementacji typedefs dla gcc znajdują się w katalogu bitslub asmnagłówku. Dla mnie to jest /usr/include/x86_64-linux-gnu/bits/types.h.
Gatunek C time_tsię typ rzeczywisty jak double, long long, int64_t, intitp
Może nawet być unsignedtak, ponieważ zwracane wartości z wielu funkcji czasu wskazujące błąd nie są -1, ale (time_t)(-1)- Ten wybór implementacji jest rzadki.
Chodzi o to, że ten „typ wiedzy” jest rzadki. Kod powinien być napisany, aby uniknąć potrzeby.
Jednak często pojawia się konieczność „wiedzieć”, gdy kod chce wydrukować plik raw time_t. Rzutowanie na najszerszą liczbę całkowitą pomieści większość współczesnych przypadków.
time_t now =0;
time(&now);
printf("%jd",(intmax_t) now);// or
printf("%lld",(longlong) now);
Casting Do doubleczy long doublezadziała też jeszcze może zapewnić niedokładnymi wyjście dziesiętny
Potrzebuję poznać sytuację, ponieważ muszę przenieść czas z systemu ARM do systemu AMD64. time_t to 32 bity na ramieniu i 64 bity na serwerze. jeśli przetłumaczę czas na format i wyślę ciąg, będzie on nieefektywny i powolny. Dlatego o wiele lepiej jest po prostu wysłać cały time_t i uporządkować go na serwerze. Muszę jednak trochę lepiej zrozumieć ten typ, ponieważ nie chcę, aby liczba była zniekształcona przez różnicowanie endianizmu między systemami, więc muszę użyć htonl ... ale najpierw potrzebuję znać, chcę aby dowiedzieć się, jaki jest typ podstawowy;)
Owl
Innym przypadkiem „musisz wiedzieć”, przynajmniej w przypadku podpisania vs niepodpisania, jest to, czy musisz zachować ostrożność, odejmując czasy. Jeśli po prostu „odejmiesz i wydrukujesz wynik”, prawdopodobnie uzyskasz to, czego oczekujesz w systemie ze znakiem time_t ze znakiem, ale bez podpisu time_t.
Michael Firth
@MichaelFirth Przypadki istnieją zarówno dla liczb całkowitych ze znakiem time_t, jak i dla znaku bez znaku time_t, gdzie odjęcie surowe spowoduje nieoczekiwane wyniki. C zapewnia double difftime(time_t time1, time_t time0)jednolite podejście odejmowania.
chux - Przywróć Monikę
-3
time_tjest tylko typedefdla 8 bajtów ( long long/__int64), które rozumieją wszystkie kompilatory i system operacyjny. Kiedyś to było tylko long int(4 bajty), ale nie teraz. Jeśli spojrzeć na time_tw crtdefs.hWas znajdzie obie implementacje ale OS użyje long long.
wszystkie kompilatory i systemy operacyjne? Nie. W moim systemie Linux kompilator przyjmuje 4-bajtową implementację ze znakiem.
Vincent
W systemach Zynq 7010 czas_t wynosi 4 bajty.
Sowa
1
W systemach wbudowanych pracuję na time_t prawie zawsze ma 32 bity lub 4 bajty. Norma wyraźnie stwierdza, że to konkretna implementacja sprawia, że ta odpowiedź jest po prostu błędna.
long int
.Odpowiedzi:
Artykuł w Wikipedii time_t rzuca na to trochę światła. Najważniejsze jest to, że typ
time_t
nie jest gwarantowany w specyfikacji C.źródło
time_t
może się zdarzyć przy użyciu struktury danych na dysku. Ponieważ jednak systemy plików są często odczytywane przez inne systemy operacyjne, głupio byłoby zdefiniować system plików na podstawie typów zależnych od implementacji. Na przykład ten sam system plików może być używany zarówno w systemach 32-bitowych, jak i 64-bitowych itime_t
może zmieniać rozmiar. Dlatego systemy plików muszą być zdefiniowane bardziej precyzyjnie („32-bitowa liczba całkowita ze znakiem, dająca liczbę sekund od początku 1970 r. W UTC”), niż tylko taktime_t
.time.h
treści. Ten artykuł prowadzi do strony cppreference.com, ale nigdzie nie można znaleźć cytowanej treści…time_t
jest podpisane, jest nieprawidłowe. pubs.opengroup.org/onlinepubs/9699919799/basedefs/… dyktuje, że różne rzeczy muszą być „typem całkowitym ze znakiem” lub „typem całkowitym bez znaku”, ale ztime_t
tego wynika jedynie, że „powinien być typem całkowitym” . Implementacja może sprawić, że będzietime_t
bez znaku i nadal będzie zgodna z POSIX.[root]# cat time.c
[root]# gcc -E time.c | grep __time_t
typedef long int __time_t;
Jest to zdefiniowane
$INCDIR/bits/types.h
poprzez:źródło
typedef __int32_t __time_t;
itypedef __time_t time_t;
w sposóbFreeBSD 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
. Twoje wyniki są jawnie ustawione w ten sposób w Linuksie (przynajmniej na 2.6.32-5-xen-amd64 z Debiana).__time_t
i nietime_t
znajdować podstawowego typutime_t
? Pomijasz krok?typedef __time_t time_t;
, badanie otaczającego kodu jest również potrzebne, aby upewnić się, że typedef faktycznie został użyty, a nie tylko część kompilacji warunkowej.typedef long time_t;
też mógł zostać znaleziony.Standardy
William Brendel zacytował Wikipedię, ale wolę ją z pyska konia.
Standardowy projekt C99 N1256 7.23.1 / 3 „Składniki czasu” mówi:
a 6.2.5 / 18 „Typy” mówi:
POSIX 7 sys_types.h mówi:
gdzie
[CX]
jest zdefiniowany jako :Jest to rozszerzenie, ponieważ daje silniejszą gwarancję: zmiennoprzecinkowe są wyłączone.
gcc one-liner
Nie ma potrzeby tworzenia pliku, jak wspomniano w Quassnoi :
W Ubuntu 15.10 GCC 5.2 dwie górne linie to:
Podział poleceń z kilkoma cytatami z
man gcc
:-E
: „Zatrzymaj się po etapie wstępnego przetwarzania; nie uruchamiaj kompilatora poprawnie.”-xc
: Określ język C, ponieważ dane wejściowe pochodzą ze standardowego wejścia, które nie ma rozszerzenia pliku.-include file
: „Przetwarzaj plik tak, jakby„ #include ”file” pojawił się jako pierwszy wiersz podstawowego pliku źródłowego. ”-
: wejście ze standardowego wejściaźródło
gcc -E -xc -include time.h /dev/null | grep time_t
Odpowiedź jest zdecydowanie specyficzna dla implementacji. Aby ostatecznie dowiedzieć się o swojej platformie / kompilatorze, po prostu dodaj to wyjście gdzieś w kodzie:
Jeśli odpowiedź to 4 (32 bity), a dane mają wykraczać poza 2038 r. , Masz 25 lat na migrację kodu.
Twoje dane będą w porządku, jeśli przechowujesz je jako ciąg, nawet jeśli jest to coś takiego jak:
Następnie po prostu przeczytaj go w ten sam sposób (fread, fscanf itp.) Do int), a uzyskasz czas przesunięcia epoki. Podobne obejście istnieje w .Net. Bez problemu przekazuję 64-bitowe numery epoki między systemami Win i Linux (kanałem komunikacji). To powoduje problemy z porządkowaniem bajtów, ale to już inny temat.
Aby odpowiedzieć na zapytanie paxdiablo, powiedziałbym, że wypisał „19100”, ponieważ program został napisany w ten sposób (i przyznaję, że zrobiłem to sam w latach 80.):
printf
Oświadczenie drukuje stały ciąg „rok wynosi: 19”, po której następuje zero-wyściełane ciąg z „lat od 1900 roku” (definicjatm->tm_year
). Oczywiście w 2000 r. Ta wartość wynosi 100."%02d"
pad z dwoma zerami, ale nie obcina, jeśli jest dłuższy niż dwie cyfry.Prawidłowy sposób to (zmień tylko na ostatni wiersz):
Nowe pytanie: Jakie jest uzasadnienie tego myślenia?
źródło
%zu
formatu do sformatowaniasize_t
wartości ( podanych przezsizeof
), ponieważ są one bez znaku (u
) i długości size_t (z
) ·printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));
i uniknijz
problemu.W Visual Studio 2008 domyślnie jest to
__int64
chyba, że zdefiniujesz_USE_32BIT_TIME_T
. Lepiej udawaj, że nie wiesz, jak to jest zdefiniowane, ponieważ może (i będzie) zmieniać się z platformy na platformę.źródło
time_t
jest typulong int
na maszynach 64-bitowych, w przeciwnym razie jestlong long int
.Możesz to sprawdzić w tych plikach nagłówka:
time.h
:/usr/include
types.h
itypesizes.h
:/usr/include/x86_64-linux-gnu/bits
(Poniższe instrukcje nie są jeden po drugim. Można je znaleźć w pliku nagłówkowym lub przy użyciu wyszukiwania Ctrl + f.)
1 w
time.h
2) W
types.h
3) W
typesizes.h
4) Ponownie w
types.h
źródło
long int
wszędzie jest. Zobacz stackoverflow.com/questions/384502/…Jest to 32-bitowa liczba całkowita ze znakiem na większości starszych platform. Jednak powoduje to, że Twój kod cierpi na błąd z roku 2038 . Dlatego współczesne biblioteki C powinny definiować ją jako 64-bitową int, która jest bezpieczna przez kilka miliardów lat.
źródło
Zazwyczaj te leżące u podstaw implementacji typedefs dla gcc znajdują się w katalogu
bits
lubasm
nagłówku. Dla mnie to jest/usr/include/x86_64-linux-gnu/bits/types.h
.Możesz po prostu grep lub użyć wywołania preprocesora, takiego jak to sugerowane przez Quassnoi, aby zobaczyć, który konkretny nagłówek.
źródło
Solidny kod nie dba o typ.
Gatunek C
time_t
się typ rzeczywisty jakdouble, long long, int64_t, int
itpMoże nawet być
unsigned
tak, ponieważ zwracane wartości z wielu funkcji czasu wskazujące błąd nie są-1
, ale(time_t)(-1)
- Ten wybór implementacji jest rzadki.Chodzi o to, że ten „typ wiedzy” jest rzadki. Kod powinien być napisany, aby uniknąć potrzeby.
Jednak często pojawia się konieczność „wiedzieć”, gdy kod chce wydrukować plik raw
time_t
. Rzutowanie na najszerszą liczbę całkowitą pomieści większość współczesnych przypadków.Casting Do
double
czylong double
zadziała też jeszcze może zapewnić niedokładnymi wyjście dziesiętnyźródło
double difftime(time_t time1, time_t time0)
jednolite podejście odejmowania.time_t
jest tylkotypedef
dla 8 bajtów (long long/__int64
), które rozumieją wszystkie kompilatory i system operacyjny. Kiedyś to było tylkolong int
(4 bajty), ale nie teraz. Jeśli spojrzeć natime_t
wcrtdefs.h
Was znajdzie obie implementacje ale OS użyjelong long
.źródło