在Unix中排序日期/时间

时间:2012-07-25 08:15:29

标签: perl bash shell sed awk

输入

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

所需输出

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

代码我试过

 sort -t ":" -k 1 -k 2 -k 3 Input.txt | sort -t " " -k 3

但我没有得到理想的输出。

有人可以提出任何建议吗?


我写了一段代码......但问题仍然存在......

代码

 sed 's/ 12:/00:/g' Input.txt | sort -k 1,1 -k 3,3 -k 2,2 | sed 's/00:/12:/g'

首先将 12:43:01 AM 更改为 00:43:01 AM ....然后应用sort命令。

9 个答案:

答案 0 :(得分:4)

转换为纪元秒进行排序

假设您的数据存储在/ tmp / foo中,您可以将时间戳转换为具有GNU日期的数字可排序格式。例如:

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

这应该在所有情况下正确处理排序,特别是在所有AM时间应该在同一日期的所有PM时间之前到来的情况。例如,上午12:01现在列在晚上10点之前。

答案 1 :(得分:3)

除了12小时之外,字符串可以简单地按词汇顺序排序。

此解决方案使用Schwartzian Transform更改用于对字符串进行排序的键。它只会在以PM结尾的任何字符串的小时字段中添加12,并按其排序。

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 AM
2012-07-26 10:13:58 AM
2012-07-24 10:13:58 AM
2012-07-24 10:57:50 AM
2012-07-24 11:15:03 AM
2012-07-24 11:26:08 PM
2012-07-25 11:26:08 PM

<强>输出

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

<强>更新

正如斯特芬指出的那样,即使在调整上午/下午的时间后,午夜和中午仍然会阻止简单的字符串排序。

此程序使用核心Time::Piece模块重新格式化ISO 8601格式2000-02-29T12:34:56中的日期/时间,可以对词汇进行排序。

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 AM
2012-07-26 10:13:58 AM
2012-07-24 10:13:58 AM
2012-07-24 10:57:50 AM
2012-07-24 11:15:03 AM
2012-07-24 11:26:08 PM
2012-07-25 11:26:08 PM
2012-08-01 01:00:00 PM
2012-08-01 12:30:00 PM
2012-08-01 12:00:00 PM
2012-08-01 11:30:00 AM
2012-08-01 01:00:00 AM
2012-08-01 12:30:00 AM
2012-08-01 12:00:00 AM

<强>输出

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

答案 2 :(得分:2)

有点awk病房,我承认......

<击>     cat Input.txt | \       awk&#39; BEGIN {FS =&#34; [: - ]&#34;} {if($ 7 ==&#34; PM&#34;)$ 4 + = 12;打印$ 1&#34; - &#34; $ 2&#34; - &#34; $ 3&#34; &#34; $ 4#34;:&#34; $ 5#34;:&#34; $ 6#34; &#34; $ 7}&#39; | \       排序| \       awk&#39; BEGIN {FS =&#34; [: - ]&#34;} {if($ 7 ==&#34; PM&#34;)$ 4- = 12;打印$ 1&#34; - &#34; $ 2&#34; - &#34; $ 3&#34; &#34; $ 4#34;:&#34; $ 5#34;:&#34; $ 6#34; &#34; $ 7}&#39;

<击>

<击>

修改

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}'

但它有效......

说明:我使用awk将时间格式转换为24小时,对其进行排序并将其转换回来。

修改:我将0添加到只有一位数的小时,以便1:0:012:0:0排序正确。也适用于AM。

答案 3 :(得分:2)

使用Schartzian TransformDate::Parse

use strict;
use warnings;
use 5.010;
use Date::Parse;

my @data = <DATA>;
chomp @data;

my @sorted = 
    map  { $_->[0] }
    sort { $a->[1] <=> $b->[1] }
    map  { [$_, str2time($_)] } @data;

say for @sorted;

__DATA__
2012-07-24 11:15:03 AM
2012-07-24 11:26:08 PM
2012-07-25 01:26:08 PM
2012-07-25 12:26:08 PM
2012-07-25 01:26:08 AM
2012-07-25 12:26:08 AM
2012-07-25 11:26:08 AM
2012-07-25 11:26:08 PM

