O que é time_t, em última análise, um typedef?

195

Eu procurei na minha caixa Linux e vi este typedef:

typedef __time_t time_t;

Mas não consegui encontrar a __time_t definição.

kal
fonte

Respostas:

175

O artigo do artigo time_t da Wikipedia lança alguma luz sobre isso. A linha inferior é que o tipo de time_tnão é garantido na especificação C.

O time_ttipo de dados é um tipo de dados na biblioteca ISO C definido para armazenar valores de hora do sistema. Esses valores são retornados da time() função de biblioteca padrão . Este tipo é um typedef definido no cabeçalho padrão. O ISO C define time_t como um tipo aritmético, mas não especifica nenhum tipo , intervalo, resolução ou codificação específica para ele. Também não são especificados os significados das operações aritméticas aplicadas aos valores temporais.

Os sistemas compatíveis com Unix e POSIX implementam o time_ttipo como um signed integer(geralmente com largura de 32 ou 64 bits) que representa o número de segundos desde o início da época do Unix : meia-noite UTC de 1 de janeiro de 1970 (sem contar os segundos bissextos). Alguns sistemas lidam corretamente com valores de tempo negativos, enquanto outros não. Os sistemas que usam um time_ttipo de 32 bits são suscetíveis ao problema do ano 2038 .

William Brendel
fonte
6
Observe, no entanto, que os valores de time_t geralmente são armazenados apenas na memória, não no disco. Em vez disso, time_t é convertido em texto ou em outro formato portátil para armazenamento persistente. Isso faz com que o problema Y2038 não seja realmente um problema.
11
@Heath: em um sistema específico, onde as mesmas pessoas criam o sistema operacional e a biblioteca C, usando time_ta estrutura de dados em disco. No entanto, como os sistemas de arquivos são frequentemente lidos por outros sistemas operacionais, seria tolo definir o sistema de arquivos com base nesses tipos dependentes da implementação. Por exemplo, o mesmo sistema de arquivos pode ser usado em sistemas de 32 e 64 bits e time_tpode mudar de tamanho. Portanto, os sistemas de arquivos precisam ser definidos com mais precisão ("número inteiro assinado de 32 bits que fornece o número de segundos desde o início de 1970, no UTC") do que apenas time_t.
1
Observação: o artigo vinculado da Wikipedia foi removido e agora é redirecionado para a lista de time.hconteúdos. Esse artigo tem um link para cppreference.com, mas o conteúdo citado não é encontrado em nenhum lugar…
Michał Górny
3
@ MichałGórny: Corrigido, desde que os artigos não sejam excluídos, você sempre pode dar uma olhada no histórico para encontrar a versão correta.
Zeta
4
-1; a reivindicação citada na Wikipedia de que o POSIX garante que time_testá assinado está incorreta. pubs.opengroup.org/onlinepubs/9699919799/basedefs/… determina que várias coisas devem ser um "tipo inteiro assinado" ou "tipo inteiro não assinado", mas time_tdiz apenas que "deve ser um tipo inteiro" . Uma implementação pode deixar de ser time_tassinada e ainda ser compatível com 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;

É definido $INCDIR/bits/types.hatravé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
fonte
1
Eu vejo os dois typedef __int32_t __time_t;e typedef __time_t time_t;em um 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. Seus resultados são explicitamente definidos como no Linux (pelo menos no 2.6.32-5-xen-amd64 do Debian).
ssice
1
O @Viet também é possível com uma linha, sem a criação de um arquivo: stackoverflow.com/a/36096104/895245
Ciro Santilli 郝海东 冠状 病 六四 事件
1
Por que grep para __time_te não time_tencontrar o tipo subjacente de time_t? Omitindo um passo?
chux - Restabelece Monica
@ chux-ReinstateMonica - O OP disse que encontrou o typedef de time_t a __time_t. Esta resposta está apenas abordando a questão de como __time_t é definido. Mas eu concordo que, para um caso genérico (em que time_t não pode ser digitado com __time_t), você precisará grep primeiro por time_t e, em seguida, possivelmente grep novamente pelo que retorna
Michael Firth
@MichaelFirth Fair suficiente. Lembro-me da minha preocupação como, apesar de o OP ter encontrado typedef __time_t time_t;, o exame do código ao redor também é necessário para garantir que o typedef foi de fato usado e não apenas parte de uma compilação condicional. typedef long time_t;pode ter sido encontrado também.
chux - Restabelece Monica
30

