C++ Básico Aula 15: Datas

 Imagem meramente ilustrativa

Recapitulando

    Na aula passada conhecemos a biblioteca ctime que oferece funcionalidades para trabalhar com data e hora. Dessa biblioteca vimos exemplos de uso de dados do tipo time_t que são chamados de timestamp, através de exemplos com a função time() que retorna o timestamp da data atual e a função  ctime() que recebe com parâmetro uma timestamp e retorna uma string com um formato de data e hora internacional.

Estruturas de data/hora

    Acontece que time_t não é o único tipo de dado em C++ que armazena datas. Existe também o tipo struct tm para estruturas de data/hora. Sua vantagem em relação ao timestamp é que ele armazena a data "quebrada" em vários componentes como dia, mês, ano, etc.

Membros do struct tm

    Essas partes da data nós chamamos de membros do struct tm e podem ser usadas para "montar" a data completa quando precisamos. Lembrando que elas são números inteiros e devem ser informadas dentro de uma faixa válida, por exemplo, de 0 a 59 para minutos. 

    Estes são os principais membros do struct tm:

  • tm_sec: para segundos (de 0 a 59)
  • tm_min: para minutos (de 0 a 59)
  • tm_hour: para a hora (de 0 a 23)
  • tm_mday: para o dia do mês
  • tm_mon: para o mês (de 0 a 11, sendo 0 o janeiro)
  • tm_year: para o ano (a partir de 1900)
  • tm_wday: para o dia da semana (de 0 a 6, sendo 0 o domingo)
  • tm_yday: para o dia do ano (de 0 a 365, sendo 0 o 1º de janeiro)

    Temos ainda a tm_isdst que é usada para tratar do horário de verão sendo zero quando o horário não está em vigor e um valor positivo quando está valendo.

Montando uma data

    Para montar uma data completa com os membros do struct tm é necessário usar a função mktime() que recebe como parâmetro a referência um dado do tipo struct tm. Esse dado é uma variável, mas é criado como se fosse um objeto instanciado a partir do tipo struct tm, como se ele fosse uma classe.

    Vejamos um exemplo de uso para ilustrar. Digamos que eu preciso montar a data 04 de Janeiro de 2006 às 22:30 hs. Esse poderia ser o nosso código:

  1. #include <iostream>
  2. #include <ctime>  
  3. using namespace std;
  4.  
  5. int main () {
  6.   struct tm  nascimento;
  7.   int dia, mes, ano, horas, minutos, segundos;
  8.   ano = 2006;
  9.   mes = 1;
  10.   dia=4;
  11.   horas=22;
  12.   minutos=30;
  13.   segundos=0;
  14.   nascimento.tm_year = ano - 1900; 
  15.   nascimento.tm_mon = mes - 1; 
  16.   nascimento.tm_mday = dia;
  17.   nascimento.tm_hour = horas; 
  18.   nascimento.tm_min = minutos; 
  19.   nascimento.tm_sec = segundos;
  20.   time_t data=mktime(&nascimento);
  21.   cout <<ctime(&data)<<"\n";
  22.   cout<<dia<<"/"<<mes<<"/"<<ano<<"\n";
  23.   cout<<horas<<":"<<minutos<<"\n";
  24.   string dias[] = {"Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"};
  25.   cout<<"Essa data caiu em um(a): "<< dias[nascimento.tm_wday]<<"\n";
  26.   return 0;
  27. }

    Repare que na linha 6 desse exemplo eu estou criando um dado do tipo struct tm que eu chamei de nascimento. Nas linhas subsequentes eu estou criando variáveis inteiro para usar posteriormente em uma data formatada simplificada.

    Das linhas 14 a 19 eu estou atribuindo a cada membro desse struct um valor para compor a data que eu preciso. Veja que sintaxe usada na atribuição é parecida com a de atributos de classe. Para converter o ano é necessário subtrair 1900 que é o ponto de partida. Da mesma forma preciso descontar 1 do mês porque ele começa a ser contado do zero.

    Note que eu estou criando na linha 13 um dado do tipo time_t  que chamei de data e estou atribuindo a ele a data montada pelo mktime() com os membros individuais da minha struct tm nascimento. Veja que como usamos ctime() para formatar a timestamp e ela saiu no formato internacional. 

    Como alternativa eu mostrei na tela as variáveis inteiros como se fossem uma data nas linhas 22 e 23, mas essa não é forma correta de se formatar um dado do tipo data.

    Você deve estar se perguntando, porque então eu devo complicar tudo transformando em um dado do tipo data, se posso simplificar juntando variáveis para dia, mês e ano? A resposta é que para fazer cálculos com datas você precisa de um dado do tipo data!

    Veja que na linha 24 eu criei uma matriz com os dias da semana e na linha 25 eu estou pegando o elemento da matriz que corresponde ao tm_wday, isto é, que corresponde a um número de 0 a 6 que representa o dia da semana. Isso só é possível porque nascimento é um dado do tipo data.