<强>输出:

2012-07-24 11:15:03 AM
2012-07-24 11:26:08 PM
2012-07-25 12:26:08 AM
2012-07-25 01:26:08 AM
2012-07-25 11:26:08 AM
2012-07-25 12:26:08 PM
2012-07-25 01:26:08 PM
2012-07-25 11:26:08 PM

答案 4 :(得分:1)

12:01 AM凌晨1点01分到来,所以我看不出使用排序会有什么帮助。

您需要转换为其他格式,例如无论何时何时,ISO 8601或秒 获得可以作为文本或数字进行比较的内容。 perl oneliner会做到这一点。

答案 5 :(得分:1)

您可以使用:

sed 's/ 12:/ 00:/'| LC_ALL="C" sort -k 1,1 -k 3 | sed 's/ 00:/ 12:/'

应该是非常快速的解决方案。

答案 6 :(得分:0)

有我的变体:

$sed 's|\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\) \([0-9]\+\):\([0-9]\+\):\([0-9]\+\) \([A-Z]\+\)|\1 \2 \3 \4 \5 \6 \7 \0|' input.txt | awk '{if($7=="AM"){$7="1";if($4==12){$4 = 0}}else{$7="0"};print}' | sort -n -k1 -k2 -k3 -k4 -k5 -k6 -k7 | cut -d' ' -f 8-
2012-07-24 10:05:08 PM
2012-07-24 10:13:58 AM
2012-07-24 10:57:50 AM
2012-07-24 11:15:03 AM
2012-07-24 11:26:08 PM
2012-07-25 11:26:08 PM
2012-07-26 10:13:58 AM

添加额外字段的主要想法,按它们排序并在排序后我将它们删除。排序数字很简单,但要对AM / PM进行排序,我将其转换为1/0位数以简化排序。

已更新:sed + awk使用情况可由awk替换:

awk -F'[-: ]' '{printf("%d %d %d %d %d %d %d %s\n", $1, $2, $3, ($4 == 12 && $7 == "AM" ? 0 : $4), $5, $6, $7 == "AM", $0)}' input.txt |
sort -n -k1 -k2 -k3 -k4 -k5 -k6 -k7 |
cut -d' ' -f 8-

更新:修复上午/下午问题

答案 7 :(得分:0)

这可能适合你(GNU sed):

sed 's/.*/echo -e "$(date -d"&" +%s)\t&"/e' file | sort -n | sed 's/.*\t//'

或:

date -f file +%s | paste - file | sort -n | sed 's/\S\+\s\+//'

答案 8 :(得分:-1)

最后我编码时没有使用任何外部模块。虽然它很长但是可以顺利地用于任何日期格式。

使用的技术:

  1. 首先将文件中的每个日期转换为其时间戳
  2. 按时间顺序对时间戳进行排序。
  3. 再次使用标量本地时间将时间戳转换为日期。
  4. <强>代码

    my @input = `cat Input.txt`;
    
        open (ts,">","tt.txt");
        foreach my $i (@input)
        {
                chomp($i);
                my $timestamp = `date --date "$i" +\%s`;
                chomp($timestamp);
                push (@time,$timestamp);
                print ts "$timestamp\n";
        }
        close(ts);
    
        open (ts,">","sort_time.txt");
        my @sorted_time = join "\n",sort {$a<=>$b} @time;
        chomp(@sorted_time);
        print ts "@sorted_time\n";
        close(ts);
    
        my @input1=  `cat sort_time.txt`;
        open (ts,">","sort_timestamp.txt");
        foreach my $st1 (@input1)
        {
                chomp($st1);
                my $st2 = scalar localtime($st1);
                chomp($st2);
                print ts "$st2\n";
        }
        close(ts);
    
    
    
        @input2 = `cat sort_timestamp.txt`;
        open (ts,">","Output.txt");
        foreach my $st2 (@input2)
        {
                chomp($st2);
                $pro_time = `date --date "$st2" +\%Y-\%m-\%d~\%r | sed 's/~/ /g'`;
                chomp($pro_time);
                print ts "$pro_time\n";
        }
        close(ts);
    
    
        `rm tt.txt sort_time.txt sort_timestamp.txt`;