Padrões

William Brendel citou a Wikipedia, mas eu prefiro da boca do cavalo.

O rascunho padrão C99 N1256 7.23.1 / 3 "Componentes do tempo" diz:

Os tipos declarados são size_t (descrito em 7.17) clock_t e time_t, que são tipos aritméticos capazes de representar tempos

e 6.2.5 / 18 "Tipos" diz:

Tipos inteiros e flutuantes são chamados coletivamente de tipos aritméticos.

O POSIX 7 sys_types.h diz:

[CX] time_t deve ser um tipo inteiro.

onde [CX]é definido como :

[CX] Extensão ao padrão ISO C.

É uma extensão porque oferece uma garantia mais forte: os pontos flutuantes estão fora.

liner gcc

Não há necessidade de criar um arquivo como mencionado por Quassnoi :

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

No Ubuntu 15.10 GCC 5.2, as duas principais linhas são:

typedef long int __time_t;
typedef __time_t time_t;

Divisão de comando com algumas citações de man gcc:

  • -E: "Pare após o estágio de pré-processamento; não execute o compilador adequadamente."
  • -xc: Especifique o idioma C, uma vez que a entrada vem do stdin que não possui extensão de arquivo.
  • -include file: "Processar arquivo como se" #include "file" "aparecesse como a primeira linha do arquivo de origem primário."
  • -: entrada de stdin
Ciro Santilli adicionou uma nova foto
fonte
1
gcc -E -xc -include time.h /dev/null | grep time_t
Também
12

A resposta é definitivamente específica da implementação. Para descobrir definitivamente sua plataforma / compilador, basta adicionar esta saída em algum lugar do seu código:

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

Se a resposta for 4 (32 bits) e seus dados forem além de 2038 , você terá 25 anos para migrar seu código.

Seus dados ficarão bem se você os armazenar como uma string, mesmo que seja algo simples como:

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

Depois, leia-o da mesma maneira (fread, fscanf etc. em um int) e você terá o tempo de compensação da época. Uma solução semelhante existe no .Net. Eu passo números de época de 64 bits entre os sistemas Win e Linux sem problemas (em um canal de comunicação). Isso traz problemas de ordem de bytes, mas esse é outro assunto.

Para responder à consulta de paxdiablo, eu diria que ele imprimiu "19100" porque o programa foi escrito dessa maneira (e admito que eu mesmo fiz isso nos anos 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);

A printfinstrução imprime a sequência fixa "O ano é: 19" seguida por uma sequência preenchida com zero com os "anos desde 1900" (definição de tm->tm_year). Em 2000, esse valor é 100, obviamente. "%02d"pads com dois zeros, mas não trunca se tiver mais de dois dígitos.

A maneira correta é (mude para a última linha apenas):

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

Nova pergunta: qual é a justificativa para esse pensamento?

pwrgreg007
fonte
2
Você provavelmente deve usar o %zuespecificador de formato para o formato de size_tvalores (como gerados pelo sizeof), como eles não são assinados ( u) e de size_t length ( z) ·
Adrian Günter
... ou use printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));e evite o zproblema.
Chux - Reintegrar Monica
6

No Visual Studio 2008, o padrão é um, a __int64menos que você defina _USE_32BIT_TIME_T. É melhor você fingir que não sabe como é definido, pois ele pode (e vai) mudar de plataforma para plataforma.

Eclipse
fonte
2
Isso geralmente funciona, mas se o seu programa tiver como objetivo acompanhar as coisas que acontecerão daqui a 30 anos, é muito importante que você não tenha um time_t de 32 bits assinado.
Rob Kennedy
4
@ Rob, bah, deixa pra lá! Vamos começar a correr como galinhas sem cabeça em 2036, o mesmo que fizemos para o Y2K. Alguns de nós ganharão muito dinheiro sendo consultores do Y2k38, Leonard Nimoy lançará outro livro hilário sobre como todos devemos nos esconder na floresta ... #
315
1
... e tudo vai explodir, o público se perguntando o que era todo esse alarido. Posso até sair da aposentadoria para ganhar dinheiro com a herança das crianças :-).
paxdiablo
2
BTW, nós só encontrou um bug Y2K e que era uma página web que listou a data como 01 de janeiro, 19100. exercício para o leitor a respeito de porque ...
paxdiablo
9
Se o evento que ocorrer em 30 anos for "expirar esse backup", você poderá estar com problemas AGORA, e não em 2038. Adicione 30 anos ao time_t de 32 bits de hoje e você terá uma data no passado. Seu programa procura por eventos a serem processados, encontra um que está atrasado (em 100 anos!) E o executa. Opa, não há mais backup.
Rob Kennedy
5

