1

I have year and month in the format YYMM and I want to convert them to YYYY Month format. The year is always 2000 and later. So for example;

1908 becomes 2019 Aug
1904 becomes 2019 Apr
2012 becomes 2020 Dec
2111 becomes 2021 Nov

I have written up a script shown below:

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

But, I get some errors.

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

I think the single digit months with padded zero is an issue in some places but works in other.

Not sure how to make bash understand that 08 is just 8. I tried to change ${i:2:2} to $((i:2:2)). But, that doesn't work.

line 7: i:2:2: syntax error in expression (error token is ":2:2")
1
  • That looks more like zsh syntax than bash syntax. zsh has a builtin interface to strptime() and strftime() though (the strftime builtin in the zsh/datetime module), so you wouldn't need to do it by hand like that. – Stéphane Chazelas May 25 at 12:06
4

08 is interpreted as an invalid octal number. It's octal since it starts with zero, it's invalid because it contains 8. It does this because you're using this substring as a number in an arithmetic context (an integer comparison). You would have the same issue with 09 but not with 07 or lower numbers.

The following script does the calculation using the date 2000-01-01 plus the YY bit of the number as a year. It then adds the rest of the string as some number of months, and subtracts one day. This calculation lands on the last day of the month that the YYMM code refers to. With %Y %b, the year and month is outputted in YYYY Mon format.

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

Output:

2019 Aug
2019 Apr
2020 Dec
2020 Jan
2021 Nov

Slightly more efficient code which only calls GNU date once:

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

Note that this code allows you to write e.g. 2013 Apr as the code 1040.

To get the names of the abbreviated months formatted according te the current locale, and not according to the POSIX locale, remove LC_ALL=C in front of the calls to date.


Working without GNU date, using the same approach as you do, with validation of the month part of the date code:

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

This uses the months as an associative array.

2
  • busybox date has a more useful API than GNU date for this kind of thing: date -D%y%m -d 1908 +'%Y %b' – Stéphane Chazelas May 25 at 12:07
  • 1
    Note that with bash and your last code, you'd get a months: bad array subscript error if $MM is empty. You may want to replace ${months[$MM]} with ${months[${MM:-empty}]} (or switch to a better shell ;-). – Stéphane Chazelas May 25 at 12:47
2

Done by below method Added date in the mentioned input to get desired output

here i have added date "01" to input mentioned to structure into valid input

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

output

2019 Aug
2019 Apr
2020 Dec
2021 Nov

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.