Преобразовать год месяц в другой формат

Преобразовать год месяц в другой формат

У меня есть год и месяц в формате YYMM, и я хочу преобразовать их в YYYY Monthформат. Год всегда 2000 и позже. Так, например;

  • 1908становится2019 Aug
  • 1904становится2019 Apr
  • 2012становится2020 Dec
  • 2111становится2021 Nov

Я написал сценарий, показанный ниже:

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

Но у меня возникают некоторые ошибки.

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

Я думаю, что однозначные обозначения месяцев с дополнениями в виде нулей в некоторых местах являются проблемой, но в других местах работают.

Не уверен, как заставить Bash понять, что 08это просто 8. Я пробовал изменить ${i:2:2}на $((i:2:2)). Но это не работает.

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

решение1

08интерпретируется как недопустимое восьмеричное число. Оно восьмеричное, так как начинается с нуля, недопустимое, так как содержит 8. Это происходит, потому что вы используете эту подстроку как число в арифметическом контексте (сравнение целых чисел). У вас будет та же проблема с , 09но не с 07или меньшими числами.

Следующий скрипт выполняет расчет, используя дату 2000-01-01 плюс бит YYчисла как год. Затем он добавляет остаток строки как некоторое количество месяцев и вычитает один день. Этот расчет попадает на последний день месяца, на который YYMMссылается код. С помощью %Y %bгод и месяц выводятся в YYYY Monформате.

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

Выход:

2019 Aug
2019 Apr
2020 Dec
2020 Jan
2021 Nov

Чуть более эффективный код, который вызывает GNU только dateодин раз:

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

Обратите внимание, что этот код позволяет вам писать, например, 2013 Aprкак код 1040.

Чтобы получить сокращенные названия месяцев, отформатированные в соответствии с текущей локалью, а не в соответствии с локалью POSIX, удалите LC_ALL=Cперед вызовами date.


Работаем без GNU date, используя тот же подход, что и вы, с проверкой части месяца в коде даты:

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

Здесь используется monthsассоциативный массив.

решение2

Выполнено следующим методом. Добавлена ​​дата в указанные входные данные для получения желаемого результата.

здесь я добавил дату «01» к упомянутому вводу для структурирования в допустимый ввод

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

выход

2019 Aug
2019 Apr
2020 Dec
2021 Nov

Связанный контент