time_té do tipo long intem máquinas de 64 bits, caso contrário, é long long int.

Você pode verificar isso nestes arquivos de cabeçalho:

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

(As instruções abaixo não são uma após a outra. Elas podem ser encontradas no respetivo arquivo de cabeçalho usando a pesquisa Ctrl + f.)

1) Em time.h

typedef __time_t time_t;

2) Em types.h

# define __STD_TYPE     typedef  
__STD_TYPE __TIME_T_TYPE __time_t;  

3) Em 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) Novamente em 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
fonte
Esses arquivos são fornecidos pela glibc no Ubuntu 15.10 BTW.
Ciro Santilli escreveu
3
Não está em long inttodo lugar. Veja stackoverflow.com/questions/384502/…
Zan Lynx
4

É um tipo inteiro assinado de 32 bits na maioria das plataformas herdadas. No entanto, isso faz com que seu código sofra com o bug do ano 2038 . Portanto, as bibliotecas C modernas devem defini-lo como um int assinado de 64 bits, o que é seguro por alguns bilhões de anos.


fonte
3

Normalmente, você encontrará esses typedefs específicos de implementação subjacentes para o gcc no diretório bitsou asmheader. Para mim é /usr/include/x86_64-linux-gnu/bits/types.h.

Você pode apenas grep ou usar uma chamada de pré - processador como a sugerida por Quassnoi para ver qual cabeçalho específico.

poolie
fonte
1

Em última análise, para que serve um time_t typedef?

Código robusto não se importa com o tipo.

Espécies C time_tpara ser um tipo real como double, long long, int64_t, int, etc.

Pode até ser unsignedcomo os valores de retorno de muitas funções de tempo indicando erro -1, mas (time_t)(-1)- Esta opção de implementação é incomum.

O ponto é que a "necessidade de conhecer" o tipo é rara. O código deve ser escrito para evitar a necessidade.


No entanto, uma "necessidade de saber" comum ocorre quando o código deseja imprimir o raw time_t. A conversão para o tipo inteiro mais amplo acomoda os casos mais modernos.

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

A conversão para um doubleou long doubletambém funcionará, mas pode fornecer uma saída decimal inexata

printf("%.16e", (double) now);
chux - Restabelecer Monica
fonte
Estou precisando conhecer a situação porque preciso transferir tempo de um sistema ARM para um sistema AMD64. time_t tem 32 bits no braço e 64 bits no servidor. se eu traduzir a hora em um formato e enviar uma string, é ineficiente e lento. Portanto, é muito melhor enviar o time_t inteiro e classificá-lo no final do servidor. No entanto, preciso entender um pouco mais o tipo, porque não quero que o número seja confundido por diferentes endianidades entre os sistemas, então preciso usar o htonl ... mas primeiro, com base na necessidade de saber, quero para descobrir o tipo subjacente;)
Owl
Outro caso "necessário saber", pelo menos para assinado versus não assinado, é se você precisa tomar cuidado ao subtrair os tempos. Se você apenas "subtrair e imprimir o resultado", poderá obter o que espera de um sistema com um time_t assinado, mas não com um time_t não assinado.
Michael Firth
@MichaelFirth Existem casos para o número inteiro assinado time_t e não assinado time_t em que a subtração bruta resultará em resultados inesperados. C fornece double difftime(time_t time1, time_t time0)uma abordagem de subtração uniforme.
chux - Restabelece Monica
-3

time_té apenas typedefpara 8 bytes ( long long/__int64) que todos os compiladores e sistemas operacionais entendem. Antigamente, costumava ser apenas por long int(4 bytes), mas não agora. Se você olhar para time_tdentro crtdefs.h, encontrará as duas implementações, mas o sistema operacional será usado long long.

bvrwoo_3376
fonte
5
todos os compiladores e sistemas operacionais? Não. No meu sistema Linux, o compilador pega a implementação assinada de 4 bytes.
21714 Vincent Vincent
Nos sistemas Zynq 7010, time_t é de 4 bytes.
Owl
1
Nos sistemas embarcados, trabalho com time_t quase sempre com 32 bits ou 4 bytes. O padrão afirma especificamente que é específico da implementação, o que torna esta resposta incorreta.
Cobusve