Your skill will accomplishwhat the force of many cannot PerlMonks

### Time calculations

by Dirk80 (Pilgrim)
 on Aug 20, 2010 at 15:57 UTC Need Help??

Dirk80 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I want to store times (hours, minutes, seconds, fractions of a second) in an object. Time::Piece and Time::Seconds make a good beginning. They offer some arithmetic operations and have overloaded >, <, ... .

The following code uses Time::Seconds and Time::Piece to construct the sum of "16:51" and "34:48" in the format (min:sec).

```#!/usr/bin/perl

use strict;
use warnings;

use Time::Piece;
use Time::Seconds;

my \$t1 = Time::Piece->strptime("16:51", "%M:%S");
my \$t2 = Time::Piece->strptime("34:48", "%M:%S");

\$t1 += \$t2->min*60 + \$t2->sec;

print \$t1->strftime("%M:%S");

But there are two reasons why they are not sufficient for what I want to do.

1. I need a fraction of a second (e.g. "16:51,12" and "34:48,14"). I did not found fraction for strftime or strptime.
2. I want to be able to use more arithmetic operations with these time objects. For example: (\$t1 * 4) + \$t2

Here the summary: I would like to have a parse function (time string e.g. "16:51,14" to time object). It should be possible to apply arithmetic operations and comparisons of time objects and the time object should at least store hours, minutes, seconds and fractions of a second. And of course I want to be able to print the time object (something like strftime).

Is there already a module in CPAN which can do this? I did not find any. What would you suggest how to solve that? I don't want to reinvent everything. If possible I want to use an available module.

Thank you and Greetings

Dirk

Replies are listed 'Best First'.
Re: Time calculations
by Generoso (Prior) on Aug 20, 2010 at 16:22 UTC

Look at Time::HiRes module for milliseconds.

Re: Time calculations
by murugu (Curate) on Aug 20, 2010 at 16:42 UTC

Regards,
Murugesan Kandasamy
use perl for(;;);

Re: Time calculations
by ww (Archbishop) on Aug 20, 2010 at 20:49 UTC
Bottom Line(s) come(s) from answers to your recent, previous posts on this general topic:
1. Normalize your times before you start calculating.
2. Check the Time::... modules on CPAN, via PPM or your distro's packages

You're dealing with part one at least in part. Good. We admire self-help.

Time::HiRes (subject of two previous replies) is widely cited here at PM for part two. Perhaps you should also familiarize yourself with the use of Google site:PerlMonks ..., Search and Super Search.

Time::HiRes is not really helping me. I checked the CPAN for time packages many hours and found nothing.

Believe me I am using google, super search and cpan a lot before asking here. And I really want to learn from you and not that you provide me a full solution.

My goal was it to use a common module. But because I do not find one, I've written my own class.

Here you can see the code.

EDIT: Updated code and rewrote some paragraphs which described the code.

EDIT: Now I found a package which has milli and nanoseconds and has math and duration stuff: DateTime. I'll look deeper into it. Perhaps I can replace my own class by using this module. But it was cool to try overloading and writing an own module.

```package My::TimeCalc;

use strict;

'-'  => 'subtract',
'*'  => 'mult',
'/'  => 'divide',
'<=>' => 'compare');

sub new
{
my (\$class, \$time_str) = @_;
my \$seconds;
my \$self = \\$seconds;
bless(\$self, \$class);

\$time_str = defined(\$time_str) ? \$time_str : "0";
return( \$self->fromString(\$time_str) );
}

sub sec
{
my (\$self) = @_;
return \$\$self;
}

