Time difference in seconds
Asked Answered
B

6

12

In a Perl program I have a variable containing date / time in this format:

Feb 3 12:03:20  

I need to determine if that date is more than x seconds old (based on current time), even if this occurs over midnight (e.g. Feb 3 23:59:00 with current time = Feb 4 00:00:30).

The perl date / time information I've found is mind-boggling. Near as I can tell I need to use Date::Calc, but I am not finding a seconds-delta. Thanks :)

Bookrack answered 3/2, 2012 at 20:1 Comment(0)
D
3

In perl, there is always more than one way to do something. Here's one which uses only a module that comes standard with Perl:

#! perl -w

use strict;
use Time::Local;

my $d1 = "Feb 3 12:03:20";
my $d2 = "Feb 4 00:00:30";

# Your date formats don't include the year, so
# figure out some kind of default.
use constant Year => 2012;


# Convert your date strings to Unix/perl style time in seconds
# The main problems you have here are:
# * parsing the date formats
# * converting the month string to a number from 1 to 11
sub convert
{
    my $dstring = shift;

    my %m = ( 'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3,
            'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7,
            'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11 );

    if ($dstring =~ /(\S+)\s+(\d+)\s+(\d{2}):(\d{2}):(\d{2})/)
    {
        my ($month, $day, $h, $m, $s) = ($1, $2, $3, $4, $5);
        my $mnumber = $m{$month}; # production code should handle errors here

        timelocal( $s, $m, $h, $day, $mnumber, Year - 1900 );
    }
    else
    {
        die "Format not recognized: ", $dstring, "\n";
    }
}

my $t1 = convert($d1);
my $t2 = convert($d2);

print "Diff (seconds) = ", $t2 - $t1, "\n";

To make this really production-ready, it needs better handling of the year (for example, what happens when the start date is in December and end date in January?) and better error handling (for example, what happens if the 3-char month abbreviation is mispelled?).

Denial answered 3/2, 2012 at 20:20 Comment(4)
@xivix, it's unnecessarily complicated for what you want. vmpstr's answer is totally workable here.Triserial
It all depends on how many modules you can or will use. See the answer by @vmpstr, using Date::Parse. If installing that module is acceptable, go for it. It's likely doing a better work of handling different data formats and possible errors than this short snippet of code.Denial
I like this code snippet, simple and workable without additional dependency .Divertimento
vmpstr's answer is just 2 lines, and uses a robust CPAN module, which to me is a much better approach.Nystatin
N
21
#!/usr/bin/perl

my $Start = time();
sleep 3;
my $End = time();
my $Diff = $End - $Start;

print "Start ".$Start."\n";
print "End ".$End."\n";
print "Diff ".$Diff."\n";

This is a simple way to find the time difference in seconds.

Newborn answered 14/6, 2013 at 12:20 Comment(3)
This doesn't answer the question at all, which is about comparing times in two variables.Nystatin
True, BUT it allows ... a.) the person who asked the original question to consider a different approach, which may be simpler than his/her original way; b.) other people finding this page through a web search to easily measure the seconds passed. I belong to the latter, and I very much like Sjoerd's example. Simple, and has no dependencies. Just what I was looking for.Gory
and of course solves my problem after 3 seconds googling , so thank you ! BTW ... one guru told me that the proper var names should be : start_time , stop_time and begin_time -> end_time ...Unhesitating
C
9

There seems to be a convenient Date::Parse. Here's the example:

use Date::Parse;

print str2time ('Feb 3 12:03:20') . "\n";

And here's what it outputs:

$ perl test.pl
1328288600

which is: Fri Feb 3 12:03:20 EST 2012

I'm not sure how decent the parsing is, but it parses your example just fine :)

Connelly answered 3/2, 2012 at 20:7 Comment(3)
how would I get the current date into that same format for comparison? localtime(time) gives a much longer number.Bookrack
@xivix: You don't need localtime, just use time: it returns a Unix timestamp (seconds since 1 Jan 1970 00:00:00 UTC), just like str2time.Chough
Fortunately for your needs, perl uses the Unix convention of measuring time in seconds since 1 Jan 1970, so the output of str2parse above is already in seconds, and you can do the math.Denial
B
6

In the spirit of TMTOWTDI, you can leverage the core Time::Piece :

#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
my $when = "@ARGV" or die "'Mon Day HH:MM:SS' expected\n";
my $year = (localtime)[5] + 1900;
my $t = Time::Piece->strptime( $year . q( ) . $when, "%Y %b %d %H:%M:%S" );
print "delta seconds = ", time() - $t->strftime("%s"),"\n";

$ ./mydelta Feb 3 12:03:20

delta seconds = 14553

The current year is assumed and taken from your localtime.

