O comando date permite exibir e alterar a data/hora do sistema em diferentes formatos, e até mesmo efetuar cálculos de tempo. Veja alguns parâmetros que podem ser utilizados para formatação de datas:
- %Y : ano
- %y : os dois últimos dígitos do ano
- %j : dia ano (1 a 366)
- %m : mês (1 a 12)
- %A : dia da semana (domingo,…, sábado)
- %a : dia da semana abreviado (dom,…, sab)
- %w : dia da semana, onde 0 = domingo, 1 = segunda,…, 6 = sábado
- %B : nome do mês (janeiro,…, dezembro)
- %b : nome do mês abreviado (jan,…, dez)
- %c : dia da semana, data e hora
- %d : dia do mês (00-31)
- %T : hora no formato hh:mm:ss
- %H : hora do dia (0 a 23)
- %M : minuto (0 a 59)
- %S : segundos (0 a 61)
- %s : número de segundos desde das zero horas de 01/01/1970
Veja esse exemplo que converte dia do ano (vide tabela mais acima) em data “ANO-MÊS-DIA” (inclusive considerando os anos bissextos):
date -d "$(($dia_do_ano-1)) days $ano-01-01" +"%Y-%m-%d"
Dica: o recurso ‘2016-03-31 +1 month’ pode resultar na data ‘2016-05-01’, porque ‘2016-04-31’ é uma data inválida. Para determinar o mês seguinte de forma mais confiável, pode-se pedir para o mês do dia 15 do mês seguinte ao atual, como no exemplo abaixo.
date +"%B %Y" --date="$(date "+%Y-%m-15") next month"
Caso apareça um erro do tipo “date: data inválida” (principalmente após começar um período de horário de verão), experimente colocar um horário qualquer depois da data, ainda dentro das aspas duplas (” 12:00″, por exemplo).
Para mais opções do comando date e para ver como acertar data, hora e fuso horário, clique nos respectivos links.
Convertendo uma data escrita em hexadecimal para calendário “normal”
O número de segundos do início da data UNIX (iniciada à meia noite de 1 de janeiro de 1970) até a data GPS (iniciada à meia noite de 6 de janeiro de 1980) pode ser obtido através do seguinte comando:
date -u -d "1980-01-06 00:00:00" +%s
Para ver mais detalhes sobre os conceitos de Era Linux, GPS time e leap secondes, veja o post Formas alternativas de medir o tempo. Segue um comando para converter hexadecimal (3FC29A80, por exemplo) em “time stamp” no formato YYYY-MM-DD HH:MM:SS no sistema GPS (ou seja, descontando 16 leap seconds):
date -u --date @$((`printf '%d\n' 0x3FC29A80`+315964800-16)) '+%Y-%m-%d %T'
O parâmetro ‘-u’ converte para UTC e o “@” serve para indicar que é uma time stamp. As operações matemáticas de adição e subtração devem ser realizadas dentro de “$((a+b-c))”.
O comando printf imprime na tela a mensagem, substituindo caracteres de controles, sendo que o %d captura somente números inteiros, %f os números com casas decimais flutuantes e %s strings de caracteres. Veja mais parâmetros do printf clicando no link. O formato %X (%x para letras minúsculas) serve para número hexadecimal.
O sistema de numeração hexadecimal representa os números em base 16, ou seja, em ordem crescente os elementos são “0,1,2,3,4,5,6,7,8,9A,B,C,D,E,F”. É muito utilizado em informática, pois os computadores costumam utilizar o byte ou octeto (pois representa 28 valores possíveis) como unidade básica da memória.
Exemplo de script manipulando datas
Segue um script que utiliza os comandos vistos para criar diretórios com a data dos arquivos obtidas a partir do nome dos mesmos. Os nomes dos arquivos foram gravados a partir da informação de tempo de um GPS (ou seja, a hora está correta mas não está em UTC, e sim em GPS time) em hexadecimal. Cabe ao comando date converter para uma data UTC (conforme visto), gravando o resultado em uma string que será dividida em diferentes campos (hora, mês, etc) para formar os nomes dos respectivos diretórios para os quais os arquivos serão movidos.
No exemplo, os diretórios estão divididos de modo a receber os dados dos últimos 15 minutos. Assim, ao criar o nome de um diretório não devem surgir nomes contendo horas como “24” nem dias como “32”, daí que aparecem as exceções no último “elif” – destaque para o modo que foi calculado o “último dia do mês” utilizando o comando cal.
#!/bin/bash # Script para converter um valor hexadecimal em time stamp function coloca_zero(){ a=$1 b=`echo "$a" | wc -L | awk '{print $1}'` while [ $b != 2 ]; do b=$((b+1)) a=0$a done echo $a } for file in *.gz; do hexa=`echo "${file%%.*}"` hexa_to_sec=`printf '%d\n' 0x$hexa` # Agrupar arquivos entre 00-14 | 15-29 | 30-44 | 45-59 date_str=`date -u --date @$(($hexa_to_sec+315964800-16)) '+%Y:%m:%d:%T'` min=`echo $date_str | awk -F":" '{print $5}'` hour=`echo $date_str | awk -F":" '{print $4}'` day=`echo $date_str | awk -F":" '{print $3}'` month=`echo $date_str | awk -F":" '{print $2}'` year=`echo $date_str | awk -F":" '{print $1}'` if [ $min -lt 15 ]; then min_new=15 elif [ $min -ge 15 -a $min -lt 30 ]; then min_new=30 elif [ $min -ge 30 -a $min -lt 45 ]; then min_new=45 elif [ $min -ge 45 ]; then min_new=00 hour=$((10#$hour+1)) hour=`coloca_zero "$hour"` if [ $hour -eq 24 ]; then hour=00 day=$((10#$day+1)) day=`coloca_zero "$day"` # Ultimo dia do mes last_day=`echo $(cal $month $year) | awk '{print $NF}'` last_day_err=$(($last_day+1)) if [ $day -eq $last_day_err ]; then day=01 month=$((10#$month+1)) month=`coloca_zero "$month"` if [ $month -eq 13 ]; then month=12 year=$(($year+1)) fi fi fi fi name_dir=$year-$month-${day}_$hour$min_new #echo $name_dir mkdir -p $name_dir mv $file $name_dir done
Os caracteres “10#” servem para forçar a leitura da variável como um decimal – no caso de ter um zero na frente do número (por exemplo “03”), o bash interpreta como hexadecimal. Uma data pode ser impressa sem o zero à esquerda usando um sinal de “menos” depois da porcentagem por exemplo ‘+%-d’ para imprimir o dia sem zero à esquerda caso tenha somente um dígito.
O script a seguir cria uma lista de n dias a partir de uma data inicial informada (considerando anos bissextos) – muito útil para servir de gabarito ao comparar uma lista de datas existente e descobrir se estão faltando dias e quais são eles:
data_inicial="01/01/2013" n_dias=400 for i in $(seq 0 $n_dias); do date -d "$data_inicial +$i day" +%d/%m/%Y done
Extra: comando gcal
O Gcal é um programa para calcular e imprimir calendários. Ele exibe folhas de calendário Juliano e gregoriano, por um mês, três meses ou um ano inteiro, assim como tem suporte para outros sistemas de calendário, por exemplo, os calendários chinês e japonês, o calendário hebraico e o calendário civil islâmico. Ele também exibe listas de feriados e datas fixas para muitos países ao redor do mundo, além de calcular vários dados astronômicos e os tempos do Sol e da Lua em qualquer lugar, com precisão suficiente para a maioria dos propósitos civis.
gcal --holiday-list --cc-holidays=br
O programa pode ser instalado através do comando “sudo apt-get install gcal”. O comando acima apresenta a lista de feriados (+) e algumas datas comemorativas (*) no Brasil para o ano atual, com a data, o dia da semana e o número de dias que já passaram ou ainda faltam para cada uma.