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.
zsh
syntax thanbash
syntax.zsh
has a builtin interface tostrptime()
andstrftime()
though (thestrftime
builtin in thezsh/datetime
module), so you wouldn't need to do it by hand like that.