How can I convert to date format (DD MMM YYYY) using the shell?

Viewed 3431

I have data in a file where one column is a date column which has date in the following format:

2021-05-10T18:25:00.000+0100
2021-05-14T18:25:00.000+0100
2021-05-19T18:25:00.000+0100

Expected output is:

10 MAY 2021
14 MAY 2021
19 MAY 2021

My approach which I've tried:

while -r read line
do
    year=`echo $line | awk '{ print $1 }' `
    month=`echo $line | awk '{ print $2 }' `
    dt=`echo $line | awk '{ print $3 }' `

    v=$dt"-"$month"-"$year
    d=date '`$v' | dd-mm-yyyy
    echo $d
done < /f/filename.txt
6 Answers

The GNU coreutils date command supports the input format you have, e.g.:

date -f filename.txt +'%d %b %Y'

Output:

10 May 2021
14 May 2021
19 May 2021

Pipe it through tr a-z A-Z if you want it in all-caps.

Note: tested with version 8.30 of GNU coreutils.

You have a number of syntax and design problems in your attempt.

I don't particularly recommend using the shell for this; the other answers with plain date and Awk and Perl etc solutions are definitely better for this particular task, both in terms of usability and readability as well as performance. But there are scenarios where you do want or need to use the shell, and then it's important to understand what the proper syntax looks like.

For the record, then, the shell is perfectly capable of splitting the input line on whitespace, or whatever IFS is set to, into variables.

You also had some peculiar typos in your d assignment. I'm guessing wildly here as to what you actually meant.

while IFS='-T' read -r year month dt _;
do
    date -d "$year-$month-$dt" +"%d %b %Y"
done < /f/filename.txt

d=date whatever would assign the literal string date to the variable d and then attempt to run whatever as a command, and probably fail. But of course, creating a variable only so that you can then immediately echo it is just a useless use of echo.

date -d is a GNU variation; on *BSD (including macOS) you'd need something like date -j -f %Y-%m-%d +"%d %b %Y" 2021-05-28

I don't think strftime offers an uppercase version of the month name; if SHOUTING is important to you, maybe pipe to

done <file | tr a-z A-Z

The format code %b is locale-dependent; if you want to force English (or legacy) month names, try LC_TIME=POSIX date (notice now how we use the syntax I explained above!)

With your sample, please try the following:

awk -F'[T-]' '
BEGIN{
  num=split("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec",month,",")
}
{
  mon=substr($2,2,1)
}
(mon in month){
  print $3,month[mon],$1
}
' Input_file

OR try following:

awk -F'[T-]' '
BEGIN{
  num=split("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec",month,",")
}
($2+0 in month){
  print $3,month[$2+0],$1
}
' Input_file

Explanation: Adding detailed explanation for the code above:

awk -F'[T-]' '             ##Starting awk program from here and setting field separator as - OR T here for all Lines.
BEGIN{                     ##Starting BEGIN section of this program here.
  num=split("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec",month,",") ##Creating month array here which has month names here.
}
{
  mon=substr($2,2,1)       ##Creating variable mon, which has sub string of 2nd field with 1st character.
}
(mon in month){            ##Checking if mon is present in month then do following.
  print $3,month[mon],$1   ##Printing 3rd field, month value 1st field.
}
' Input_file               ##Mentioning Input_file name here.

Using gnu-awk, this can be done in a single line:

awk '{
print toupper(strftime("%d %b %Y", mktime(gensub(/[-:T]/, " ", "g"))))
}' file

10 MAY 2021
14 MAY 2021
19 MAY 2021

Or without gensub:

awk -F '[-:T]' '{$1=$1; print toupper(strftime("%d %b %Y", mktime($0)))}' file

Another approach:

awk -F'[T-]' 'BEGIN{                         
                mon="JanFebMarAprMayJunJulAugSepOctNovDec"

              # if need uppercase use below variable
              # mon="JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"
              }
              {
                print $3,substr(mon,($2*3)-2,3), $1
              }' file

Test Results:

Input:

$ cat file 
2021-01-10T18:25:00.000+0100
2021-02-14T18:25:00.000+0100
2021-03-19T18:25:00.000+0100
2021-04-19T18:25:00.000+0100
2021-05-19T18:25:00.000+0100
2021-06-19T18:25:00.000+0100
2021-07-19T18:25:00.000+0100
2021-08-19T18:25:00.000+0100
2021-09-19T18:25:00.000+0100
2021-10-19T18:25:00.000+0100
2021-11-19T18:25:00.000+0100
2021-12-19T18:25:00.000+0100

Output :

$ awk -F'[T-]' 'BEGIN{mon="JanFebMarAprMayJunJulAugSepOctNovDec"}{print $3,substr(mon,($2*3)-2,3), $1}' file
10 Jan 2021
14 Feb 2021
19 Mar 2021
19 Apr 2021
19 May 2021
19 Jun 2021
19 Jul 2021
19 Aug 2021
19 Sep 2021
19 Oct 2021
19 Nov 2021
19 Dec 2021

With perl:

$ perl -MTime::Piece -lne '$t = Time::Piece->strptime(substr($_,0,10), "%F");
                           print $t->strftime("%d %b %Y")' ip.txt
10 May 2021
14 May 2021
19 May 2021

I used two statements for readability, single statement version shown below:

perl -MTime::Piece -lne 'print Time::Piece->strptime(substr($_,0,10), "%F")->strftime("%d %b %Y")'
Related