Aritmética de datas é o processo de realizar contas utilizando datas e intervalos de tempo.
O fator mais complicante nestes tipos de operações é que data não é algo tão exato quanto se imagina. Tudo por conta que a Terra leva 365,2422 dias para completar uma volta inteira no Sol. Para entender com detalhes, leia esta explicação.
Em termos computacionais, uma data pode ser considerada um conjunto de componentes (dia, mês, ano, hora, minuto, segundo, fuso horário, etc), ou, se observarmos a essência da data, podemos considerá-la como um "instante único" (na Terra). Na prática, trabalhar com as componentes de maneira individual para realizar contas com datas é complicado. Para facilitar, foi criada a famosa "Era Unix", que é uma forma de representar um timestamp através do número de segundos decorrentes desde 01/01/1970. Para realizar operações, portanto, todas as componentes são convertidas em segundo e depois é obtido o valor das componentes desejadas (dia, mês, etc) através de um banco de informações.
Entretanto, o tipo de dado utilizado para guardar o timestamp é o inteiro, que na arquitetura de computadores de 32 bits, consegue armazenar valores até o ano de 2038. Para a arquitetura de 64 bits isso se resolveria por um prazo bastante razoável.
Funções para Manipulação de Data
Antes de PHP 5.2, a forma disponível para se realizar aritmética de datas era através de algumas funções que trabalhavam com timestamp. As principais delas são:
-
Geração de Timestamp
- mktime - Para criar um timestamp a partir de componentes de data.
- time - Para obter o timestamp atual.
-
Leitura (parse) de uma data
- strptime - Para obter as componentes de uma data formatada com strftime (string parse time).
- strtotime - Para obter um timestamp de uma data em formato americano e, opcionalmente, fazer contas com a data base.
- Funções de string como preg_match, explode, strpos e substr.
-
Escrita (formatação) de uma data
- strftime - Para formatar um timestamp (string from time).
- date - Para formatar um timestamp (com lista de formatos diferentes de strftime).
-
Validação de Datas
- checkdate - Para checar se uma data existe.
A aritmética de datas está escondida em algumas funções como mktime e strtotime.
Primeiramente, mktime devolve sempre um timestamp. Se pedirmos o timestamp do dia 32 de janeiro, a função irá devolver, na verdade, o timestamp do dia primeiro de fevereiro (afinal, janeiro tem 31 dias). Por outro lado, se pedirmos o dia zero de janeiro, será devolvido o último dia de dezembro do ano anterior.
// Criando o timestamp (hora, minuto, segundo, mês, dia, ano) $time = mktime(0, 0, 0, 1, 32, 2010); // Formatando o timestamp calculado (01/02/2010 00:00:00) echo strftime('%d/%m/%Y %H:%M:%S', $time);
Se temos a uma data no formato dd/mm/aaaa, como somar 40 dias?
$data = '01/01/2010'; // Obter as componentes de data com strptime $componentes = strptime($data, '%d/%m/%Y'); $dia = $componentes['tm_mday']; $mes = $componentes['tm_mon'] + 1; $ano = $componentes['tm_year'] + 1900; // Ou obter as componentes com preg_match preg_match('/^(\d+)\/(\d+)\/(\d+)$/', $data, $matches); list($data, $dia, $mes, $ano) = $matches; // Realizar a soma de 40 dias $time = mktime(0, 0, 0, $mes, $dia + 40, $ano); // Formatar a data obtida echo strftime('%d/%m/%Y', $time); // 10/02/2010
Atenção: somar 40 dias é diferente de somar 1 mês e 10 dias. Afinal, "1 mês" não é algo exato, já que cada mês possui uma quantidade diferente de dias.
A função strtotime possui algumas utilidades extras, como, por exemplo, somar semanas ou obter o último sabado a partir de um timestamp:
// Um timestamp qualquer $time1 = mktime(0, 0, 0, 1, 1, 2010); // Somar uma semana e dois dias $time2 = strtotime('+1 week 2 day', $time1); // Formatar a data obtida echo strftime('%d/%m/%Y', $time2); // Obter ultimo sabado a partir de $time1 $time3 = strtotime('last saturday', $time1); // Formatar a data obtida echo strftime('%d/%m/%Y', $time3);
A comparação de duas datas é feita por um processo semelhante: primeiro duas datas são convertidas para timestamp, depois os dois timestamps são comparados normalmente (comparação de inteiros). Para comparar componentes (checar se duas datas estão no mesmo mês, por exemplo), precisa primeiro converter para timestamp, obter a componente desejada e comparar a componente.
As classes DateTime, DataInterval, DatePeriod e DateTimeZone
As classes DateTime, DataInterval, DateTimeZone e DatePeriod são formas mais atuais de se trabalhar com datas utilizando o paradigma Orientado a Objetos.
Os principais métodos são:
-
DateTime
- __construct - Criar um objeto a partir de uma data no formato americano ou data vazia.
- createFromFormat - Criar um objeto a partir de uma data formata (mesmo formato aceito na função date).
- diff - Obtém o intervalo de diferença entre duas datas (representado por um DateInterval).
- format - Formata uma data com os mesmos elementos da função date.
- getTimestamp - Obtém o timestamp (para compatibilidade com as funções antigas).
- modify - Realiza operações, assim como strtotime.
- setDate, setTime, setTimestamp, setTimezone - Modifica a data, hora, timestamp ou TimeZone do objeto.
- add e sub - Adiciona ou subtrai um intervalo de tempo através de um DateInterval.
-
DateInterval
- __construct - Construtor do intervalo.
- format - Formata o intervalo.
Vamos tentar fazer os mesmos exemplos feitos com funções:
$data = '01/01/2010'; // Criar o objeto representando a data $obj_data = DateTime::createFromFormat('d/m/Y', $data); // Definir a hora, minuto e segundo, que não foram informados // (caso contrário, é obtido os valores da hora atual) $obj_data->setTime(0, 0, 0); // Realizar a soma de 40 dias $obj_data->modify('+40 days'); // Formatar a data obtida echo $obj_data->format('d/m/Y');
Bem mais simples, não?
Agora vamos fazer o mesmo exemplo, mas utilizando a classe DateInterval ao invés do método modify:
$data = '01/01/2010'; // Criar o objeto representando a data $obj_data = DateTime::createFromFormat('d/m/Y', $data); $obj_data->setTime(0, 0, 0); // Realizar a soma de 40 dias $intervalo = new DateInterval('P40D'); $obj_data->add($intervalo); // Formatar a data obtida echo $obj_data->format('d/m/Y');
Para criar um intervalo, é utilizada uma string que começa com a letra P, seguida por números inteiros acompanhados de sua respectiva unidade (Y para anos, M para meses, D para dias e W para semanas). Caso o intervalo especifique horas, deve ser seguido pela letra T, que é seguido por números inteiros acompanhados de sua respectiva unidade (H para horas, M para minutos e S para segundos).
Exemplos de strings para intervalos (note em vermelho a porção de data e em azul a porção de tempo):
- P1Y - Um ano
- P2Y3D - Dois anos e três dias
- P3MT4M - Três meses e quatro minutos
- PT4S - Quatro segundos
- P1Y2M3DT4H5M6S - Um ano, dois meses, três dias, quatro horas, cinco minutos e seis segundos
Observação: de maneira análoga a mktime, as funções setDate e setTime também trabalham com aritmética de datas. Veja o exemplo:
$data = '01/01/2010'; // Criar o objeto representando a data $obj_data = DateTime::createFromFormat('d/m/Y', $data); $obj_data->setTime(0, 0, 0); // Realizar a soma de 40 dias $dia = $obj_data->format('d'); $mes = $obj_data->format('m'); $ano = $obj_data->format('Y'); $obj_data->setDate($ano, $mes, $dia + 40); // Formatar a data obtida echo $obj_data->format('d/m/Y H:i:s');
Porém, esta última forma não é muito indicada, já que existem formas mais adequadas.
Desde o PHP 5.2.2, é possível utilizar os operadores de comparação (==, !=, <, <=, >, >=) para comparar duas datas. Antes dessa versão, a forma mais útil de comparar duas datas era usando o método getTimestamp e comparar dois inteiros. Outra forma de se obter uma comparação é fazendo a diferença (método diff) entre duas datas, que retorna um intervalo.
A classe DateTimeZone é utilizada para representar o fuso horário de um DateTime. Já a classe DatePeriod é apenas uma forma conveniente de representar um período (data de início e data de término, ou data de início mais um intervalo). DataPeriod é útil para ser usado em estruturas foreach, já que é "atravessável".
5 comentários
como utilizar uma variável ao invés de numero 40 em $intervalo = new DateInterval('P40D');
Anônimo, pela sua pergunta, você precisa entender como funcionam as strings no PHP.
Você pode usar aspas duplas ou usar a função sprintf:
sprintf('P%dD', $dias)
Veja esses links:
http://php.net/manual/en/language.types.string.php
http://php.net/manual/en/function.sprintf.php
Grato pelo retorno, mas não entendi. Eu queria colocar uma variavel nessa linha $intervalo = new DateInterval('P$VARIAVELD'); Não está dando certo nem com aspas duplas
Anônimo, como existe uma letra depois do nome da variável, você precisa delimitá-la com chaves assim:
"P{$variavel}D", caso contrário, o PHP iria procurar pela variável chamada $variavelD, que não existe.
Rubens Takiguti Ribeiro, muito obrigado por responder e solucionar a minha questão.
Postar um comentário
Nota: fique a vontade para expressar o que achou deste artigo ou do blog.
Dica: para acompanhar as respostas, acesse com uma conta do Google e marque a opção "Notifique-me".
Atenção: o blogger não permite inclusão de tags nos comentários, por isso, use algum site externo para postar seu código com dúvidas e deixe o link aqui. Exemplo: pastebin.com