Shell – Modify date format in-place through `sed` command

datesedshell-script

I'm trying to modify the dates in a text in place where there are as 2016-Dec-24 or 2016-12-24 and I have to replace them like 24/12/2016.

Is there any way I can use sed -i to do this?

Input example:

Holiday or Observance Religious Group Dates Additional Notes
*Rosh Hashanah (New Year) Jewish 25-Sep-2014 – 26-Sep-2014 Designated non-work day for observers Navaratri/Dassehra Hindu Sept. 2014-Oct-03

*Yom Kippur (Day of Atonement) Jewish 2014-Oct-04, Designated non-work day for observers

Sponsored Conferences Title Location Conference Date 2017 IEEE Sensors
Applications Symposium (SAS) United States 2017-03-13, 2017-03-14,
2017-03-15 2017 IEEE International Symposium on Medical Measurements
and Applications (MeMeA) United States 2017-05-07 to 2017-05-10 2017
IEEE International Instrumentation and Measurement Technology
Conference (I

That's what I've tried so far.

sed -i -e 's/\([0-9]\{4\}\)-\(01|Jan\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(02|Feb\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(03|Mar\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(04|Apr\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(05|May\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(06|Jun\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(07|Jul\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(08|Aug\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(09|Sep\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(10|Oct\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(11|Nov\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(12|Dec\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' $1

Best Answer

If you have access to GNU date (the default on Linux systems), you can do:

$ sed -E 's/(.*)-([a-z]+)(.+)/\2\3-\1/i' file | 
    while read d; do date -d "$d" +%d/%m/%y; done
24/12/16
24/12/16

That changes lines like 2016-Dec-24 to Dec-24-2016 (a format that GNU date can understand), leaves lines like 2016-12-24 (a format GNU date already understands) alone, and then passes each line as an input date string to date. It doesn't do it in-place and it doesn't use sed -i but is almost certainly the simplest approach.

If you really need to do it using sed, you could make a list of all months and corresponding numbers:

$ for m in {1..12}; do printf '%s %s\n' "$m" $(date -d "$m/1/2016" +%b); done
1 Jan
2 Feb
3 Mar
4 Apr
5 May
6 Jun
7 Jul
8 Aug
9 Sep
10 Oct
11 Nov
12 Dec

Save that as months and then iterate over it to modify your file:

while read num mon; do 
    sed -Ei "s/$mon/$num/; s#(.*)-(.*)-(.*)#\3/\2/\1#" file
done < months 

Or, if your sed implementation needs separate -e:

while read num mon; do 
    sed -i -e "s/$mon/$num/" -Ee 's#(.*)-(.*)-(.*)#\3/\2/\1#' file
done < months 

The first substitution will replace all alphabetical month names with their corresponding number and the second moves things around to get your desired format.

Related Question