添加MM :: SS次数,除以它们并打印平均值

时间:2016-09-14 20:48:56

标签: perl datetime

让我们说我跑了10公里,现在我以MM :: SS的形式进行了10次1公里的分割。我想要一种简单的方法来添加任意数量的分割时间并对它们求平均值。例如,也许我想看看最后5公里与前5公里相比有多快(或更慢)。

我可以通过解析时间,将它们分成几秒然后将它们转换回MM :: SS来自己做。数学并不难,但我想知道CPAN上的某些东西是否已经以简单的方式完成了这项工作。我的第一次尝试是使用DateTime,但由于闰秒,它不会从秒转换为分钟。

要明确的是,在这种情况下我并不关心闰秒,但我对图书馆是否存在感到好奇。作为一个例子,这是我尝试过的。

#!/usr/bin/env perl

use strict;
use warnings;
use feature qw( say );

use DateTime::Format::Duration;

my $formatter = DateTime::Format::Duration->new( pattern => '%M:%S' );

my @splits = @ARGV;
my $split_number = @splits;

my $total = $formatter->parse_duration( shift @splits );

foreach my $split (@splits) {
    $total->add_duration(
        $formatter->parse_duration($split) );
}

say 'Total time: ' . join ':', $total->minutes, $total->seconds;

$total->multiply( 1 / $split_number );

say 'Average time: ' . join ':', $total->minutes, $total->seconds;
say 'Using DateTime::Format::Duration: ' . $formatter->format_duration( $total );

示例运行:

$ perl script/add-splits.pl 1:30 2:30
Total time: 3:60
Average time: 1.5:30
Using DateTime::Format::Duration: 01:30

所以,你可以看到持续时间对象本身给了我一个正确的答案,但不是人类想要破解的东西。

DateTime :: Format :: Duration尝试提供帮助,但在此过程中会丢失30秒。

我没有寻找原始代码来执行此操作。我对CPAN上是否已存在感兴趣。

2 个答案:

答案 0 :(得分:1)

在CPAN上找到这个确切功能的问题是你想要操作时间间隔的字符串。大多数模块都关注这些字符串的上下文,将它们用作 date time 。因此很难找到只添加mm:ss格式的内容。由于这个任务非常具体,而且编写起来很简单,为什么不将它包装在你自己的包中呢?

话虽如此,看看下面的片段是否符合您的要求。

这是核心模块Time::Piece的简单解决方案。它确实需要几秒钟才能进行数学计算,但它可以包含在一些副本中,然后也可以轻松扩展以用于其他计算。

use warnings 'all';
use strict;
use feature 'say';

use Time::Piece;
use POSIX 'strftime';
use List::Util 'sum';

my @times = @ARGV;

my $fmt = '%M:%S'; 

my $tot_sec = sum map { Time::Piece->strptime($_, $fmt)->epoch } @times;

my $ave_sec = sprintf("%.0f", $tot_sec/@times);  # round the average

my ($tot, $ave) = map { strftime $fmt, gmtime $_ } ($tot_sec, $ave_sec);

say "Total time:   $tot";
say "Average time: $ave";

对于manip_times.pl 2:30 1:30,这会打印

Total time:   04:00
Average time: 02:00

我们使用strptime中的Time::Piece来获取对象,然后其epoch方法返回秒,这些值会被添加并进行平均。使用POSIX中的mm:ss将其转换回strftimeTime::Piece也有strftime但要使用它我们必须有一个对象。

请注意,Time::Piece 直接减去其对象$t1 - $t2,但无法添加它们。可以通过$t1 + 120向对象添加整数(秒)。另请参阅核心Time::Seconds,这是分发的一部分。

对问题中使用的方法的评论

使用的DateTime::Duration个对象无法在不同单位之间进行转换

  

有关详细信息,请参阅DateTime.pm文档的How DateTime Math Works部分。短期课程:一般不能在几秒,几分钟,几天和几个月之间进行转换,所以这个课程永远不会这样做。

从页面的下方开始,我们会看到可以进行哪些转换,相关的转换仅为“小时< =>分钟”和“秒< => ;纳秒“。原因与闰秒,夏令时等有关。因此,计算必须产生诸如1.5分钟的结果。

请注意Class::Date也适合这些特殊要求。

答案 1 :(得分:0)

这是使用DateTime对象的版本。由于它不处理日期解析,我们必须自己拆分字符串......

<强> add_splits.pl

#!/usr/bin/env perl

use strict;
use warnings;
use feature qw( say );

use DateTime;

my @splits = @ARGV;
my $split_number = @splits;

my $fmt = "%M:%S";
my $total = DateTime->from_epoch( epoch => 0 );

foreach my $split (@splits) {
    my ($split_min, $split_sec) = split /:/, $split;
    $total->add( minutes => $split_min, seconds => $split_sec );
}

say 'Total time: ' . $total->strftime($fmt);

my $avg_seconds = $total->epoch / $split_number;
my $avg = DateTime->from_epoch( epoch => 0 )->add( seconds => $avg_seconds );

say 'Average time: ' . $avg->strftime($fmt);

<强>输出

$ perl add_splits.pl 1:30 2:30
Total time: 04:00
Average time: 02:00