sub asString
{
my (\$self, \$precision) = @_;

\$precision = defined(\$precision)?\$precision:2;

my \$min = int(abs(\$\$self) / 60.0);
my \$sec = abs(\$\$self) % 60.0;
my \$frac = sprintf("%.\${precision}f", abs(\$\$self - int(\$\$self))) *
+ 10**\$precision;

my \$time_str = "";
if( \$\$self < 0 ) { \$time_str = "-"; }
\$time_str .= "\$min:" if( \$min != 0 );
\$time_str .= sprintf("%02d", \$sec) if ( (\$min != 0) || (\$sec != 0)
+ );
\$time_str .= "," . sprintf("%0\${precision}d", \$frac) if ( \$frac !=
+ 0 );

if( (\$min == 0) && (\$sec == 0) && (\$frac == 0) ) { \$time_str = "0"
+; }

return( \$time_str );
}

sub fromString
{
my (\$self, \$time_str) = @_;

# 1) min:sec,frac  e.g. "5:29,11"
# 2) sec,frac       e.g. "29,11"
# 3) sec           e.g. "29"
# 4) ,frac           e.g. ",11"
# 5) min:sec       e.g. "5:29"
# Note: instead of "," you can also take "."!

# remove leading and trailing whitespace characters
\$time_str =~ s/^\s+|\s+\$//g;

# valid time string
if( (\$time_str =~ m/^(?<min>\d+):(?<sec>\d+)(,|\.)(?<frac>\d+)\$/)
+||
(\$time_str =~ m/^(?<sec>\d+)(,|\.)(?<frac>\d+)\$/) ||
(\$time_str =~ m/^(?<sec>\d+)\$/) ||
(\$time_str =~ m/^(,|\.)(?<frac>\d+)\$/) ||
(\$time_str =~ m/^(?<min>\d+):(?<sec>\d+)\$/) )

{
no warnings 'uninitialized'; # intention: undef treated as zer
+o
\$\$self = (\$+{'min'} * 60.0) + \$+{'sec'} + (\$+{'frac'}/(10**len
+gth(\$+{'frac'})));
return( \$self );
}

return( undef );
}

{
my (\$self, \$another) = @_;

my \$result;

if( ref(\$another) eq "My::TimeCalc" )
{
\$result = \$self->sec() + \$another->sec();
}
else
{
\$result = \$self->sec() + \$another;
}

return( new My::TimeCalc(\$result) );
}

sub subtract
{
my (\$self, \$another) = @_;

my \$result;

if( ref(\$another) eq "My::TimeCalc" )
{
\$result = \$self->sec() - \$another->sec();
}
else
{
\$result = \$self->sec() - \$another;
}

return( new My::TimeCalc(\$result) );
}

sub mult
{
my (\$self, \$another) = @_;

my \$result;

if( ref(\$another) eq "My::TimeCalc" )
{
\$result = \$self->sec() * \$another->sec();
}
else
{
\$result = \$self->sec() * \$another;
}

return( new My::TimeCalc(\$result) );
}

sub divide
{
my (\$self, \$another) = @_;

my \$result;

if( ref(\$another) eq "My::TimeCalc" )
{
\$result = \$self->sec() / \$another->sec();
}
else
{
\$result = \$self->sec() / \$another;
}

return( new My::TimeCalc(\$result) );
}

sub compare
{
my (\$self, \$another) = @_;

if( ref(\$another) eq "My::TimeCalc" )
{
return( \$self->sec() <=> \$another->sec() );
}
else
{
return( \$self->sec() <=> \$another );
}
}

1;

And now I come back to the example (\$t1 * 4) + \$t2.

```use strict;
use warnings;

use My::TimeCalc;

my \$t1 = new My::TimeCalc("16:51,12");
my \$t2 = new My::TimeCalc("34:48,14");
my \$t = (\$t1 * 4) + \$t2;

print(\$t,"\n"); # 102:12,62

It's the first time I used perl in an object oriented way and my first test with this new class were successful.

I'm sure that it is not good code. I want to change the parse and print function to common time formats. Perhaps here I can use an official module.

Would be great if you can give me feedback. What could I do better?

Thank you

Dirk

Create A New User
Node Status?
node history
Node Type: perlquestion [id://856305]
Approved by moritz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (5)
As of 2020-02-29 12:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
What numbers are you going to focus on primarily in 2020?

Results (128 votes). Check out past polls.

Notices?