#!/usr/bin/perl -w # This script is released under the GNU GPL 2, copyright (c) 2005 by Adam Katz # see timecalc_help function below for description -Adam use strict; my $tcPROGRAM = "timecalc"; my $tcVERSION = "0.4"; sub timecalc_usage { return "Usage: $tcPROGRAM [OPTIONS] [TIME|SECONDS]"; } my $min = 60; my $hour = 60*$min; my $day = 24*$hour; my $year = 365*$day; sub timecalc_help { print "Convert time between colon-delimited units and numbers of seconds.\n"; print timecalc_usage(); print ' TIME Colon-separated time including seconds or a specified smallest unit Format is Y:D:H:M:S, 365.0 days/year (ignores leap years) SECONDS A number of seconds The first line of standard input is used if no other input is given OPTIONS --time Output colon-delimited time regardless of input --seconds Output time in seconds regardless of input Units indicate smallest value; 3h & 3:00m & 3:00:00 will all return 10800 Returns: opposite of argument (TIME->SECONDS, SECONDS->TIME) Valid Range: 0-2147483647 or 0-68:35:03:14:07 (assuming 32bit limit) 64bit Max: 9223372036854775807 or 292471208677:195:15:30:07 (9.22Es, 292Gy)'; print "\n\n"; timecalc_version(); # Disclaimer - I am a /bin/sh scripter, this is one of my first perl scripts # timecalc 0.2 was written in /bin/sh ... in less code, though this is faster exit 0; } sub timecalc_version { print "$tcPROGRAM $tcVERSION, "; print 'Copyright (c) 2005 by Adam Katz , GPL'; print "\n"; exit 0; } # test_timecalc: string|number number|string -> boolean # takes in an argument for timecalc and reports if its result was the other arg sub test_timecalc { my($value,$answer,$option) = @_; my $result = ""; if (defined($option)) { $result = timecalc($option,$value); } else { $result = timecalc($value); } if ($result =~ /^$answer$/) { return 1; } print STDERR "Test of $value failed. "; print STDERR "Computed $result, should be $answer.\n"; return 0; } # timecalc_testsuite: VOID -> VOID # Test suite for timecalc, prints tests that failed or reports that none failed sub timecalc_testsuite { # multiply to prevent short-circuit (which would only show first failure) if( test_timecalc(5,"0:05") * test_timecalc("0:66",66) * test_timecalc("67s","1:07") * test_timecalc("1:30",90) * test_timecalc("2h",7200) * test_timecalc("3d",259200) * test_timecalc(1440,"24:00") * test_timecalc("3:00:89",10889) * test_timecalc("-1:4:93","-" . timecalc("1:4:93")) * test_timecalc(-126410465,"-4:3:02:01:05") * test_timecalc("65.5h","235800") * test_timecalc("65:30m","235800") * test_timecalc("5595","5595","-s") * test_timecalc("55:95","56:35","-t") * test_timecalc("55:95m","2:08:35:00","-t") * test_timecalc("5595","1:33:15","--time") ) { print "All tests completed successfully.\n"; } else { print "At least one test failed.\n"; } } # timecalc_err: string -> VOID # report that string is bad, suggest help sub timecalc_err { print STDERR "$tcPROGRAM: `@_' is neither a time nor a number of seconds.\n"; print STDERR timecalc_usage(); print STDERR "Try `$tcPROGRAM --help' for more information.\n"; } sub timecalc_units { my ($time,$multiplier) = @_; my $unit = $time; $unit =~ s/^.*(.$)/$1/; $time =~ s/^(.*).$/$1/; # switch on unit ($unit =~ /y/i) ? $multiplier = $year : ($unit =~ /w/i) ? $multiplier = ($day*7) # buggy easter-egg : ($unit =~ /d/i) ? $multiplier = $day : ($unit =~ /h/i) ? $multiplier = $hour : ($unit =~ /m/i) ? $multiplier = $min : ($unit =~ /s/i) ? 1 # default is seconds, no need for multiplier : print STDERR "ignoring unknown unit `$unit'\n"; return ($time,$multiplier); } sub time2sec { my($time,$multiplier) = @_; my $temp = $time; $time = 0; foreach my $field (1,$min,$hour,$day,$year) { if ($multiplier > $field) { next; } # skip units below the one specified $_ = $temp; s/.*?([^:]+)$/$1/; $temp =~ s/^(.*?):?[^:]+$/$1/; if ($_ =~ /./) { $time = $time + ($field * $_); } } if($temp =~ /./) { print STDERR "Ignored fields larger than years, $temp\n"; } return $time; } sub sec2time { my($time,$multiplier) = @_; $time = $time * $multiplier; # use specified units my ($years,$days,$hours,$mins,$secs); $time = $time - $year*($years = int($time/$year)); $time = $time - $day *($days = int($time/$day)); $time = $time - $hour*($hours = int($time/$hour)); $time = $time - $min *($mins = int($time/$min)); $time = "$years:$days--$hours--$mins--$time-"; # double-colons break perl $time =~ s/-([0-9]-)/-0$1/g; # pad values, needs doubles to parse $time =~ s/-+/:/g; # dash(es)->colon $time =~ s/^[0:]*(.+:.+?):*$/$1/; # remove lead zeros, lead/tail colons return $time; } # timecalc: string|number -> number|string # Convert a colon-separated time to its value in seconds or vice-versa, units ok sub timecalc { my $OUTPUT = ""; my($time,$option) = @_; # OPTIONS if ($time =~ /^-+help/) { timecalc_help(); exit 0; } # --help elsif ($time =~ /^-+v(er|$)/i) { timecalc_version(); exit 0; } # --version elsif ($time =~ /^-+test/) { timecalc_testsuite(); } # --test if (defined($option) && $time =~ /^-+[tsh]/) { if ($time =~ /^-+t([^e]|$)/) { $OUTPUT = "time"; } elsif ($time =~ /^-+s/) { $OUTPUT = "seconds"; } $time = $option; } elsif ($time =~ /^-+test/) { exit 0; } # quit on --test w/out more input # use the first line of input if no data was passed from the command line if (!defined($ARGV[0])) { $ARGV[0] = ; } my $multiplier = 1; # units/second, default unit is seconds my $neg = ""; # negative values are noted, removed, and prepended to end result if ($time =~ /^-[0-9]/) { $neg="-"; $time =~ s/^-(.*)$/$1/; } # only operate on valid input if ($time =~ /^[0-9:.]+[ywdhms]?$/i) { # UNITS if($time =~ /[a-zA-Z]$/) { ($time,$multiplier) = timecalc_units($time,$multiplier); } # TIME -> SECONDS if ($multiplier > 1 || $time =~ /:/) { $time = time2sec($time,$multiplier); if ($OUTPUT =~ /time/) { $time = sec2time($time,1); } # --time forced } # SECONDS -> TIME elsif ($time =~ /^[0-9.]*$/) { $time = sec2time($time,$multiplier); if ($OUTPUT =~ /seconds/) { $time = time2sec($time,1); }# --seconds forced } } # INVALID INPUT else { timecalc_err($time); exit 1; } return "$neg$time"; } ################################## # the above could be a library if we rip out the following # and put it in a script with a Require line referencing the library $tcPROGRAM=$0; $tcPROGRAM =~ s/^.*?\/([^\/]+)$/$1/; print timecalc(@ARGV) . "\n";