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 awk
ward, 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.