¿Para qué es time_t en última instancia un typedef?

Respuestas:

175

El artículo del artículo time_t Wikipedia arroja algo de luz sobre esto. La conclusión es que el tipo de time_tno está garantizado en la especificación C.

El time_ttipo de datos es un tipo de datos en la biblioteca ISO C definida para almacenar valores de tiempo del sistema. Dichos valores se devuelven de la time() función de biblioteca estándar . Este tipo es un typedef definido en el encabezado estándar. ISO C define time_t como un tipo aritmético, pero no especifica ningún tipo , rango, resolución o codificación en particular. Tampoco se especifican los significados de las operaciones aritméticas aplicadas a los valores de tiempo.

Los sistemas compatibles con Unix y POSIX implementan el time_ttipo como signed integer(generalmente de 32 o 64 bits de ancho) que representa el número de segundos desde el inicio de la época de Unix : medianoche UTC del 1 de enero de 1970 (sin contar los segundos bisiestos). Algunos sistemas manejan correctamente los valores de tiempo negativos, mientras que otros no. Los sistemas que utilizan un tipo de 32 bits time_tson susceptibles al problema del año 2038 .

William Brendel
fuente
66
Sin embargo, tenga en cuenta que los valores de time_t generalmente solo se almacenan en la memoria, no en el disco. En cambio, time_t se convierte en texto u otro formato portátil para el almacenamiento persistente. Eso hace que el problema Y2038 no sea realmente un problema.
11
@Heath: en un sistema específico, donde las mismas personas crean el sistema operativo y la biblioteca C, time_tpuede suceder el uso de la estructura de datos en el disco. Sin embargo, dado que los sistemas de archivos a menudo son leídos por otros sistemas operativos, sería una tontería definir el sistema de archivos basado en tales tipos dependientes de la implementación. Por ejemplo, el mismo sistema de archivos podría usarse tanto en sistemas de 32 bits como en sistemas de 64 bits, y time_tpodría cambiar el tamaño. Por lo tanto, los sistemas de archivos deben definirse de manera más exacta ("entero de 32 bits con signo que proporcione el número de segundos desde el comienzo de 1970, en UTC") de la misma manera time_t.
1
Como nota: el artículo vinculado de Wikipedia se ha eliminado y ahora se redirige a la lista de time.hcontenidos. Ese artículo enlaza con cppreference.com pero el contenido citado no se encuentra en ninguna parte ...
Michał Górny
3
@ MichałGórny: Corregido, siempre y cuando no se eliminen los artículos, siempre puedes echar un vistazo al historial para encontrar la versión correcta.
Zeta
44
-1; El reclamo citado de Wikipedia que POSIX garantiza que time_testá firmado es incorrecto. pubs.opengroup.org/onlinepubs/9699919799/basedefs/… dicta que varias cosas deben ser un "tipo entero con signo" o "tipo entero sin signo", pero de time_teso dice simplemente que "será un tipo entero" . Una implementación puede hacer que time_tno esté firmada y seguir siendo compatible con POSIX.
Mark Amery el
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;

Se define a $INCDIR/bits/types.htravés de:

# 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
Quassnoi
fuente
1
Veo ambos typedef __int32_t __time_t;y typedef __time_t time_t;en a 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. Sus resultados se establecen explícitamente así en Linux (al menos en 2.6.32-5-xen-amd64 de Debian).
ssice
1
@Viet también es posible con una línea sin crear un archivo: stackoverflow.com/a/36096104/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
¿Por qué buscar __time_ty no time_tencontrar el tipo subyacente de time_t? ¿Omitiendo un paso?
chux - Restablece a Mónica el
@ chux-ReinstateMonica - El OP dijo que había encontrado el typedef de time_t a __time_t. Esta respuesta solo aborda la pregunta de qué __time_t se define. Pero estoy de acuerdo en que para un caso genérico (donde time_t puede no estar definido como __time_t), primero necesitaría grep por time_t, y luego posiblemente grep nuevamente por lo que eso devuelve
Michael Firth
@MichaelFirth Bastante justo. Recuerdo mi preocupación porque, aunque OP encontró typedef __time_t time_t;, el examen del código circundante también es necesario para asegurar que typedef se haya utilizado de hecho y no solo una parte de una compilación condicional. typedef long time_t;Puede haber sido encontrado también.
chux - Restablece a Mónica
30

Normas

William Brendel citó Wikipedia, pero prefiero que salga de la boca del caballo.

