A manipulação de tempo e data é uma necessidade comum em programas: registrar eventos, medir intervalos, exibir timestamps ou otimizar desempenho. A biblioteca padrão time.h fornece funções e tipos para lidar com tempo de forma portátil. Nesta aula, vamos explorar as principais funcionalidades dessa biblioteca, desde a obtenção do tempo atual até a formatação de datas e a medição precisa de desempenho.

Dominar essas funções é essencial para qualquer programador C que precise trabalhar com cronometragem, logs, agendamentos ou benchmarks. Veremos como converter entre representações de tempo, como usar relógios de alta resolução e como evitar armadilhas comuns.

time e clock

A função time() retorna o tempo atual em segundos desde a época (1 de janeiro de 1970, 00:00:00 UTC). O valor é do tipo time_t, que geralmente é um inteiro longo. Para obter o tempo, basta passar um ponteiro para time_t ou NULL:

#include <stdio.h>
#include <time.h>

int main() {
    time_t agora = time(NULL);
    printf("Segundos desde a época: %ld\n", (long)agora);
    return 0;
}

clock() mede o tempo de CPU gasto pelo programa, em unidades de CLOCKS_PER_SEC. É útil para medir o desempenho de trechos de código, pois conta o tempo de processador, não o tempo de parede. Exemplo:

#include <stdio.h>
#include <time.h>

int main() {
    clock_t inicio = clock();
    // Simula trabalho pesado
    for (long i = 0; i < 100000000; i++);
    clock_t fim = clock();
    double tempo = (double)(fim - inicio) / CLOCKS_PER_SEC;
    printf("Tempo de CPU: %f segundos\n", tempo);
    return 0;
}

Note que clock() pode não ser preciso em sistemas com múltiplos núcleos, pois conta o tempo de CPU total. Para medir tempo de parede, use time() ou gettimeofday() (não padrão).

Formatação

Para formatar datas e horas, usamos struct tm, que representa o tempo decomposto em campos (ano, mês, dia, hora, etc.). A função localtime() converte time_t para struct tm no fuso local, e gmtime() para UTC. Depois, strftime() formata conforme especificadores:

#include <stdio.h>
#include <time.h>

int main() {
    time_t agora = time(NULL);
    struct tm *info = localtime(&agora);
    char buffer[80];
    strftime(buffer, sizeof(buffer), "%d/%m/%Y %H:%M:%S", info);
    printf("Data e hora: %s\n", buffer);
    return 0;
}

Especificadores comuns: %d dia, %m mês, %Y ano, %H hora (24h), %M minuto, %S segundo, %A nome do dia, %B nome do mês. Também podemos usar asctime() para uma formatação fixa, mas strftime() é mais flexível.

Medindo desempenho

Para medir o tempo de execução de um trecho de código, além de clock(), podemos usar time() para intervalos longos e clock_gettime() (POSIX) para alta precisão. O padrão C11 oferece timespec_get() que preenche uma estrutura struct timespec com resolução de nanossegundos:

#include <stdio.h>
#include <time.h>

int main() {
    struct timespec inicio, fim;
    timespec_get(&inicio, TIME_UTC);
    // Trabalho
    for (long i = 0; i < 100000000; i++);
    timespec_get(&fim, TIME_UTC);
    double seg = (fim.tv_sec - inicio.tv_sec) + (fim.tv_nsec - inicio.tv_nsec) / 1e9;
    printf("Tempo decorrido: %f s\n", seg);
    return 0;
}

Essa abordagem é preferível quando precisamos de precisão. difftime() também pode ser usada para comparar dois time_t, retornando a diferença em segundos.

Exemplos

Vamos consolidar com um programa que exibe a data atual em vários formatos e mede o tempo de uma operação:

#include <stdio.h>
#include <time.h>

