Convertir año mes a formato diferente

Convertir año mes a formato diferente

Tengo año y mes en el formato YYMMy quiero convertirlos al YYYY Monthformato. El año es siempre 2000 y posteriores. Así por ejemplo;

  • 1908se convierte2019 Aug
  • 1904se convierte2019 Apr
  • 2012se convierte2020 Dec
  • 2111se convierte2021 Nov

He escrito un script que se muestra a continuación:

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

Pero recibo algunos errores.

line 13: 08: value too great for base (error token is "08")

Creo que los meses de un solo dígito con ceros rellenos son un problema en algunos lugares, pero funcionan en otros.

No estoy seguro de cómo hacer que Bash entienda que 08es solo 8. Intenté cambiar ${i:2:2}a $((i:2:2)). Pero eso no funciona.

line 7: i:2:2: syntax error in expression (error token is ":2:2")

Respuesta1

08se interpreta como un número octal no válido. Es octal ya que comienza con cero, no es válido porque contiene 8. Lo hace porque estás usando esta subcadena como un número en un contexto aritmético (una comparación de enteros). Tendría el mismo problema con números más bajos 09, pero no con 07ellos.

El siguiente script realiza el cálculo utilizando la fecha 2000-01-01 más el YYbit del número como año. Luego suma el resto de la cadena como una cantidad determinada de meses y resta un día. Este cálculo llega al último día del mes al que YYMMhace referencia el código. Con %Y %b, se muestran el año y el mes en 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

Producción:

2019 Aug
2019 Apr
2020 Dec
2020 Jan
2021 Nov

Código ligeramente más eficiente que sólo llama a GNU dateuna 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'

Tenga en cuenta que este código le permite escribir, por ejemplo, 2013 Aprcomo código 1040.

Para obtener los nombres de los meses abreviados formateados según la configuración regional actual y no según la configuración regional POSIX, elimine LC_ALL=Cdelante de las llamadas a date.


Trabajando sin GNU date, usando el mismo enfoque que usted, con la validación de la parte del mes del código de fecha:

#!/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

Esto utiliza monthscomo una matriz asociativa.

Respuesta2

Realizado mediante el método siguiente. Se agregó la fecha en la entrada mencionada para obtener el resultado deseado.

aquí he agregado la fecha "01" a la entrada mencionada para estructurarla en una entrada válida

for i in 1908 1904 2012 2111; do date +%Y" "%b -d $i"01"; done

producción

2019 Aug
2019 Apr
2020 Dec
2021 Nov

información relacionada