El borrador estándar C99 N1256 7.23.1 / 3 "Componentes del tiempo" dice:

Los tipos declarados son size_t (descrito en 7.17) clock_t y time_t que son tipos aritméticos capaces de representar tiempos

y 6.2.5 / 18 "Tipos" dice:

Los tipos enteros y flotantes se denominan colectivamente tipos aritméticos.

POSIX 7 sys_types.h dice:

[CX] time_t será un tipo entero.

donde [CX]se define como :

[CX] Extensión al estándar ISO C.

Es una extensión porque ofrece una garantía más sólida: los puntos flotantes están fuera.

gcc one-liner

No es necesario crear un archivo como lo menciona Quassnoi :

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

En Ubuntu 15.10 GCC 5.2, las dos líneas principales son:

typedef long int __time_t;
typedef __time_t time_t;

Desglose de comandos con algunas citas de man gcc:

  • -E: "Deténgase después de la etapa de preprocesamiento; no ejecute el compilador correctamente".
  • -xc: Especifique el lenguaje C, ya que la entrada proviene de stdin que no tiene extensión de archivo.
  • -include file: "Procesar archivo como si" #include "archivo" "apareciera como la primera línea del archivo fuente primario".
  • -: entrada de stdin
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
1
No hay necesidad de que la tubería se gcc -E -xc -include time.h /dev/null | grep time_t
repita
12

La respuesta es definitivamente específica de la implementación. Para conocer definitivamente su plataforma / compilador, simplemente agregue esta salida en algún lugar de su código:

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

Si la respuesta es 4 (32 bits) y sus datos deben ir más allá de 2038 , entonces tiene 25 años para migrar su código.

Sus datos estarán bien si almacena sus datos como una cadena, incluso si es algo simple como:

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

Luego, léalo de la misma manera (fread, fscanf, etc. en un int), y tendrá su tiempo de compensación de época. Una solución similar existe en .Net. Paso sin problemas números de época de 64 bits entre sistemas Win y Linux (a través de un canal de comunicaciones). Eso trae problemas de orden de bytes, pero ese es otro tema.

Para responder a la consulta de paxdiablo, diría que imprimió "19100" porque el programa se escribió de esta manera (y admito que lo hice yo mismo en los años 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);

La printfdeclaración imprime la cadena fija "Año es: 19" seguida de una cadena con relleno de cero con los "años desde 1900" (definición detm->tm_year ). En 2000, ese valor es 100, obviamente. "%02d"rellena con dos ceros pero no se trunca si tiene más de dos dígitos.

La forma correcta es (cambiar a la última línea solamente):

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

Nueva pregunta: ¿Cuál es la razón de ese pensamiento?

pwrgreg007
fuente
2
Probablemente debería usar el %zuespecificador de formato para formatear size_tvalores (tal como se obtiene sizeof), ya que no están firmados ( u) y tienen una longitud size_t ( z) ·
Adrian Günter
... o use printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));y evite el zproblema.
chux - Restablece a Mónica el
6

En Visual Studio 2008, el valor predeterminado es a __int64menos que usted defina _USE_32BIT_TIME_T. Es mejor fingir que no sabe cómo se define, ya que puede (y cambiará) de plataforma en plataforma.

Eclipse
fuente
2
Eso generalmente funciona, pero si su programa está destinado a realizar un seguimiento de las cosas que sucederán dentro de 30 años, es bastante importante que no tenga un time_t de 32 bits firmado.
Rob Kennedy el
44
@ Rob, bah, ¡déjalo! Comenzaremos a correr como pollos sin cabeza en 2036, lo mismo que hicimos para Y2K. Algunos de nosotros va a hacer una bucketload de dinero de ser consultores Y2k38, Leonard Nimoy sacará otro libro hilarante sobre la forma en que todos deberíamos ir y esconderse en el bosque ...
paxdiablo
1
... y todo se desvanecerá, el público se preguntará por qué tanto alboroto. Incluso puedo salir de la jubilación para ganar dinero para la herencia de los niños :-).
paxdiablo
2
Por cierto, sólo encontramos una bichos Y2K y que era una página web que aparece la fecha del 1 de enero de 19100. El ejercicio para el lector por qué ...
paxdiablo
9
Si el evento que sucederá en 30 años es "caducar esta copia de seguridad", entonces podría estar en problemas AHORA, no en 2038. Agregue 30 años al actual time_t de 32 bits y obtendrá una fecha en el pasado. Su programa busca eventos para procesar, encuentra uno que está atrasado (¡en 100 años!) Y lo ejecuta. Vaya, no más copias de seguridad.
Rob Kennedy el
5

