Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

A regular expression that will work for (hh:)mm:ss [converting clock time to seconds]

by aware74 (Initiate)
on Apr 13, 2012 at 00:20 UTC ( #964844=perlquestion: print w/ replies, xml ) Need Help??
aware74 has asked for the wisdom of the Perl Monks concerning the following question:

I'm still pretty new to Perl, and I want to write a Perl script that will take the elapsed time that is returned by the command

/bin/ps -p $pid -o etime --no-heading
and convert it into seconds.

Below is my first attempt at it. But my code currently assumes that there will always be an hour component, and there will not be. Can someone give me some pointers on how to modify the regular expression for time to accept input like "32:05" in addition to "11:32:05"?

Any additional suggestions that you may have to improve the quality, readability and maintainability of the code would be greatly appreciated.

Thanks in advance,
Mike

#!/usr/bin/perl # URL that generated the regex part of this code: # http://txt2re.com/index.php3?s=345-11:24:09&9&1 use strict; use warnings; my $inputErrMsg = "\nThe input must be of the form <DD->hh:mm:ss\n\n"; my $exitCode=0; my $inputTxt=$ARGV[0]; if ( ( defined($inputTxt) ) && ( $inputTxt ne "" ) ) { $inputTxt = &trim($inputTxt); my $optionalDaysInYearRegex='^(\\d{1,3}-)?'; # optional 3-digit i +nteger followed by a dash my $timeRegex ='((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5 +][0-9])(?::[0-5][0-9]))'; # time in hh:mm:ss format (want it to becom +e <hh:>mm:ss format) my $clockTimeRegex = $optionalDaysInYearRegex.$timeRegex; if($inputTxt =~ m/$clockTimeRegex/is) { my $optionalDaysInYearStruct = $1; my $days = 0; my $nullPlaceholder = ""; if(defined($optionalDaysInYearStruct)) { ($days,$nullPlaceholder) = split('-', $optionalDaysInYearS +truct); } my $time=$2; #print STDERR "\ndays == $days; time == $time\n\n"; my ($hours,$mins,$secs) = split('\:', $time); # I know this wi +ll have to change to work correctly once hours are no longer a given my $totalSecs = ( $days * 24 * 3600 ) + ( $hours * 3600 ) + ( +$mins * 60 ) + $secs; print "$totalSecs\n"; } else { print "$inputErrMsg"; $exitCode = 1; } } else { print "$inputErrMsg"; $exitCode = 1; } sub trim($) { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } exit $exitCode;

Comment on A regular expression that will work for (hh:)mm:ss [converting clock time to seconds]
Download Code
Re: A regular expression that will work for (hh:)mm:ss [converting clock time to seconds]
by Riales (Hermit) on Apr 13, 2012 at 00:35 UTC

    You want the ? which allows 0 or 1 occurrence of the preceding element.

    my $timeRegex = '((?:[0-1][0-9]:)?[0-5][0-9]:[0-5][0-9])';

      Thank you. This is what happens when you don't fully understand the regex that is generated for you.

Re: A regular expression that will work for (hh:)mm:ss [converting clock time to seconds]
by jwkrahn (Monsignor) on Apr 13, 2012 at 02:20 UTC

    Perhaps you want something like this:

    my ( $seconds, $minutes, $hours, $days ) = reverse $inputTxt =~ /\d+/g +;
Re: A regular expression that will work for (hh:)mm:ss [converting clock time to seconds]
by GrandFather (Cardinal) on Apr 13, 2012 at 03:23 UTC

    First off, a few style related points to ponder:

    1. Be consistent with white space usage. Perl::Tidy is great for cleaning up code
    2. Don't use & to call subs, it doesn't do what you think. Use subName(...) instead.
    3. Don't use prototypes. They really don't do what you think.
    4. Use early exits to clean up error handling logic and remove levels of indentation.

    I'd put the code into a subroutine to make testing easier. I'd also simplify the time matching regex and perform the range checking outside the regex. Something like:

    #!/usr/bin/perl use strict; use warnings; for my $test ('', '234-10:23', '0:0:1', '0:1', '24:60') { my $fail = parseTime($test); print "Failed parsing '$test': $fail\n" if $fail; } sub parseTime { my ($inputTxt) = @_; return "Bad input value" if !defined $inputTxt || !length $inputTx +t; $inputTxt =~ s/^\s+|\s+$//g; return "Bad time string" if $inputTxt !~ /^(?:(\d{1,3})-)?(?:(\d+):)?(\d+):(\d+)/; my ($days, $hours, $mins, $secs) = map {$_ || 0} $1, $2, $3, $4; return "Bad day count: $days" if $days > 366 && $days >= 0; return "Bad hour value: $hours" if $hours > 23 && $hours >= 0; return "Bad minutes value: $mins" if $mins > 59 && $mins >= 0; return "Bad seconds value: $secs" if $secs > 59 && $secs >= 0; my $totalSecs = ($days * 24 * 3600) + ($hours * 3600) + ($mins * 60) + $secs; print "$inputTxt - ${totalSecs}s\n"; return; }

    Prints:

    Failed parsing '': Bad input value 234-10:23 - 20218223s 0:0:1 - 1s 0:1 - 1s Failed parsing '24:60': Bad seconds value: 60
    True laziness is hard work

      Thanks very much for taking the time to provide detailed feedback as well as a much easier-to-test alternative.

      I thought that & was just the way you call subroutines, but I have more to learn. And I like the fact that by making it a subroutine you can much more easily test your code in place.

      Thanks again,
      Mike

Re: A regular expression that will work for (hh:)mm:ss [converting clock time to seconds]
by ambrus (Abbot) on Apr 13, 2012 at 07:26 UTC
Re: A regular expression that will work for (hh:)mm:ss [converting clock time to seconds]
by ambrus (Abbot) on Apr 13, 2012 at 07:39 UTC

    Try the Date::Manip module:

    use Date::Manip::Date; my $base = Date::Manip::Date->new; for my $deltastr ("32:05", "11:32:05") { $delta = $base->new_delta($deltastr); $deltasec = $delta->printf("%sys"); print "$deltastr => $deltasec s\n"; } =begin output 32:05 => 1925 s 11:32:05 => 41525 s =end output =cut

    Update: this is incorrect, for it doesn't work for times greater than a day that ps outputs, eg. "35-04:04:09". I guess there's no simpler workaround other than separating the fields (or at least the day field) manually using a regex.

Re: A regular expression that will work for (hh:)mm:ss [converting clock time to seconds]
by JavaFan (Canon) on Apr 13, 2012 at 09:10 UTC
    Here's a one-liner:
    ps -p $pid -o etime --no-heading|perl -aF'[:-]' -nE'say+pop(@F)+60*pop +(@F)+3600*pop(@F)+84600*pop@F'

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://964844]
Approved by GrandFather
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (9)
As of 2014-09-02 22:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (32 votes), past polls