Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Time calculations

by Dirk80 (Pilgrim)
on Aug 20, 2010 at 15:57 UTC ( #856305=perlquestion: print w/replies, xml ) 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.

I'm very interested in your answers.

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
    Hope Time::HiRes helps your objective.

    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; use overload ('""' => 'asString', '+' => 'add', '-' => '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 ); } sub add { 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

Log In?
Username:
Password:

What's my password?
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?