In BASH shell scripting or using gdate, given a date like "Oct 2011" how do I convert to a year-month number format? Output should be "2011-10", for example.
7 Answers
mydate="Oct 2011"
date --date="$(printf "01 %s" $mydate)" +"%Y-%m"
The parse_datetime interface for GNU date (which is what the example uses) has lots of rules. the Oct 2011 form of the date isn't one of them, so you prepend a "01 " to the front of it and date likes it.

- 16,005
- 2
- 34
- 51
-
1This didn't work for me, I had to put quotes around `$mydate` such that the second line reads `date --date="$(printf "01 %s" "$mydate")" +"%Y-%m"` as otherwise `Oct 2011` becomes `Oct2011` – gene_wood Sep 27 '17 at 17:02
-
simple question to make sure I'm not missing anything but, in your example, what's the advantage of using `printf` ? I'v tried as `date --date="01 $mydate" +"%Y-%m"` which seems to yield the same result but maybe I'm oversimplifying just to run into some corner cases later. – Ar3s Aug 04 '23 at 15:36
read mon year <<< "Oct 2012"
date -d "$mon 1 $year" "+%Y-%m"
Result:
2012-10

- 6,387
- 2
- 29
- 50
-
3You can even skip the `read` command, as "1 Oct 2012" is an accepted input format for `date`: `dateStr="Oct 2012"; date -d "1 $dateStr" "+%Y-%m"` – chepner Mar 06 '13 at 17:04
-
a little more explanation would be nice, like with the 1 in between $mon and $year and the + in front of %Y. – wordsforthewise Apr 20 '17 at 05:28
-
I figured out the 1 after $mon is like a flag that says 'use the abbreviated month'. the + is `when using an option to specify date(s), any non-option argument must be a format string beginning with '+'` – wordsforthewise Apr 20 '17 at 05:31
case "`date | awk '{print $2 }'`" in
Jan) MON="01" ;;
Feb) MON="02" ;;
Mar) MON="03" ;;
Apr) MON="04" ;;
May) MON="05" ;;
Jun) MON="06" ;;
Jul) MON="07" ;;
Aug) MON="08" ;;
Sep) MON="09" ;;
Oct) MON="10" ;;
Nov) MON="11" ;;
Dec) MON="12" ;;
esac
echo $MON

- 2,947
- 1
- 33
- 43

- 41
- 1
You can convert the month to a number by finding the position of the name string:
#!/bin/bash
month=Oct
months="JanFebMarAprMayJunJulAugSepOctNovDec"
tmp=${months%%$month*}
month=${#tmp}
monthnumber $((month/3+1))
printf "%02d\n" $monthnumber
The output of the script above is:
10
Your specific string you could code:
#!/bin/bash
mydate="Oct 2011"
monthnumber() {
month=$1
months="JanFebMarAprMayJunJulAugSepOctNovDec"
tmp=${months%%$month*}
month=${#tmp}
monthnumber=$((month/3+1))
printf "%02d\n" $monthnumber
}
arr=(`echo ${mydate}`);
month=$(monthnumber ${arr[0]})
year=$(echo ${arr[1]})
echo "$year-$month"
The output would be:
2011-10

- 1,679
- 1
- 22
- 35
I'm not sure if there is a shorter way of doing this, but here is one way. This is by no means fool proof. You can improve this by adding other checks to input and make the comparison case insensitive.
#!/bin/ksh
### Validate input
if [ $# -eq 0 ]
then
echo "Usage: $0 InputMonYYYY"
echo "Example: $0 \"Oct 2011\""
exit 1
fi
### Read input
INPUTSTR=$1
MON_STR=`echo $INPUTSTR |cut -d' ' -f1`
YYYY_STR=`echo $INPUTSTR |cut -d' ' -f2`
if [[ "$MON_STR" = "Jan" ]] then
MON_NUM=01
elif [[ "$MON_STR" = "Feb" ]] then
MON_NUM=02
elif [[ "$MON_STR" = "Mar" ]] then
MON_NUM=03
elif [[ "$MON_STR" = "Apr" ]] then
MON_NUM=04
elif [[ "$MON_STR" = "May" ]] then
MON_NUM=05
elif [[ "$MON_STR" = "Jun" ]] then
MON_NUM=06
elif [[ "$MON_STR" = "Jul" ]] then
MON_NUM=07
elif [[ "$MON_STR" = "Aug" ]] then
MON_NUM=08
elif [[ "$MON_STR" = "Sep" ]] then
MON_NUM=09
elif [[ "$MON_STR" = "Oct" ]] then
MON_NUM=10
elif [[ "$MON_STR" = "Nov" ]] then
MON_NUM=11
elif [[ "$MON_STR" = "Dec" ]] then
MON_NUM=12
fi
echo ${YYYY_STR}-${MON_NUM}

- 3,315
- 10
- 37
- 47
Bash4 supports hash-tables (answer by Jim is the correct one though).
Example
#!/bin/bash
declare -A months=( ["Jan"]="01" ["Feb"]="02" )
mydate="Jan 2011"
echo ${mydate:4:8}-"${months["${mydate:0:3}"]}"
Output:
2011-01

- 34,448
- 50
- 182
- 322

- 44,604
- 7
- 83
- 130
Let's kick this dead horse. If you don't care about invalid month names you can use this function I've written which is quite short (and only does 1 exec) but expects a month to be valid english 3-chars lower or upper case and only requires GNU sed and bash:
m2n() { echo $((-10+$(sed 's/./\U&/g;y/ABCEGLNOPRTUVY/60AC765A77ABB9/;s/./+0x&/g'<<<${1#?}) ));}
For your example I'd do:
read m y <<<"$@"; echo "$y-`m2n $m`"

- 361
- 1
- 3
- 3