Tenho ano e mês no formato YYMM
e quero convertê-los para YYYY Month
o formato. O ano é sempre 2000 e posteriores. Então, por exemplo;
1908
torna-se2019 Aug
1904
torna-se2019 Apr
2012
torna-se2020 Dec
2111
torna-se2021 Nov
Eu escrevi um script mostrado abaixo:
rand=(1908 1904 2012 2001 2111)
months=(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
for i in $rand
do
echo ${i:2:2}
if [ ${i:2:2} -lt 1 ] || [ ${i:2:2} -gt 12 ]
then echo "Month: ${i:2:2}. Last 2 values must be a month between 01 and 12."
fi
printf "20${i:0:2} ${months[${i:2:2}]}"
done
Mas recebo alguns erros.
line 13: 08: value too great for base (error token is "08")
Acho que os meses de um dígito com zero preenchido são um problema em alguns lugares, mas funcionam em outros.
Não sei como fazer o Bash entender que isso 08
é justo 8
. Eu tentei mudar ${i:2:2}
para $((i:2:2))
. Mas isso não funciona.
line 7: i:2:2: syntax error in expression (error token is ":2:2")
Responder1
08
é interpretado como um número octal inválido. É octal porque começa com zero, é inválido porque contém 8
. Isso acontece porque você está usando essa substring como um número em um contexto aritmético (uma comparação de números inteiros). Você teria o mesmo problema, 09
mas não com 07
números inferiores.
O script a seguir faz o cálculo usando a data 2000-01-01 mais o YY
bit do número como um ano. Em seguida, ele adiciona o restante da string como um número de meses e subtrai um dia. Este cálculo chega ao último dia do mês a que o YYMM
código se refere. Com %Y %b
, o ano e o mês são exibidos no YYYY Mon
formato.
#!/bin/bash
datecodes=(1908 1904 2012 2001 2111)
for datecode in "${datecodes[@]}"; do
printf -v thedate '2000-01-01 +%s years +%s months -1 day' "${datecode:0:2}" "${datecode:2}"
LC_ALL=C date -d "$thedate" +'%Y %b'
done
Saída:
2019 Aug
2019 Apr
2020 Dec
2020 Jan
2021 Nov
Código um pouco mais eficiente que chama o GNU apenas date
uma vez:
#!/bin/bash
datecodes=(1908 1904 2012 2001 2111)
for datecode in "${datecodes[@]}"; do
printf '2000-01-01 +%s years +%s months -1 day\n' "${datecode:0:2}" "${datecode:2}"
done | LC_ALL=C date -f - +'%Y %b'
Observe que este código permite que você escreva, por exemplo, 2013 Apr
como o código 1040
.
Para obter os nomes dos meses abreviados formatados de acordo com o código do idioma atual, e não de acordo com o código do idioma POSIX, remova LC_ALL=C
na frente das chamadas para date
.
Trabalhando sem GNU date
, usando a mesma abordagem que você, com validação da parte do mês do código de data:
#!/bin/bash
datecodes=(1908 1904 2012 2001 2111 1040)
declare -A months
months=(
[01]=Jan [02]=Feb [03]=Mar
[04]=Apr [05]=May [06]=Jun
[07]=Jul [08]=Aug [09]=Sep
[10]=Oct [11]=Nov [12]=Dec
)
for datecode in "${datecodes[@]}"; do
YY=${datecode:0:2}
MM=${datecode:2}
if [ -z "${months[$MM:-empty]}" ]; then
printf '"%s" is an invalid month\n' "$MM" >&2
continue
fi
printf '20%s %s\n' "$YY" "${months[$MM]}"
done
Isso usa the months
como uma matriz associativa.
Responder2
Feito pelo método abaixo Adicionada data na entrada mencionada para obter a saída desejada
aqui eu adicionei a data "01" à entrada mencionada para estruturar em uma entrada válida
for i in 1908 1904 2012 2111; do date +%Y" "%b -d $i"01"; done
saída
2019 Aug
2019 Apr
2020 Dec
2021 Nov