Usando strftime(), setenv() e localtime() para formatar uma data

    A função strftime() grava uma string de data e hora fornecida por um dado struct tm em um array do tipo char, permitindo a formatação da data através do uso de especificadores de formato. Entre os principais especificadores temos:

  • d: para dia em dois dígitos
  • m: para mês em dois dígitos
  • y: para ano em dois dígitos
  • Y: para ano em quatro dígitos
  • M: para minutos
  • H: para horas
  • S: para segundos

    Já a função localtime() converte um dado timestamp em um dado struct tm já ajustando para o fuso horário local. No entanto, para que esse ajuste funcione é necessário configurar uma variável de ambiente do sistema para o fuso do local. Isso pode ser feito com a função setenv().

    Vejamos um exemplo pegando a data e hora atual e formatando com strftime():

  1. #include <iostream>
  2. #include <ctime>
  3. using namespace std;

  4. int main() {
  5.   time_t data_t;
  6.   char matriz[20];
  7.   struct tm * data_s;
  8.   setenv("TZ", "America/Sao_Paulo", 1);
  9.   time(&data_t);
  10.   data_s = localtime(&data_t);
  11.   strftime(matriz, 20, "%d/%m/%Y %H:%M:%S"data_s);
  12.   cout << matriz << "\n";
  13.   return 0;
  14. }

    Nesse exemplo eu criei na linha 6 um timestamp que chamei de data_t, depois criei uma matriz de 20 elementos para caber a minha data no formato abreviado (dd/mm/aaaa hh:mm:ss). Em seguida na linha 8 eu criei uma struct tm chamada data_s.

    Depois na linha 9 a 11 eu ajustei a variável de ambiente para o fuso horário local com setenv(), peguei a timestamp e converti para uma struct tm já ajustando o fuso com o localtime(). Dai foi só gravar a matriz com o strftime() no formato desejado (linha 12).

    Cabe esclarecer que eu deixei a matriz em um tamanho pequeno só pra caber o que precisávamos, mas os exemplos que encontramos pela Web falam em um matriz de tamanho 50, talvez para caber toda uma data completa, com meses e dias por extenso.

    Nesse caso usaríamos os especificadores de formato %A para dias e %B para meses, no entanto o resultado seria em inglês. As letras em minúsculo dariam o formado abreviado, como Fri, no lugar de Friday. %c daria uma saída igual da da função ctime().

    Na próxima aula trabalharemos com arquivos.


Próxima Aula                                                                                                                        Aula Anterior

Referências

DEITEL, H. M. DEITEL, P. J. C++: Como programar. Pearson Education do Brasil. 2006.

GBDONLINE. Learn C++ Programming. Disponível em: <https://learn.onlinegdb.com/c%2B%2B_for_beginners> Acesso em 05 ago. 2024.

W3SCHOOLS. C++ Tutorial. Disponível em: <https://www.w3schools.com/cpp/default.asp> Acesso em 05 ago. 2024.