int main() {
    // Obtém e formata a data
    time_t agora = time(NULL);
    struct tm *tm = localtime(&agora);
    char data[100];
    strftime(data, sizeof(data), "%A, %d de %B de %Y - %H:%M:%S", tm);
    printf("%s\n", data);

    // Mede o tempo de uma soma
    clock_t c_inicio = clock();
    long soma = 0;
    for (long i = 0; i < 100000000; i++) soma += i;
    clock_t c_fim = clock();
    double cpu = (double)(c_fim - c_inicio) / CLOCKS_PER_SEC;

    struct timespec t_inicio, t_fim;
    timespec_get(&t_inicio, TIME_UTC);
    soma = 0;
    for (long i = 0; i < 100000000; i++) soma += i;
    timespec_get(&t_fim, TIME_UTC);
    double decorrido = (t_fim.tv_sec - t_inicio.tv_sec) + (t_fim.tv_nsec - t_inicio.tv_nsec)/1e9;

    printf("Soma: %ld\n", soma);
    printf("Tempo de CPU: %.6f s\n", cpu);
    printf("Tempo de parede: %.6f s\n", decorrido);
    return 0;
}

Boas práticas

Ao trabalhar com tempo, prefira strftime() para formatação localizada, evite asctime() por não ser thread-safe, e use clock_gettime() ou timespec_get() para medições precisas. Sempre verifique o retorno de funções que podem falhar, como strftime() (retorna 0 se o buffer for pequeno).

Referências

Exercícios

  1. Escreva um programa que imprima o tempo atual em segundos desde a época e em formato legível (dd/mm/aaaa hh:mm:ss).

    ✓ Resposta:
    #include <stdio.h>
    #include <time.h>
    
    int main() {
        time_t t = time(NULL);
        printf("Época: %ld\n", (long)t);
        struct tm *tm = localtime(&t);
        char buf[64];
        strftime(buf, sizeof(buf), "%d/%m/%Y %H:%M:%S", tm);
        printf("Data: %s\n", buf);
        return 0;
    }
  2. Use clock() para medir o tempo de CPU de um loop que realiza 1 milhão de iterações vazias. Imprima o tempo em segundos.

    ✓ Resposta:
    #include <stdio.h>
    #include <time.h>
    
    int main() {
        clock_t start = clock();
        for (int i = 0; i < 1000000; i++);
        clock_t end = clock();
        printf("Tempo de CPU: %f s\n", (double)(end - start) / CLOCKS_PER_SEC);
        return 0;
    }
  3. Implemente uma função que receba um time_t e retorne uma string formatada como "YYYY-MM-DD HH:MM:SS" (use gmtime para UTC).

    ✓ Resposta:
    #include <stdio.h>
    #include <time.h>
    
    void formatar(time_t t, char *buf, size_t tam) {
        struct tm *tm = gmtime(&t);
        strftime(buf, tam, "%Y-%m-%d %H:%M:%S", tm);
    }
    
    int main() {
        time_t agora = time(NULL);
        char buf[32];
        formatar(agora, buf, sizeof(buf));
        printf("%s\n", buf);
        return 0;
    }
  4. Compare o tempo de parede (usando time()) com o tempo de CPU (usando clock()) para uma operação que dorme 1 segundo (sleep(1)). Explique a diferença.

    ✓ Resposta:
    #include <stdio.h>
    #include <time.h>
    #include <unistd.h> // para sleep
    
    int main() {
        time_t t1 = time(NULL);
        clock_t c1 = clock();
        sleep(1);
        time_t t2 = time(NULL);
        clock_t c2 = clock();
        printf("Tempo de parede: %ld s\n", (long)(t2 - t1));
        printf("Tempo de CPU: %f s\n", (double)(c2 - c1) / CLOCKS_PER_SEC);
        return 0;
    }

    A diferença ocorre porque sleep() não consome CPU; o processo fica bloqueado, então o tempo de CPU é próximo de zero, enquanto o tempo de parede é de 1 segundo.

  5. Use timespec_get() para medir o tempo de uma função que calcula a soma de 1 a 1000000 e imprima o resultado com 9 casas decimais.

    ✓ Resposta:
    #include <stdio.h>
    #include <time.h>
    
    int main() {
        struct timespec inicio, fim;
        timespec_get(&inicio, TIME_UTC);
        long soma = 0;
        for (long i = 1; i <= 1000000; i++) soma += i;
        timespec_get(&fim, TIME_UTC);
        double tempo = (fim.tv_sec - inicio.tv_sec) + (fim.tv_nsec - inicio.tv_nsec) / 1e9;
        printf("Soma: %ld\n", soma);
        printf("Tempo: %.9f s\n", tempo);
        return 0;
    }