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.

Você vai aprender
  • O que é o time_t e por que ele conta segundos desde 1970.
  • Como separar a data em partes com localtime e a struct 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 gmtime e localtime, e o que o fuso horário tem a ver com isso.
  • Como montar uma data com mktime e contar dias com difftime.
  • Como medir tempo com clock e timespec_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 _t no fim é o costume do C para nomes de tipos (como size_t).
time(NULL)
Pergunta ao sistema "que horas são?" e devolve a resposta como time_t. O NULL quer 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 para long long e usando %lld, o printf funciona 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.

O problema do ano 2038. Por décadas, o 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 bytes
O 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:

CampoO que guardaValoresPara mostrar
tm_yearAnos desde 19002026 vira 126tm_year + 1900
tm_monMês, começando em 00 = janeiro … 11 = dezembrotm_mon + 1
tm_mdayDia do mês1 a 31 (este começa em 1!)direto
tm_hour, tm_minHora e minuto0 a 23, 0 a 59direto
tm_secSegundo0 a 60 (o 60 é o "segundo bissexto", raríssimo)direto
tm_wdayDia da semana0 = domingo … 6 = sábadouse como índice de uma lista de nomes
tm_ydayDia do ano0 = 1º de janeiro … 365tm_yday + 1
tm_isdstHorá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 strftime escreve num array seu, como o snprintf (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 strftime já 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ódigoO que escreveExemplo (pt_BR)
%dDia do mês, 2 dígitos17
%mMês, 2 dígitos (já com o +1)10
%Y / %yAno com 4 dígitos / os 2 últimos2026 / 26
%H, %M, %SHora (00–23), minuto, segundo11, 25, 26
%A / %aDia da semana, inteiro / abreviadosábado / sáb
%B / %bMês, inteiro / abreviadooutubro / out
%jDia do ano (001–366)290
%zDistância de Greenwich-0300
%FO mesmo que %Y-%m-%d (o formato ISO, bom para nomes de arquivo)2026-10-17
%x / %XA data / a hora no formato do idioma17/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;
  • localtime separa 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.

No editor do guia, o fuso é sempre UTC: lá, 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;
}
LinhaO 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 - 1As duas pegadinhas da seção 2, escritas de um jeito que se lê: "ano 2026", "mês 10".
tm_hour = 12Meio-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_get C11, que dá segundos e nanossegundos (o time só 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, um time_t) e tv_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, como double).
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.

No editor do guia, 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:

  1. Copie a struct logo depois de cada chamada: struct tm a = *localtime(&virada); (o * copia o conteúdo, não o endereço).
  2. Use localtime_r, que escreve numa struct sua. O _r vem 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 com gmtime_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 comparartime(NULL)
Dia, mês, ano, hora de um instantelocaltime_r (ou localtime, copiando a struct)
O mesmo, no horário de Greenwich (logs, servidores)gmtime_r / gmtime
Mostrar a data para uma pessoastrftime (com setlocale para português)
Uma data que não é hoje, dias da semana, somar diasstruct tm + mktime
A diferença entre dois instantesdifftime
Quanto o código gastou de processadorclock / CLOCKS_PER_SEC
Quanto tempo passou no relógio, com frações de segundotimespec_get
E o 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

A data sai como 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.

O 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) { … }.

Duas datas diferentes aparecem iguais

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);.

A hora está 3 horas adiantada no editor

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

EscritaO 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 + 1O 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_SECTempo de processador em segundos
timespec_get(&ts, TIME_UTC)Instante com nanossegundos (C11)
ctime, asctimeFormato fixo em inglês; obsoletas no C23

Exercícios

1. O ano de verdade

Complete para mostrar o ano com 4 dígitos:

printf("%d\n", t->tm_year + );

2. O código do mês

Complete o molde do strftime para escrever 17/10/2026:

strftime(texto, sizeof texto, "%d//%Y", t);

3. A diferença

Complete o nome da função que dá a diferença em segundos entre dois time_t:

double s = (fim, inicio);

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 um time_t: os segundos desde 01/01/1970 00:00 UTC (a era Unix). Para imprimir, (long long) e %lld.
  • localtime (fuso do computador) e gmtime (Greenwich) separam o instante numa struct tm. O ano conta desde 1900 e o mês começa em 0.
  • strftime escreve a data com códigos como %d/%m/%Y e %A; com setlocale(LC_ALL, ""), em português. Confira se não devolveu 0.
  • Para montar uma data: struct tm d = {0}, preencha os campos, tm_isdst = -1 e mktime. Ele normaliza (dia 117 de outubro vira 25 de janeiro) e descobre o dia da semana. A diferença sai do difftime.
  • clock mede o tempo de processador (sem as esperas); timespec_get, o tempo de relógio com nanossegundos.
  • localtime reaproveita a mesma struct a cada chamada; prefira localtime_r (C23).
  • No editor do guia: fuso sempre UTC, nomes em inglês, e clock conta as esperas.