time_tes de tipo long inten máquinas de 64 bits, de lo contrario eslong long int .

Puede verificar esto en estos archivos de encabezado:

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

(Las siguientes declaraciones no son una tras otra. Se pueden encontrar en el archivo de encabezado de respuesta usando Ctrl + f search).

1 en time.h

typedef __time_t time_t;

2) en types.h

# define __STD_TYPE     typedef  
__STD_TYPE __TIME_T_TYPE __time_t;  

3 en 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) De nuevo en 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;
abcoep
fuente
Esos archivos son proporcionados por glibc en Ubuntu 15.10 BTW.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
3
No está en long inttodas partes. Ver stackoverflow.com/questions/384502/…
Zan Lynx
4

Es un tipo entero con signo de 32 bits en la mayoría de las plataformas heredadas. Sin embargo, eso hace que su código sufra el error del año 2038 . Por lo tanto, las bibliotecas C modernas deberían definirlo como un int de 64 bits con signo, lo que es seguro durante unos miles de millones de años.


fuente
3

Por lo general, encontrará estos typedefs subyacentes específicos de implementación para gcc en el directorio de encabezado bitso asm. Para mí lo es /usr/include/x86_64-linux-gnu/bits/types.h.

Puede simplemente grep, o utilizar una invocación de preprocesador como la sugerida por Quassnoi para ver qué encabezado específico.

billar
fuente
1

¿En qué consiste finalmente un time_t typedef?

El código robusto no le importa cuál es el tipo.

C especies time_tpara ser un tipo real como double, long long, int64_t, int, etc.

Incluso podría ser unsignedcomo los valores de retorno de muchas funciones de tiempo que indican que el error no lo es -1, pero(time_t)(-1) - Esta opción de implementación es poco común.

El punto es que la "necesidad de saber" el tipo es rara. El código debe estar escrito para evitar la necesidad.


Sin embargo, se produce una "necesidad de saber" común cuando el código quiere imprimir la materia prima time_t. La conversión al tipo entero más amplio acomodará los casos más modernos.

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

Transmitir a doubleo long doublefuncionará también, pero podría proporcionar una salida decimal inexacta

printf("%.16e", (double) now);
chux - Restablece a Monica
fuente
Necesito saber la situación porque necesito transferir tiempo de un sistema ARM a un sistema AMD64. time_t tiene 32 bits en el brazo y 64 bits en el servidor. si traduzco el tiempo a un formato y envío una cadena, es ineficiente y lento. Por lo tanto, es mucho mejor enviar todo el time_t y ordenarlo en el servidor. Sin embargo, necesito entender el tipo un poco más porque no quiero que el número se altere por la diferencia de endianness entre los sistemas, así que necesito usar htonl ... pero primero, sobre la base de una necesidad de saber, quiero para descubrir el tipo subyacente;)
Owl
Otro caso de "necesidad de saber", al menos para firmado frente a no firmado, es si debe tener cuidado al restar tiempos. Si simplemente "resta e imprime el resultado", posiblemente obtendrá lo que espera en un sistema con un time_t con signo, pero no con un time_t sin signo.
Michael Firth
@MichaelFirth Existen casos para time_t con signo entero y time_t sin signo donde la resta sin procesar dará como resultado resultados inesperados. C proporciona double difftime(time_t time1, time_t time0)un enfoque de resta uniforme.
chux - Restablece a Mónica
-3

time_tes solo typedefpara 8 bytes ( long long/__int64) que todos los compiladores y sistemas operativos entienden. En el long intpasado , solía ser solo para (4 bytes) pero no ahora. Si nos fijamos en la time_ten la crtdefs.hque se encuentra tanto en implementaciones pero el sistema operativo va a utilizar long long.

bvrwoo_3376
fuente
55
todos los compiladores y sistemas operativos? No. En mi sistema Linux, el compilador toma la implementación firmada de 4 bytes.
Vincent
En los sistemas Zynq 7010, time_t es 4bytes.
Búho
1
En los sistemas integrados, trabajo en time_t casi siempre es de 32 bits o 4 bytes. El estándar establece específicamente que es específico de la implementación lo que hace que esta respuesta sea incorrecta.
Cobusve