Sort Date/Time in Unix Sort Date/Time in Unix shell shell

Sort Date/Time in Unix


Convert to Epoch Seconds for Sorting

Assuming that your data is stored in /tmp/foo, you can convert the timestamp into a numerically-sortable format with GNU date. For example:

date -f /tmp/foo '+%s' | sort |while read; do    date -d "@$REPLY" "+%F %I:%M:%S %p"done

This should correctly handle the sort in all cases, and especially the cases where all AM times should come before all PM times on the same date. For example, 12:01 AM is now listed before 10:00 PM.


The strings could simply be sorted lexically except for the 12-hour times.

This solution uses the Schwartzian Transform to change the key that is used to sort the strings. It just adds twelve to the hour field of any string ending with PM and sorts by that instead.

use strict;use warnings;my @data = <DATA>;chomp @data;my @sorted = map $_->[0],sort { $a->[1] cmp $b->[1] }map { (my $dt = $_) =~ s/(\d\d)(?=:\d\d:\d\d PM)/$1+12/e; [$_, $dt] } @data;print "$_\n" for @sorted;__DATA__2012-07-24 10:05:08 AM2012-07-26 10:13:58 AM2012-07-24 10:13:58 AM2012-07-24 10:57:50 AM2012-07-24 11:15:03 AM2012-07-24 11:26:08 PM2012-07-25 11:26:08 PM

output

2012-07-24 10:05:08 AM2012-07-24 10:13:58 AM2012-07-24 10:57:50 AM2012-07-24 11:15:03 AM2012-07-24 11:26:08 PM2012-07-25 11:26:08 PM2012-07-26 10:13:58 AM

Update

As steffen has pointed out, even after adjusting the hours for am/pm, midnight and midday still prevent a simple string sort from working.

This program uses the core Time::Piece module to reformat the date/times in ISO 8601 format 2000-02-29T12:34:56 which can be sorted lexically.

use strict;use warnings;use Time::Piece;my @data = <DATA>;chomp @data;my @sorted = map $_->[0],sort { $a->[1] cmp $b->[1] }map { [ $_, toISO8601($_) ] } @data;sub toISO8601 {  Time::Piece->strptime(@_, '%Y-%m-%d %I:%M:%S %p')->datetime;}print "$_\n" for @sorted;__DATA__2012-07-24 10:05:08 AM2012-07-26 10:13:58 AM2012-07-24 10:13:58 AM2012-07-24 10:57:50 AM2012-07-24 11:15:03 AM2012-07-24 11:26:08 PM2012-07-25 11:26:08 PM2012-08-01 01:00:00 PM2012-08-01 12:30:00 PM2012-08-01 12:00:00 PM2012-08-01 11:30:00 AM2012-08-01 01:00:00 AM2012-08-01 12:30:00 AM2012-08-01 12:00:00 AM

output

2012-07-24 10:05:08 AM2012-07-24 10:13:58 AM2012-07-24 10:57:50 AM2012-07-24 11:15:03 AM2012-07-24 11:26:08 PM2012-07-25 11:26:08 PM2012-07-26 10:13:58 AM2012-08-01 12:00:00 AM2012-08-01 12:30:00 AM2012-08-01 01:00:00 AM2012-08-01 11:30:00 AM2012-08-01 12:00:00 PM2012-08-01 12:30:00 PM2012-08-01 01:00:00 PM


a little bit awkward, I admit...

cat Input.txt | \ awk 'BEGIN{FS="[: -]"}{if($7 == "PM") $4+=12; print $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'|\ sort|\ awk 'BEGIN{FS="[: -]"}{if($7 == "PM") $4-=12; print $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'

edit:

cat Input.txt |\awk 'BEGIN{FS="[: -]"}{if(length($4)==1) $4="0"$4 ;if($7 == "PM") $4+=12; else if($4 ==12)$4-=12; print $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'|\sort|\awk 'BEGIN{FS="[: -]"}{if($7 == "PM") $4-=12; else if($4 ==0)$4+=12; print $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'

But it works...

explanation: I convert the time format to 24 hours using awk, sort it and convert it back.

edit: I prepend a 0 to hours with only one digit in order to get 1:0:0 and 12:0:0 sorted right. Also for AM.