Calendários e o comando date

Calendário é um sistema para contagem e agrupamento de dias que visa atender, principalmente, às necessidades civis e religiosas de uma cultura. Um calendário lunar é sincronizado com o movimento da Lua; um exemplo disso é o calendário islâmico. Um calendário solar é sincronizado com o movimento do Sol; um exemplo é o calendário persa. Um calendário luni-solar é sincronizado com ambos os movimentos do Sol e da Lua; um exemplo é o calendário hebraico. Um calendário arbitrário não é sincronizado nem com o Sol nem com a Lua. Um exemplo disso é o calendário juliano usado por astrônomos e continua sendo utilizado pelos cristãos ortodoxos em vários países.

Calendário permanente

Calendário permanente (clique na imagem para vê-lo maior)

O calendário gregoriano é um calendário de origem europeia, utilizado oficialmente pela maioria dos países. Foi promulgado pelo Papa Gregório XIII em 24 de Fevereiro do ano 1582 pela bula Inter gravissimas em substituição do calendário juliano implantado pelo líder romano Júlio César em 46 a.C. - anteriormente, estava em vigor o calendário romano, estabelecido por Rômulo à época da criação de Roma, em 753 a.C., e tinha 304 dias distribuídos em 10 meses. A bula ditava que o dia imediato à quinta-feira, 4 de outubro, fosse sexta-feira, 15 de outubro, omitindo-se assim 10 dias.

O dia do ano é uma forma de colocar os dias em uma sequência constante para todo o ano - no caso de anos bissextos, adicionar um dia para os meses depois de fevereiro. Fonte: Robin Wilson

O dia do ano é uma forma de colocar os dias em uma sequência constante para todo o ano - no caso de anos bissextos, adicionar um dia para os meses depois de fevereiro. Fonte: Robin Wilson

O Tempo Universal Coordenado (UTC) foi oficialmente formalizado em 1960 e adotado por muitos países, sendo o sucessor do Tempo Médio de Greenwich (Greenwich Mean Time - GMT). Ao contrário do GMT, o UTC não se define pelo sol ou as estrelas, mas é sim uma medida derivada do Tempo Atômico Internacional (TAI) - escala de tempo calculada pelo Escritório Internacional de Pesos e Medidas (BIPM), na França, usando informações de cerca de duzentos relógios atômicos em mais de 50 laboratórios nacionais ao redor do mundo. Atualmente, o TAIS está 35 segundos a frente do UTC. Quanto aos fusos horários, saiba mais clicando no link.

A data GPS (Global Positioning System time) é a escala de tempo atômica implementadas pelos relógios atômicos nas estações de controle em terra e nos próprios satélites do sistema GPS. Foi iniciada à meia noite de 6 de janeiro de 1980, que é coincidente com a hora UTC. Desde 1980 até 2012, a hora GPS acumulou 16 segundos a mais com relação ao horário "normal" devido aos leap seconds.

O leap second (ou segundo bissexto) é um ajuste de um segundo para manter os padrões de contagem de tempo próximos ao tempo solar, devido oscilações da rotação da Terra. Os segundos bissextos são necessários para manter os padrões sincronizados com os calendários civis, cuja base é astronômica. Os padrões para o tempo civil estão baseados na Coordenada Universal de Tempo (UTC, Universal Time Coordinate), que é mantida por meio de relógios atômicos extremamente precisos.

O horário UNIX é definido como o número de segundos passados desde o início da Era UNIX, em 1 de janeiro de 1970. O número de segundos do início da data UNIX até a data GPS pode ser obtido através do site Epoch Covnerter (em UTC), que dá 315964800 segundos, ou através do seguinte comando:

date -u -d "1980-01-06 00:00:00" +%s

Existe também um conversor online entre data UTC e GPS disponível no link. Veja esse relógio online com diferentes horários.

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"

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"

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.

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
Compartilhe o link desse texto, mas se for copiar algum trecho, cite a fonte. Valorize nosso trabalho.
Mais informações na licença de uso do site.

5 Pingbacks/Trackbacks