Is there a CPAN module for converting seconds to English?
Asked Answered
N

5

6

Is there a CPAN module that can convert a number of seconds to a human-readable English description of the interval?

secToEng( 125 ); # => 2 min 5 sec
secToEng( 129_600 ); # => 1 day 12 h

The format isn't important as long as it's human-readable.

I know that it would be trivial to implement.

Norward answered 25/9, 2011 at 13:6 Comment(2)
A lot of people think working with dates and times is trivial, and this is simply not the case. You've done the right thing by looking for an existing module :)Geyser
@ocharles: You're definitely right about date handling being hard. I think this case is quite easy though :)Norward
F
6

Since Perl v5.9.5, the modules Time::Seconds and Time::Piece are part of the core Perl distribution. So you can use them without installing additional modules.

perl -MTime::Seconds -e 'my $s=125; my $ts=new Time::Seconds $s; print $ts->pretty, "\n"'
# 2 minutes, 5 seconds

perl -MTime::Seconds -e 'my $s=129_600; my $ts=Time::Seconds->new($s); print $ts->pretty, "\n"'
# 1 days, 12 hours, 0 minutes, 0 seconds

You can also use stuff like $ts->weeks, $ts->minutes, etc.

Fujimoto answered 21/10, 2012 at 14:10 Comment(1)
"1 days" is a bug: the singular is "day". Please perlbug this.October
N
14

It turns out that Time::Duration does exactly this.

$ perl -MTime::Duration -E 'say duration(125)'
2 minutes and 5 seconds
$ perl -MTime::Duration -E 'say duration(129_700)'
1 day and 12 hours

From the synopsis:

Time::Duration - rounded or exact English expression of durations

Example use in a program that ends by noting its runtime:

my $start_time = time();
use Time::Duration;
# then things that take all that time, and then ends:
print "Runtime ", duration(time() - $start_time), ".\n";

Example use in a program that reports age of a file:

use Time::Duration;
my $file = 'that_file';
my $age = $^T - (stat($file))[9];  # 9 = modtime
print "$file was modified ", ago($age);
Norward answered 25/9, 2011 at 14:57 Comment(0)
E
11

DateTime can be used:

#!/usr/bin/env perl

use strict;
use warnings;

use DateTime;
use Lingua::EN::Inflect qw( PL_N );

my $dt = DateTime->from_epoch( 'epoch' => 0 );
$dt = $dt->add( 'seconds' => 129_600 );
$dt = $dt - DateTime->from_epoch( 'epoch' => 0 );

my @date;
push @date, $dt->days . PL_N( ' day', $dt->days ) if $dt->days;
push @date, $dt->hours . PL_N( ' hour', $dt->hours ) if $dt->hours;
push @date, $dt->minutes . PL_N( ' minute', $dt->minutes ) if $dt->minutes;
push @date, $dt->seconds . PL_N( ' second', $dt->seconds ) if $dt->seconds;

print join ' ', @date;

Output

1 day 12 hours
Elsy answered 25/9, 2011 at 14:1 Comment(6)
I have to admit that the “xxx(s)” notation is a personal pet peeve of mine. If they can do anything, computers should be able to count and distinguish 1 day vs 2 days. :( Note that prefix notation as in Days: 1 doesn’t have this issue.October
However, just "$x day" . ($x > 1 ? 's' : '') is a sloppy way of handling this. If you always use translation libraries to build your strings, this comes for free.Geyser
You only fixed the 'easiest' part of the problem, converting seconds to something else.Scullion
MichielB: The answer addresses just the problem in the question. With DateTime, other conversions are easy as well.Elsy
Lingua::EN::Inflect is an interesting solution to the pluralization problem. metacpan.org/module/Lingua::EN::InflectMelentha
Schwern: I have just updated my answer to include Lingua::EN::Inflect. Thank you.Elsy
F
6

Since Perl v5.9.5, the modules Time::Seconds and Time::Piece are part of the core Perl distribution. So you can use them without installing additional modules.

perl -MTime::Seconds -e 'my $s=125; my $ts=new Time::Seconds $s; print $ts->pretty, "\n"'
# 2 minutes, 5 seconds

perl -MTime::Seconds -e 'my $s=129_600; my $ts=Time::Seconds->new($s); print $ts->pretty, "\n"'
# 1 days, 12 hours, 0 minutes, 0 seconds

You can also use stuff like $ts->weeks, $ts->minutes, etc.

Fujimoto answered 21/10, 2012 at 14:10 Comment(1)
"1 days" is a bug: the singular is "day". Please perlbug this.October
A
2

I wasn't able to find such code a while back, so I wrote these two routines. The second sub uses the first and does what you are looking for.

#-----------------------------------------------------------
# format_seconds($seconds)
# Converts seconds into days, hours, minutes, seconds
# Returns an array in list context, else a string.
#-----------------------------------------------------------

sub format_seconds {
    my $tsecs = shift;

    use integer;
    my $secs  = $tsecs % 60;
    my $tmins = $tsecs / 60;
    my $mins  = $tmins % 60;
    my $thrs  = $tmins / 60;
    my $hrs   = $thrs  % 24;
    my $days  = $thrs  / 24;

    if (wantarray) {
        return ($days, $hrs, $mins, $secs);
    }

    my $age = "";
    $age .= $days . "d " if $days || $age;
    $age .= $hrs  . "h " if $hrs  || $age;
    $age .= $mins . "m " if $mins || $age;
    $age .= $secs . "s " if $secs || $age;

    $age =~ s/ $//;

    return $age;
}

#-----------------------------------------------------------
# format_delta_min ($seconds)
# Converts seconds into days, hours, minutes, seconds
# to the two most significant time units.
#-----------------------------------------------------------

sub format_delta_min {
    my $tsecs = shift;

    my ($days, $hrs, $mins, $secs) = format_seconds $tsecs;

    # show days and hours, or hours and minutes, 
    # or minutes and seconds or just seconds

    my $age = "";
    if ($days) {
        $age = $days . "d " . $hrs . "h";
    } 
    elsif ($hrs) {
        $age = $hrs . "h " . $mins . "m";
    } 
    elsif ($mins) {
        $age = $mins . "m " . $secs . "s";
    }
    elsif ($secs) {
        $age = $secs . "s";
    }

    return $age;
}
Alcoholic answered 25/9, 2011 at 13:36 Comment(3)
Thanks for supplying some code. I was mostly interested in knowing whether there's already such a CPAN module out there, or if one should be written.Norward
The question was not 'Please write me a sub'.Scullion
I didn't write a sub for him. I shared some code I wrote for myself years ago. Thought that would be a friendly thing to do.Alcoholic
R
2

The DateTime modules are what you want.

DateTime::Duration and DateTime::Format::Duration will do what you need.

Rigmarole answered 25/9, 2011 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.