4

如何在 perl 中漂亮地打印持续时间?

到目前为止我唯一能想到的是

my $interval = 1351521657387 - 1351515910623; # milliseconds
my $duration = DateTime::Duration->new(
    seconds => POSIX::floor($interval/1000) ,
    nanoseconds  => 1000000 * ($interval % 1000),
);
my $df = DateTime::Format::Duration->new(
    pattern => '%Y years, %m months, %e days, ' .
               '%H hours, %M minutes, %S seconds, %N nanoseconds',
    normalize => 1,
);
print $df->format_duration($duration);

这导致

0 years, 00 months, 0 days, 01 hours, 35 minutes, 46 seconds, 764000000 nanoseconds

这对我没有好处,原因如下:

  1. 我不想看到“ 0 years ”(空间浪费)&c,我不想从(如果我下次确实需要几年怎么办?)pattern
  2. 我事先知道我的精度只有毫秒,我不想在纳秒部分看到 6 个零。
  3. 我更关心漂亮/紧凑/人类可读性,而不是精度/机器可读性。即,我想看到类似“ 1.2 年”或“ 3.22 个月”或“ 7.88 天”或“ 5.7 小时”或“ 75.5 分钟”(或“ 1.26 小时”,无论你觉得更好)或“ 24.7 秒”之类的东西或“ 133.7 毫秒” &c(类似于R打印difftime 的方式
4

3 回答 3

3

您可以根据某些值是否为“真”来动态构建模式。

...
push @pattern, '%Y years' if $duration->year;
push @pattern, '%m months' if $duration->month;
...
my $df = DateTime::Format::Duration->new(
    pattern => join(', ', @pattern),
    normalize => 1,
);
print $df->format_duration($duration);
于 2012-11-02T16:58:12.563 回答
2

这是我最终使用的:

sub difftime2string ($) {
  my ($x) = @_;
  ($x < 0) and return "-" . difftime2string(-$x);
  ($x < 1) and return sprintf("%.2fms",$x*1000);
  ($x < 100) and return sprintf("%.2fsec",$x);
  ($x < 6000) and return sprintf("%.2fmin",$x/60);
  ($x < 108000) and return sprintf("%.2fhrs",$x/3600);
  ($x < 400*24*3600) and return sprintf("%.2fdays",$x/(24*3600));
  return sprintf("%.2f years",$x/(365.25*24*3600));
}
于 2013-11-12T15:12:39.723 回答
0

我想看到“1.2 年”或“3.22 个月”或“7.88 天”之类的内容

您可以使用Time::Seconds中的常量:

use Time::Seconds;
use feature qw(say);
...

$time_seconds = $interval / 1000;
if ( $time_seconds > ONE_YEAR ) {
    printf "The interval is %.2f years\n", $time_seconds / ONE_YEAR;
}
else {
if ( $time_seconds > ONE_DAY ) {
    printf "The interval is %.2f days\n", $time_seconds / ONE_DAY;
}
else { 
if ( $time_seconds > ONE_HOUR ) {
    printf "The interval is %.2f hours\n", $time_seconds / ONE_HOUR;
}
else {
    say "The interval is $time_seconds seconds";
}

也可以使用开关,但仍标记为实验性

use feature qw(switch say);
use Time::Seconds;

...
my $time_seconds = $interval / 1000;

for ( $time_seconds ) {
    when ( $time_seconds > ONE_YEAR ) {
        printf "The interval is %.2f years\n", $time_seconds / ONE_YEAR;
    }
    when ( $time_seconds > ONE_DAY ) {
        printf "The interval is %.2f days\n", $time_seconds / ONE_DAY;
    }
    when ( $time_seconds > ONE_HOUR ) {
        printf "The interval is %.2f hours\n", $time_seconds / ONE_HOUR;
    }
    default { say "The interval is $time_seconds seconds"; }
}

甚至可能有一种方法可以将所有内容组合成一个数组,以便拥有一个Time语句。(未经测试,但你明白了):

 my @times = (
    [ INTERVAL => ONE_YEAR, VALUE => "years" ],
    [ INTERVAL => ONE_DAY,  VALUE => "days"  ],
    [ INTERVAL => ONE_HOUR, VALUE => "hours" ],
);

for my $interval ( @times ) {
    if ( $time_seconds > $interval->{INTERVAL} ) {
       printf "The interval is %.2f %s\n"
          , $time_seconds / $interval->{INTERVAL}, $interval->{VALUE};
    }
}

对此并不太疯狂。你最好简单地制作一个pretty_time子程序来隐藏代码。

say pretty_time( $interval );
于 2013-11-12T15:34:15.633 回答