C Data e Hora (time.h)
Quase todo programa de verdade precisa do relógio: o recibo da lojinha da Ana mostra a data da venda, a agenda avisa quantos dias faltam para o aniversário, o jogo mede quanto tempo o jogador levou. Em C, tudo isso vem de um cabeçalho só, o <time.h>. Nesta aula você vai pegar a hora atual, separar dia, mês e ano, escrever a data em português, montar datas para fazer contas com elas e medir quanto tempo um pedaço do programa leva.
- O que é o
time_te por que ele conta segundos desde 1970. - Como separar a data em partes com
localtimee astruct tm(e as duas pegadinhas: o ano e o mês). - Como escrever a data do jeito que você quiser com
strftime, inclusive "sábado, 17 de outubro de 2026". - A diferença entre
gmtimeelocaltime, e o que o fuso horário tem a ver com isso. - Como montar uma data com
mktimee contar dias comdifftime. - Como medir tempo com
clocketimespec_get, e por que os dois dão números diferentes.
Todos os programas foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook, sistema em português), na pasta ~/c/tempo, com gcc -std=c23 -Wall -Wextra arquivo.c -o programa. A Ana mora em São Paulo, no fuso de Brasília (3 horas a menos que Greenwich, -03). As datas e horas dos terminais são as do momento em que cada programa rodou: no seu computador, você vai ver a sua data e a sua hora.
A ideia: o hodômetro e o calendário
O carro da Ana tem um hodômetro, aquele contador de quilômetros que só anda para a frente: 45.213 km. É um número só, fácil de guardar e de fazer contas ("andei 300 km desde a revisão"). Mas ninguém fala "nasci no segundo 1.792.247.126". As pessoas falam em dia, mês, ano, hora.
O C guarda o tempo dos dois jeitos:
time_té o hodômetro: um número de segundos contados a partir de uma data fixa. Bom para guardar e para fazer contas.struct tmé o calendário: uma struct com dia, mês, ano, hora, minuto, segundo, dia da semana. Bom para mostrar às pessoas.
Quase tudo nesta aula é passar de um para o outro.
1. time: os segundos desde 1970
#include <stdio.h>
#include <time.h>
int main(void) {
time_t agora = time(NULL);
printf("Segundos desde 1970: %lld\n", (long long) agora);
printf("Em dias, mais ou menos: %lld\n", (long long) agora / 86400);
return 0;
}
- #include <time.h>
- O cabeçalho de data e hora do C padrão.
- time_t
- O tipo que guarda um instante como número de segundos. O nome vem de "time type", tipo do tempo. O
_tno fim é o costume do C para nomes de tipos (comosize_t). - time(NULL)
- Pergunta ao sistema "que horas são?" e devolve a resposta como
time_t. ONULLquer dizer "não precisa guardar em mais nenhum lugar" (a forma antiga,time(&agora), também guarda o valor na variável). - (long long) agora
- O padrão não diz qual inteiro é o
time_t. Convertendo paralong longe usando%lld, oprintffunciona em qualquer sistema (o cenário "Quando dá errado" mostra o aviso que aparece com%d). - / 86400
- Um dia tem 24 × 60 × 60 = 86.400 segundos.
$ gcc -std=c23 -Wall -Wextra agora.c -o agora $ ./agora Segundos desde 1970: 1792247126 Em dias, mais ou menos: 20743 $ date +%s « o comando date mostra o mesmo número 1792247126
O ponto de partida, o "zero" do hodômetro, é 1º de janeiro de 1970, meia-noite, no horário de Greenwich (UTC, o horário de referência do mundo). Ele se chama era Unix (Unix epoch): foi a data escolhida pelos criadores do Unix, e hoje Linux, macOS, Android e quase toda a internet contam a partir dela. Desde então passaram uns 20 mil dias.
time_t foi um int de 32 bits, cujo maior valor é 2.147.483.647. Esse segundo cai em 19/01/2038 às 03:14:07 UTC; um segundo depois, o contador "dá a volta" (aula Inteiros) e o relógio pularia para 1901. Os sistemas de hoje usam 64 bits, que duram bilhões de anos:
$ ./y2038 O maior int32_t: 19/01/2038 03:14:07 UTC Um segundo depois: 19/01/2038 03:14:08 UTC O time_t tem 8 bytesO editor do guia também usa 8 bytes. Mas programas antigos e aparelhos pequenos que gravam a data num
int ainda vão dar trabalho em 2038.
2. localtime e a struct tm: dia, mês e ano
Para separar o número em partes, use localtime, que devolve um ponteiro para uma struct tm já preenchida (aula Structs e Ponteiros para o ->):
#include <stdio.h>
#include <time.h>
int main(void) {
time_t agora = time(NULL);
struct tm *t = localtime(&agora);
printf("Dia %d do mês %d de %d\n", t->tm_mday, t->tm_mon + 1, t->tm_year + 1900);
printf("Hora: %02d:%02d:%02d\n", t->tm_hour, t->tm_min, t->tm_sec);
printf("Dia da semana: %d (0 = domingo)\n", t->tm_wday);
printf("Dia do ano: %d\n", t->tm_yday + 1);
return 0;
}
$ gcc -std=c23 -Wall -Wextra partes.c -o partes $ ./partes Dia 17 do mês 10 de 2026 Hora: 11:25:26 Dia da semana: 6 (0 = domingo) Dia do ano: 290 $ date sáb 17 out 2026 11:25:26 -03
Os campos da struct tm ("time", tempo) têm algumas regras estranhas, que vêm dos anos 1970 e nunca mudaram:
| Campo | O que guarda | Valores | Para mostrar |
|---|---|---|---|
tm_year | Anos desde 1900 | 2026 vira 126 | tm_year + 1900 |
tm_mon | Mês, começando em 0 | 0 = janeiro … 11 = dezembro | tm_mon + 1 |
tm_mday | Dia do mês | 1 a 31 (este começa em 1!) | direto |
tm_hour, tm_min | Hora e minuto | 0 a 23, 0 a 59 | direto |
tm_sec | Segundo | 0 a 60 (o 60 é o "segundo bissexto", raríssimo) | direto |
tm_wday | Dia da semana | 0 = domingo … 6 = sábado | use como índice de uma lista de nomes |
tm_yday | Dia do ano | 0 = 1º de janeiro … 365 | tm_yday + 1 |
tm_isdst | Horário de verão? | maior que 0 = sim, 0 = não, negativo = não sei | (seção 5) |
Por que o mês começa em 0 e o dia em 1? Porque o mês costuma ser usado como índice de um array de nomes (meses[t->tm_mon]), e arrays em C começam em 0 (aula Arrays). O dia do mês é só um número para mostrar.
Experimente: rode e compare a hora com o relógio do seu computador. O editor do guia não sabe o seu fuso horário: ele usa sempre o horário de Greenwich (UTC). Quem está em Brasília vai ver a hora 3 horas adiantada. Depois crie um array const char *semana[] = {"domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado"}; e mostre semana[t->tm_wday] no lugar do número.
#include <stdio.h>
#include <time.h>
int main(void) {
time_t agora = time(NULL);
struct tm *t = localtime(&agora);
printf("Dia %d do mês %d de %d\n", t->tm_mday, t->tm_mon + 1, t->tm_year + 1900);
printf("Hora: %02d:%02d:%02d\n", t->tm_hour, t->tm_min, t->tm_sec);
printf("Dia da semana: %d (0 = domingo)\n", t->tm_wday);
printf("Dia do ano: %d\n", t->tm_yday + 1);
return 0;
}
3. strftime: a data do jeito que você quiser
Montar a data com printf e somar 1900 toda vez cansa. O strftime ("string format time", formatar o tempo num texto) faz isso por você, com códigos parecidos com os do printf:
#include <stdio.h>
#include <time.h>
#include <locale.h>
int main(void) {
time_t agora = time(NULL);
struct tm *t = localtime(&agora);
char texto[100];
strftime(texto, sizeof texto, "%d/%m/%Y %H:%M", t);
printf("Curto: %s\n", texto);
strftime(texto, sizeof texto, "%A, %d de %B de %Y", t);
printf("Antes do setlocale: %s\n", texto);
setlocale(LC_ALL, "");
strftime(texto, sizeof texto, "%A, %d de %B de %Y", t);
printf("Depois do setlocale: %s\n", texto);
return 0;
}
- char texto[100];
- O
strftimeescreve num array seu, como osnprintf(aula Strings). Ele precisa caber o texto inteiro mais o'\0'. - strftime(texto, sizeof texto, formato, t)
- Quatro coisas: onde escrever, quanto cabe, o molde e a
struct tm. Devolve quantos caracteres escreveu, ou 0 se não coube. - "%d/%m/%Y %H:%M"
- Dia, mês e ano com 4 dígitos, hora e minuto. O
strftimejá soma o 1 do mês e o 1900 do ano, e põe o zero à esquerda (09). - %A, %B
- O nome do dia da semana e o nome do mês, no idioma do programa.
- setlocale(LC_ALL, "")
- Passa a usar o idioma do sistema, como na aula errno e perror.
$ gcc -std=c23 -Wall -Wextra formatar.c -o formatar $ ./formatar Curto: 17/10/2026 11:25 Antes do setlocale: Saturday, 17 de October de 2026 Depois do setlocale: sábado, 17 de outubro de 2026
Sem setlocale, todo programa C começa no idioma neutro "C" (inglês): Saturday, October. Com ele, a glibc do Linux escreve em português. No editor do guia, os nomes saem sempre em inglês: a biblioteca C dele não traz traduções.
| Código | O que escreve | Exemplo (pt_BR) |
|---|---|---|
%d | Dia do mês, 2 dígitos | 17 |
%m | Mês, 2 dígitos (já com o +1) | 10 |
%Y / %y | Ano com 4 dígitos / os 2 últimos | 2026 / 26 |
%H, %M, %S | Hora (00–23), minuto, segundo | 11, 25, 26 |
%A / %a | Dia da semana, inteiro / abreviado | sábado / sáb |
%B / %b | Mês, inteiro / abreviado | outubro / out |
%j | Dia do ano (001–366) | 290 |
%z | Distância de Greenwich | -0300 |
%F | O mesmo que %Y-%m-%d (o formato ISO, bom para nomes de arquivo) | 2026-10-17 |
%x / %X | A data / a hora no formato do idioma | 17/10/2026 / 11:25:26 |
%% | Um % de verdade | % |
Para mostrar uma data que não é a de hoje, preencha você mesmo a struct tm. O = {0} zera todos os campos (aula Structs), e depois você ajusta os que importam:
Missão: o programa mostra a data do Natal no formato americano, 12/25/26 (mês primeiro, ano com 2 dígitos). Mude só o molde do strftime para o formato brasileiro, com o ano inteiro: Natal: 25/12/2026.
#include <stdio.h>
#include <time.h>
int main(void) {
struct tm natal = {0};
natal.tm_year = 2026 - 1900;
natal.tm_mon = 12 - 1;
natal.tm_mday = 25;
char texto[32];
strftime(texto, sizeof texto, "%m/%d/%y", &natal);
printf("Natal: %s\n", texto);
return 0;
}
#include <stdio.h>
#include <time.h>
int main(void) {
struct tm natal = {0};
natal.tm_year = 2026 - 1900;
natal.tm_mon = 12 - 1;
natal.tm_mday = 25;
char texto[32];
strftime(texto, sizeof texto, "%d/%m/%Y", &natal);
printf("Natal: %s\n", texto);
return 0;
}
4. gmtime, localtime e o fuso horário
O time_t é o mesmo no mundo inteiro: no instante em que a Ana roda o programa em São Paulo, o computador de uma amiga em Tóquio tem o mesmo número. O que muda é como cada um lê esse número no relógio. Por isso há duas funções:
gmtime("Greenwich Mean Time") separa o instante no horário de Greenwich, o UTC;localtimesepara no fuso do computador.
#include <stdio.h>
#include <time.h>
void mostrar(const char *nome, const struct tm *t) {
char texto[64];
strftime(texto, sizeof texto, "%d/%m/%Y %H:%M:%S %z", t);
printf("%-10s %s\n", nome, texto);
}
int main(void) {
time_t zero = 0;
mostrar("gmtime:", gmtime(&zero));
mostrar("localtime:", localtime(&zero));
return 0;
}
$ gcc -std=c23 -Wall -Wextra fuso.c -o fuso $ ./fuso gmtime: 01/01/1970 00:00:00 +0000 localtime: 31/12/1969 21:00:00 -0300 $ TZ=UTC ./fuso « finge que o computador está em Greenwich gmtime: 01/01/1970 00:00:00 +0000 localtime: 01/01/1970 00:00:00 +0000 $ TZ=Asia/Tokyo ./fuso « e agora em Tóquio gmtime: 01/01/1970 00:00:00 +0000 localtime: 01/01/1970 09:00:00 +0900
O segundo zero da era Unix foi meia-noite em Greenwich, mas em São Paulo ainda eram 9 da noite do dia 31 de dezembro de 1969. O gmtime não muda nunca; o localtime segue o fuso, que no Linux vem do arquivo /etc/localtime ou da variável de ambiente TZ (aula Variáveis de Ambiente). O TZ=… antes do comando muda a variável só para aquela execução.
localtime e gmtime dão o mesmo resultado (01/01/1970 00:00:00 +0000 nos dois). Por isso as missões desta aula nunca dependem da hora atual nem do fuso: elas usam datas fixas.
5. mktime: montar uma data e fazer contas
O caminho de volta, da struct tm para o time_t, é o mktime ("make time", fazer o tempo). Com dois time_t, a diferença em segundos sai do difftime. Quantos dias faltam para o Natal?
#include <stdio.h>
#include <time.h>
int main(void) {
struct tm hoje = {0};
hoje.tm_year = 2026 - 1900;
hoje.tm_mon = 10 - 1;
hoje.tm_mday = 17;
hoje.tm_hour = 12;
hoje.tm_isdst = -1;
struct tm natal = hoje;
natal.tm_mon = 12 - 1;
natal.tm_mday = 25;
time_t t1 = mktime(&hoje);
time_t t2 = mktime(&natal);
double segundos = difftime(t2, t1);
printf("Faltam %.0f dias para o Natal.\n", segundos / 86400);
printf("Hoje é o dia %d da semana; o Natal, o dia %d.\n", hoje.tm_wday, natal.tm_wday);
return 0;
}
| Linha | O que faz |
|---|---|
struct tm hoje = {0}; | Começa com tudo zerado. Sem isso, os campos que você não preencher teriam lixo (aula Variáveis). |
2026 - 1900, 10 - 1 | As duas pegadinhas da seção 2, escritas de um jeito que se lê: "ano 2026", "mês 10". |
tm_hour = 12 | Meio-dia: longe da meia-noite, a conta não erra por causa de uma mudança de horário de verão. |
tm_isdst = -1 | "Não sei se é horário de verão: descubra você." É o valor certo quase sempre. |
struct tm natal = hoje; | Copia a struct inteira e muda só o mês e o dia. |
mktime(&hoje) | Converte para time_t e completa a struct: preenche tm_wday e tm_yday, que você não deu. |
difftime(t2, t1) | t2 - t1 em segundos, como double. Funciona qualquer que seja o tipo por trás do time_t. |
$ gcc -std=c23 -Wall -Wextra natal.c -o natal $ ./natal Faltam 69 dias para o Natal. Hoje é o dia 6 da semana; o Natal, o dia 5.
69 dias, e o mktime descobriu sozinho que 17/10/2026 é um sábado (6) e o Natal, uma sexta (5).
O mktime arruma datas impossíveis
O mktime aceita campos "fora do lugar" e normaliza: dia 32 vira o dia 1 do mês seguinte, mês 12 vira janeiro do ano seguinte. Isso deixa somar dias muito fácil:
#include <stdio.h>
#include <time.h>
int main(void) {
struct tm data = {0};
data.tm_year = 2026 - 1900;
data.tm_mon = 10 - 1;
data.tm_mday = 17 + 100; // daqui a 100 dias
data.tm_hour = 12;
data.tm_isdst = -1;
mktime(&data);
char texto[32];
strftime(texto, sizeof texto, "%d/%m/%Y", &data);
printf("Daqui a 100 dias: %s\n", texto);
return 0;
}
$ gcc -std=c23 -Wall -Wextra normaliza.c -o normaliza $ ./normaliza Daqui a 100 dias: 25/01/2027
"Dia 117 de outubro" virou 25 de janeiro de 2027: o mktime contou os dias de outubro, novembro e dezembro e passou o ano. Você não precisa saber quantos dias tem cada mês, nem quais anos são bissextos.
6. Medir quanto tempo leva: clock e timespec_get
Há dois tempos diferentes para medir um programa, e o C tem uma função para cada:
- tempo de relógio: o que o cronômetro na mão da Ana marcaria, contando as esperas. Use
timespec_getC11, que dá segundos e nanossegundos (otimesó dá segundos inteiros, pouco para medir). - tempo de processador: só o tempo em que o processador trabalhou para o programa. Use
clock.
#include <stdio.h>
#include <time.h>
#include <unistd.h>
double segundos_do_relogio(void) {
struct timespec agora;
timespec_get(&agora, TIME_UTC);
return agora.tv_sec + agora.tv_nsec / 1e9;
}
int main(void) {
double r0 = segundos_do_relogio();
clock_t c0 = clock();
sleep(2); // espera 2 s sem trabalhar
volatile double soma = 0;
for (int i = 0; i < 100000000; i++) { // trabalha um pouco
soma += i;
}
double cpu = (double) (clock() - c0) / CLOCKS_PER_SEC;
double relogio = segundos_do_relogio() - r0;
printf("Tempo de relógio: %.2f s\n", relogio);
printf("Tempo de processador: %.2f s\n", cpu);
return 0;
}
- struct timespec
- Uma struct com dois campos:
tv_sec(segundos, umtime_t) etv_nsec(nanossegundos, bilionésimos de segundo, de 0 a 999.999.999). - timespec_get(&agora, TIME_UTC)
- Preenche a struct com o instante atual.
TIME_UTCé a única base que todo sistema tem. - agora.tv_nsec / 1e9
- Transforma os nanossegundos em fração de segundo (
1e9é 1 bilhão, comodouble). - sleep(2)
- Faz o programa dormir 2 segundos (do
unistd.h, do POSIX: existe no Linux e no macOS). - volatile double soma
- Obriga o laço a acontecer de verdade (aula volatile e restrict).
- clock()
- Devolve o tempo de processador gasto até agora, num tipo
clock_t, contado em "tiques". - / CLOCKS_PER_SEC
- Quantos tiques há num segundo. Dividindo, o resultado fica em segundos. O
(double)evita uma divisão inteira (aula Conversão de Tipos).
$ gcc -std=c23 -Wall -Wextra cronometro.c -o cronometro $ ./cronometro Tempo de relógio: 2.33 s Tempo de processador: 0.33 s
Os 2 segundos do sleep contam no relógio, mas não no processador: enquanto o programa dormia, o processador cuidava de outros programas. Para saber "quanto o meu código é rápido", o clock é melhor, porque não conta as esperas. Para saber "quanto o usuário esperou", use o relógio.
clock conta também as esperas. O mesmo programa mostra Tempo de processador: 2.33 s lá: o editor não tem como medir o tempo de processador dentro do navegador e usa o relógio no lugar. Além disso, o CLOCKS_PER_SEC vale 1.000.000 no Linux e 1.000.000.000 no editor: sempre divida por ele, nunca por um número escrito à mão.
7. O localtime reaproveita a mesma struct
O localtime e o gmtime devolvem um ponteiro para uma struct tm que fica dentro da biblioteca, a mesma para todas as chamadas. Cada chamada escreve por cima da anterior:
#include <stdio.h>
#include <time.h>
int main(void) {
time_t virada = 946728000; // 1º de janeiro de 2000
time_t natal = 1798200000; // 25 de dezembro de 2026
struct tm *a = localtime(&virada);
struct tm *b = localtime(&natal);
printf("a: ano %d\n", a->tm_year + 1900);
printf("b: ano %d\n", b->tm_year + 1900);
printf("a e b apontam para o mesmo lugar? %s\n", a == b ? "sim" : "não");
return 0;
}
$ gcc -std=c23 -Wall -Wextra sobrescreve.c -o sobrescreve $ ./sobrescreve a: ano 2026 b: ano 2026 a e b apontam para o mesmo lugar? sim
O ano 2000 sumiu: a segunda chamada escreveu 2026 no mesmo lugar, e a e b são o mesmo ponteiro. Há dois consertos:
- Copie a struct logo depois de cada chamada:
struct tm a = *localtime(&virada);(o*copia o conteúdo, não o endereço). - Use
localtime_r, que escreve numa struct sua. O_rvem de "reentrant" (reentrante): pode ser chamada de vários lugares ao mesmo tempo sem uma atrapalhar a outra, o que importa muito com threads. Veio do POSIX e entrou no C padrão no C23, junto comgmtime_r.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <time.h>
int main(void) {
time_t virada = 946728000; // 1º de janeiro de 2000
time_t natal = 1798200000; // 25 de dezembro de 2026
struct tm a, b;
localtime_r(&virada, &a);
localtime_r(&natal, &b);
printf("a: ano %d\n", a.tm_year + 1900);
printf("b: ano %d\n", b.tm_year + 1900);
return 0;
}
$ gcc -std=c23 -Wall -Wextra sobrescreve2.c -o sobrescreve2 $ ./sobrescreve2 a: ano 2000 b: ano 2026
A primeira linha, #define _POSIX_C_SOURCE 200809L, pede ao cabeçalho as funções do POSIX (como na aula Compilação Condicional). O gcc 15 com -std=c23 já aceita localtime_r sem ela, mas com -std=c17 dá error: implicit declaration of function ‘localtime_r’, e o editor do guia também exige a linha (use of undeclared identifier 'localtime_r'). Com ela, funciona nos dois.
Qual função usar?
| Você quer… | Use |
|---|---|
| A hora atual, para guardar ou comparar | time(NULL) |
| Dia, mês, ano, hora de um instante | localtime_r (ou localtime, copiando a struct) |
| O mesmo, no horário de Greenwich (logs, servidores) | gmtime_r / gmtime |
| Mostrar a data para uma pessoa | strftime (com setlocale para português) |
| Uma data que não é hoje, dias da semana, somar dias | struct tm + mktime |
| A diferença entre dois instantes | difftime |
| Quanto o código gastou de processador | clock / CLOCKS_PER_SEC |
| Quanto tempo passou no relógio, com frações de segundo | timespec_get |
ctime e o asctime? São duas funções antigas que devolvem a data num formato fixo, em inglês: Wed Dec 31 21:00:00 1969. Elas usam um texto guardado dentro da biblioteca (o mesmo problema da seção 7) e não deixam escolher o formato. O C23 as marcou como obsoletas: use strftime. Você ainda vai vê-las em código antigo.
Quando dá errado
17/9/126
printf("Hoje: %d/%d/%d\n", t->tm_mday, t->tm_mon, t->tm_year);
$ gcc -std=c23 -Wall -Wextra cru.c -o cru $ ./cru Hoje: 17/9/126
O que significa: as duas pegadinhas da struct tm ao mesmo tempo. O mês começa em 0 (outubro é 9) e o ano conta desde 1900 (2026 é 126). Nenhum compilador avisa: para ele, são só números.
Como consertar: t->tm_mon + 1 e t->tm_year + 1900, ou, melhor ainda, strftime com "%d/%m/%Y", que já faz as contas e põe os zeros.
warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘time_t’
$ gcc -std=c23 -Wall -Wextra formato.c -o formato
formato.c: In function ‘main’:
formato.c:6:21: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘time_t’ {aka ‘long int’} [-Wformat=]
6 | printf("Agora: %d\n", agora);
| ~^ ~~~~~
| | |
| int time_t {aka long int}
| %ld
O que significa: o %d é para int, mas no Linux o time_t é um long (8 bytes). O gcc sugere %ld, que serve no Linux; no editor do guia o time_t é long long, e o Clang sugere %lld. Cada sistema escolhe o seu tipo.
Como consertar: converta para um tipo conhecido: printf("%lld\n", (long long) agora);. Funciona em qualquer lugar.
strftime devolveu 0 e o texto saiu vazio ou estranho
$ ./curto « texto[10] para "sábado, 17 de outubro de 2026" strftime escreveu 0 caracteres Não coube!
O que significa: o array era pequeno demais para o texto. O strftime não escreve pela metade: devolve 0, e o conteúdo do array fica indefinido. Repare que, com setlocale, os nomes ficam maiores ("sábado" tem acento, que ocupa 2 bytes em UTF-8; aula Acentos e UTF-8).
Como consertar: use um array folgado (64 ou 100 bytes) e confira o retorno: if (strftime(…) == 0) { … }.
O que significa: você guardou dois ponteiros devolvidos por localtime (ou gmtime), e a segunda chamada escreveu por cima da primeira (o sobrescreve.c da seção 7).
Como consertar: localtime_r com uma struct sua para cada data, ou copie com struct tm a = *localtime(&t);.
O que significa: o editor do guia usa o horário de Greenwich (UTC). No seu computador, com gcc, o localtime usa o fuso do sistema.
Como consertar: nada a consertar no código. Para ver a sua hora, compile no seu computador (aula Instalar no Linux).
Referência rápida
| Escrita | O que faz |
|---|---|
time_t agora = time(NULL); | Segundos desde 01/01/1970 00:00 UTC |
struct tm *t = localtime(&agora); | Separa no fuso local (struct da biblioteca, reaproveitada) |
gmtime(&agora) | Separa no horário de Greenwich |
localtime_r(&agora, &minha) | Separa numa struct sua (C23 e POSIX) |
t->tm_year + 1900, t->tm_mon + 1 | O ano e o mês de verdade |
strftime(texto, sizeof texto, "%d/%m/%Y", t) | Escreve a data; devolve 0 se não coube |
setlocale(LC_ALL, "") | Nomes de dias e meses em português (<locale.h>) |
struct tm d = {0}; … d.tm_isdst = -1; mktime(&d) | Monta uma data, normaliza e preenche tm_wday/tm_yday |
difftime(fim, inicio) | Diferença em segundos (double) |
(double) (clock() - c0) / CLOCKS_PER_SEC | Tempo de processador em segundos |
timespec_get(&ts, TIME_UTC) | Instante com nanossegundos (C11) |
ctime, asctime | Formato fixo em inglês; obsoletas no C23 |
Exercícios
1. O ano de verdade
Complete para mostrar o ano com 4 dígitos:
2. O código do mês
Complete o molde do strftime para escrever 17/10/2026:
3. A diferença
Complete o nome da função que dá a diferença em segundos entre dois time_t:
4. Em março, quanto vale tm_mon?
5. O programa dorme 5 segundos com sleep(5) e quase não faz contas. No Linux, o que o clock vai medir?
6. Para que serve o mktime?
7. Em São Paulo, localtime do instante 0 dá 31/12/1969, 21:00. Quanto dá gmtime do mesmo instante?
Missão final: contagem regressiva para o Natal
Missão: o programa da Ana deveria contar os dias de 17/10/2026 até o Natal e dizer o dia da semana, mas diz que faltam 694060 dias e que o Natal é no ano 3927! A data de hoje está certa; os dois campos errados estão na data do Natal. Conserte-os (lembre as duas pegadinhas) para a saída ser:
Faltam 69 dias para o Natal. O Natal de 2026 cai num(a) sexta-feira.
#include <stdio.h>
#include <time.h>
int main(void) {
const char *dias[] = {"domingo", "segunda-feira", "terça-feira", "quarta-feira",
"quinta-feira", "sexta-feira", "sábado"};
struct tm hoje = {0};
hoje.tm_year = 2026 - 1900;
hoje.tm_mon = 10 - 1;
hoje.tm_mday = 17;
hoje.tm_hour = 12;
hoje.tm_isdst = -1;
struct tm natal = hoje;
natal.tm_year = 2026;
natal.tm_mon = 12;
natal.tm_mday = 25;
time_t t1 = mktime(&hoje);
time_t t2 = mktime(&natal);
printf("Faltam %.0f dias para o Natal.\n", difftime(t2, t1) / 86400);
printf("O Natal de %d cai num(a) %s.\n", natal.tm_year + 1900, dias[natal.tm_wday]);
return 0;
}
#include <stdio.h>
#include <time.h>
int main(void) {
const char *dias[] = {"domingo", "segunda-feira", "terça-feira", "quarta-feira",
"quinta-feira", "sexta-feira", "sábado"};
struct tm hoje = {0};
hoje.tm_year = 2026 - 1900;
hoje.tm_mon = 10 - 1;
hoje.tm_mday = 17;
hoje.tm_hour = 12;
hoje.tm_isdst = -1;
struct tm natal = hoje;
natal.tm_year = 2026 - 1900;
natal.tm_mon = 12 - 1;
natal.tm_mday = 25;
time_t t1 = mktime(&hoje);
time_t t2 = mktime(&natal);
printf("Faltam %.0f dias para o Natal.\n", difftime(t2, t1) / 86400);
printf("O Natal de %d cai num(a) %s.\n", natal.tm_year + 1900, dias[natal.tm_wday]);
return 0;
}
Por que 3927, e não 3926? O tm_year = 2026 já dava o ano 1900 + 2026 = 3926, e o mês 12 (que seria o 13º mês) fez o mktime normalizar para janeiro do ano seguinte. Um erro em cada campo, e o Natal foi parar 1900 anos no futuro.
Resumo
time(NULL)devolve umtime_t: os segundos desde 01/01/1970 00:00 UTC (a era Unix). Para imprimir,(long long)e%lld.localtime(fuso do computador) egmtime(Greenwich) separam o instante numastruct tm. O ano conta desde 1900 e o mês começa em 0.strftimeescreve a data com códigos como%d/%m/%Ye%A; comsetlocale(LC_ALL, ""), em português. Confira se não devolveu 0.- Para montar uma data:
struct tm d = {0}, preencha os campos,tm_isdst = -1emktime. Ele normaliza (dia 117 de outubro vira 25 de janeiro) e descobre o dia da semana. A diferença sai dodifftime. clockmede o tempo de processador (sem as esperas);timespec_get, o tempo de relógio com nanossegundos.localtimereaproveita a mesma struct a cada chamada; prefiralocaltime_r(C23).- No editor do guia: fuso sempre UTC, nomes em inglês, e
clockconta as esperas.