Converter ano mês para formato diferente

Converter ano mês para formato diferente

Tenho ano e mês no formato YYMMe quero convertê-los para YYYY Montho formato. O ano é sempre 2000 e posteriores. Então, por exemplo;

  • 1908torna-se2019 Aug
  • 1904torna-se2019 Apr
  • 2012torna-se2020 Dec
  • 2111torna-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, 09mas não com 07números inferiores.

O script a seguir faz o cálculo usando a data 2000-01-01 mais o YYbit 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 YYMMcódigo se refere. Com %Y %b, o ano e o mês são exibidos no YYYY Monformato.

#!/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 dateuma 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 Aprcomo 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=Cna 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 monthscomo 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

informação relacionada