Buine answered 3/2, 2012 at 21:7 Comment(3)
Not sure why, but $t->epoch works while $t->strftime("%s") doesn'tYann
@Yann What version of Time::Piece are you using? It looks like 1.20 added strftime support for %s.Buine
$t->epoch seems to use some localization setting which I'm not aware of. In my case, epoch is returning EDT time zone value, while I'm actually in JST (Japan time) time. However, strftime("%s") returns the correct value in seconds (JST), which is consistent with time().Disappearance
D
3

In perl, there is always more than one way to do something. Here's one which uses only a module that comes standard with Perl:

#! perl -w

use strict;
use Time::Local;

my $d1 = "Feb 3 12:03:20";
my $d2 = "Feb 4 00:00:30";

# Your date formats don't include the year, so
# figure out some kind of default.
use constant Year => 2012;


# Convert your date strings to Unix/perl style time in seconds
# The main problems you have here are:
# * parsing the date formats
# * converting the month string to a number from 1 to 11
sub convert
{
    my $dstring = shift;

    my %m = ( 'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3,
            'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7,
            'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11 );

    if ($dstring =~ /(\S+)\s+(\d+)\s+(\d{2}):(\d{2}):(\d{2})/)
    {
        my ($month, $day, $h, $m, $s) = ($1, $2, $3, $4, $5);
        my $mnumber = $m{$month}; # production code should handle errors here

        timelocal( $s, $m, $h, $day, $mnumber, Year - 1900 );
    }
    else
    {
        die "Format not recognized: ", $dstring, "\n";
    }
}

my $t1 = convert($d1);
my $t2 = convert($d2);

print "Diff (seconds) = ", $t2 - $t1, "\n";

To make this really production-ready, it needs better handling of the year (for example, what happens when the start date is in December and end date in January?) and better error handling (for example, what happens if the 3-char month abbreviation is mispelled?).

Denial answered 3/2, 2012 at 20:20 Comment(4)
@xivix, it's unnecessarily complicated for what you want. vmpstr's answer is totally workable here.Triserial
It all depends on how many modules you can or will use. See the answer by @vmpstr, using Date::Parse. If installing that module is acceptable, go for it. It's likely doing a better work of handling different data formats and possible errors than this short snippet of code.Denial
I like this code snippet, simple and workable without additional dependency .Divertimento
vmpstr's answer is just 2 lines, and uses a robust CPAN module, which to me is a much better approach.Nystatin
A
2

Assuming you want to use Date::Calc, convert the two values to "time" values with Date_to_Time and subtract the values to get the difference in seconds. But to do this, you need to convert from the strings to YY MM DD hh mm ss values to pass to Date_to_Time first.

Aleras answered 3/2, 2012 at 20:9 Comment(0)
G
0

There are some cases where you absolutely cannot import any third party package. One of these instances: online code sandbox websites. After checking ten different Perl sandboxes, I couldn't find a single one that would allow use statements. Unless you full have system-admin permissions to a Perl environment that's entirely at your whim, even testing any of these answers above will be tricky!

Package-Free Solution

Here is a solution that works works without any package dependencies and remains lightweight and simple...

Full Working Demo

sub getTimeDiff {
    my ($times) = @_;
    
    my @times = sort {$a cmp $b} @{$times};
    my ($endhour, $endminute, $endsecond, $starthour, $startminute, $startsecond) = (split(':', $times[0]), split(':', $times[1]));
    my $diff = ($starthour * 60 * 60) + ($startminute * 60) + ($startsecond) - ($endhour * 60 * 60) - ($endminute * 60) - ($endsecond);
    
    return $diff;
}

print(getTimeDiff(["13:00:00", "13:35:17"]));      // output: 2117 (seconds)

Explanation

There are three operations happening here:

  1. Sort the times using alpha cmp sort, so that the endtime is in the [0]'th position and the start time in the [1]'th position.
  2. Split the start/end times into pieces.
  3. Calculate the diff using basic arithmetic.

Avoid Package-Based or CPAN-Based Solutions

Why do it this way? Easy, it's not even 1kb of code. Look at some of the file sizes in these packages! That chews through diskspace and memory! If you have something simple to just calculate the time diff, it might be faster to just use your own function.

Date::Manip FileSizes

What's worse than that? Even understanding how these packages work!

use Date::Manip;
$ret = Date::Manip::DateCalc("13:00:00","13:30:00",0).'';
$ret = Date::Manip::DateCalc("00:00:00", $ret,0).'';
$ret = UnixDate($ret, '%I:%M:%S');
print("Date::Manip Result: " . $ret);

According to Date::Manip, what's the difference between "13:00:00" and "13:30:00"? It should be obvious to everyone!

Date::Manip Result: 12:30:00

So, there are difficulties in these packages with the problem of basic addition. I'm really not sure what else to say.

Gratia answered 13/11, 2020 at 18:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.