Change date and time format from csv file without using date -d

csvdatetext processing

I have a .csv file contains

Data1|Data2|10/24/2017 8:10:00 AM

I want to change date and time format of column 3 as following:

From 10/24/2017 8:10:00 AM (12-Hour) to 20171024 08:10:00(24-hour).

Not using -d

Best Answer

A pure awk solution (that doesn’t fork off a date command):

awk -F'|' -vOFS='|' '
function fail() {
        printf "Bad data at line %d: ", NR
        print
        next
    }
    {
        if (split($3, date_time, " ") != 3) fail()
        if (split(date_time[1], date, "/") != 3) fail()
        if (split(date_time[2], time, ":") != 3) fail()
        if (time[1] == 12) time[1] = 0
        if (date_time[3] == "PM") time[1] += 12
        $3 = sprintf("%.4d%.2d%.2d %.2d:%.2d:%.2d", date[3], date[1], date[2], time[1], time[2], time[3])
        print
    }'
  • -F'|' breaks the input line apart at vertical bars into $1, $2, $3, etc…
  • split($3, date_time, " ") breaks the date/time field into three pieces: the date, the time, and the AM/PM indicator.  If there aren’t three pieces, issue an error message and skip the line.
  • split(date_time[1], date, "/") splits the date into the month, the day, and the year.
  • split(date_time[2], time, ":") splits the time into the hour, the minutes, and the seconds.
  • Do some math on the hour; for example, 12:42 AM is 00:42 in 24-hour time.  And of course PM adds 12 hours.
  • The sprintf reassembles the year, month, day, hour, minutes, and seconds, with leading zeroes, if necessary.  Assigning this to $3 rebuilds the input line with the reformatted date/time; we then print that.
  • Feature: If the input has more than three fields; e.g.,

    Data1|Data2|10/24/2017 8:10:00 AM|Data4|Data5
    

    this script will preserve those extra field(s).


Usage:  A few minor variations:

  • Type the above multi-line command, and, at the end of the last line (right after }'), put the name(s) of file(s) you want to process.  You can (of course) use wildcards (e.g., *.csv) here, in addition to or instead of filename(s).
  • Same as the above, but after }', say < and a filename.  (You can process only one file at a time this way.)
  • Create a script file. 
    • The first line should be #!/bin/sh.  (Or, if you prefer, you can use #!/bin/bash or #!/usr/bin/env bash.  A discussion of the differences between these different “she-bang” lines, and their relative merits and counter-indications, is beyond the scope of this question, but you can find plenty of discourse on the topic if you search.)
    • Then put the above code starting at line 2.
    • At the end of the last line (right after }'),  put "$@" (including the quotes).
    • Save the file.  Let’s assume that you call the script gman.
    • Type chmod +x gman.
    • Type ./gman followed by either a list of filenames and/or wildcards, or by < and a single filename.
Related Question