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 .
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.
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).
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.
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?
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.
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:
É 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.
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.
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",(longlong) now);
A conversão para um doubleou long doubletambém funcionará, mas pode fornecer uma saída decimal inexata
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.
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.
long int
.Respostas:
O artigo do artigo time_t da Wikipedia lança alguma luz sobre isso. A linha inferior é que o tipo de
time_t
não é garantido na especificação C.fonte
time_t
a 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 etime_t
pode 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 apenastime_t
.time.h
conteúdos. Esse artigo tem um link para cppreference.com, mas o conteúdo citado não é encontrado em nenhum lugar…time_t
está 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", mastime_t
diz apenas que "deve ser um tipo inteiro" . Uma implementação pode deixar de sertime_t
assinada e ainda ser compatível com POSIX.[root]# cat time.c
[root]# gcc -E time.c | grep __time_t
typedef long int __time_t;
É definido
$INCDIR/bits/types.h
através de:fonte
typedef __int32_t __time_t;
etypedef __time_t time_t;
em umFreeBSD 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).__time_t
e nãotime_t
encontrar o tipo subjacente detime_t
? Omitindo um passo?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.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:
e 6.2.5 / 18 "Tipos" diz:
O POSIX 7 sys_types.h diz:
onde
[CX]
é definido como :É 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 :
No Ubuntu 15.10 GCC 5.2, as duas principais linhas são:
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 stdinfonte
gcc -E -xc -include time.h /dev/null | grep time_t
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:
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:
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):
A
printf
instrução imprime a sequência fixa "O ano é: 19" seguida por uma sequência preenchida com zero com os "anos desde 1900" (definição detm->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):
Nova pergunta: qual é a justificativa para esse pensamento?
fonte
%zu
especificador de formato para o formato desize_t
valores (como gerados pelosizeof
), como eles não são assinados (u
) e de size_t length (z
) ·printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));
e evite oz
problema.No Visual Studio 2008, o padrão é um, a
__int64
menos 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.fonte
time_t
é do tipolong int
em 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.h
etypesizes.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
2) Em
types.h
3) Em
typesizes.h
4) Novamente em
types.h
fonte
long int
todo lugar. Veja stackoverflow.com/questions/384502/…É 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
Normalmente, você encontrará esses typedefs específicos de implementação subjacentes para o gcc no diretório
bits
ouasm
header. 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.
fonte
Código robusto não se importa com o tipo.
Espécies C
time_t
para ser um tipo real comodouble, long long, int64_t, int
, etc.Pode até ser
unsigned
como 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.A conversão para um
double
oulong double
também funcionará, mas pode fornecer uma saída decimal inexatafonte
double difftime(time_t time1, time_t time0)
uma abordagem de subtração uniforme.time_t
é apenastypedef
para 8 bytes (long long/__int64
) que todos os compiladores e sistemas operacionais entendem. Antigamente, costumava ser apenas porlong int
(4 bytes), mas não agora. Se você olhar paratime_t
dentrocrtdefs.h
, encontrará as duas implementações, mas o sistema operacional será usadolong long
.fonte