Date substraction in perl?

Hello, I have a date something like
Aug 18 23:59:59 2012 GMT

Open in new window

and I would like to get is subtracted from the current GMT time which would tell me how many days are left. Please help me with perl one liner to achieve it. Thanks!
beer9Asked:
Who is Participating?

Improve company productivity with a Business Account.Sign Up

x
 
ozoConnect With a Mentor Commented:
perl -MDate::Manip -e 'print Delta_Format DCalc("now",ParseDate \@ARGV),0,"%st\n"' Aug 18 23:59:59 2012 GMT
0
 
beer9Author Commented:
Thanks a lot Ozo, is it possible without using any perl Module? The format of date is going to be in "Aug 18 23:59:59 2012 GMT" only. Thanks!
0
 
wilcoxonCommented:
I think this will do what you want.  It's definitely simpler to use Date::Manip or similar module.
#!/usr/local/bin/perl

use strict;
use warnings;

# setup month mapping
my %mon = (Jan=>1, Feb=>2, Mar=>3, Apr=>4, May=>5, Jun=>6, Jul=>7, Aug=>8, Sep=>9, Oct=>10, Nov=>11, Dec=>12);

# parse the date string
my $str = 'Aug 18 23:59:59 2012 GMT';
unless ($str =~ m{^(\w+)\s+(\d+)\s+\d+:\d+:\d+\s+(\d+)\s}) {
    die "could not parse date $str\n";
}
my @dt = ($2, $mon{$1}, $3);

my @gmt = (gmtime)[3,4,5];
$gmt[2] += 1900;
$gmt[1]++;

# days left in current month
my $days = days_in_mon($gmt[1], $gmt[2])-$gmt[0];
$gmt[1]++;
if ($gmt[1] > 12) {
    $gmt[1] -= 12;
    $gmt[2]++;
}
# days in next months/years
while ($gmt[1] < $dt[1] or $gmt[2] < $dt[2]) {
    $days += days_in_mon($gmt[1], $gmt[2]);
    $gmt[1]++;
    if ($gmt[1] > 12) {
        $gmt[1] -= 12;
        $gmt[2]++;
    }
}

print "days = $days\n";

sub days_in_mon {
    my ($mon, $yr) = @_;
    if ($mon == 2) {
        if ($yr % 4 == 0 and ($yr % 100 != 0 or $yr % 1000 == 0)) {
            return 29;
        } else {
            return 28;
        }
    } elsif ($mon =~ m{^(?:0?[13578]|1[02])$}) {
        return 31;
    } elsif ($mon =~ m{^(?:0?[469]|11)$}) {
        return 30;
    } else {
        die "could not determine days in month for $mon/$yr\n";
    }
}

Open in new window

0
Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

 
wilcoxonCommented:
Minor error - after line 35, add:

$days += $dt[0];

If the result ends up being off by 1 day then you can add "-1" to the end of the above line.

Let me know if it doesn't work for you or if there is an error in the calc.
0
 
wilcoxonConnect With a Mentor Commented:
I ran a few quick tests and found it is off in the other direction and I forgot to account for the case of the same month/year.  Here's the corrected code...

This also has the limit of only working correctly if the string date is in the future.
#!/usr/local/bin/perl

use strict;
use warnings;

# setup month mapping
my %mon = (Jan=>1, Feb=>2, Mar=>3, Apr=>4, May=>5, Jun=>6, Jul=>7, Aug=>8, Sep=>9, Oct=>10, Nov=>11, Dec=>12);

# parse the date string
my $str = 'Aug 18 23:59:59 2012 GMT';
unless ($str =~ m{^(\w+)\s+(\d+)\s+\d+:\d+:\d+\s+(\d+)\s}) {
    die "could not parse date $str\n";
}
my @dt = ($2, $mon{$1}, $3);

my @gmt = (gmtime)[3,4,5];
$gmt[2] += 1900;
$gmt[1]++;

# if same month/year
if ($dt[1] == $gmt[1] and $dt[2] == $gmt[2]) {
    my $days = $dt[0] - $gmt[0];
    print "days = $days\n";
    exit;
}

# days left in current month
my $days = days_in_mon($gmt[1], $gmt[2])-$gmt[0]+1;
$gmt[1]++;
if ($gmt[1] > 12) {
    $gmt[1] -= 12;
    $gmt[2]++;
}
# days in next months/years
while ($gmt[1] < $dt[1] or $gmt[2] < $dt[2]) {
    $days += days_in_mon($gmt[1], $gmt[2]);
    $gmt[1]++;
    if ($gmt[1] > 12) {
        $gmt[1] -= 12;
        $gmt[2]++;
    }
}
$days += $dt[0];

print "days = $days\n";

sub days_in_mon {
    my ($mon, $yr) = @_;
    if ($mon == 2) {
        if ($yr % 4 == 0 and ($yr % 100 != 0 or $yr % 1000 == 0)) {
            return 29;
        } else {
            return 28;
        }
    } elsif ($mon =~ m{^(?:0?[13578]|1[02])$}) {
        return 31;
    } elsif ($mon =~ m{^(?:0?[469]|11)$}) {
        return 30;
    } else {
        die "could not determine days in month for $mon/$yr\n";
    }
}

Open in new window

0
 
tel2Commented:
Hi ozo,

I've just tried your one-liner, but can't get it to work.  Chech this out:

    perl -Iperl/usr/lib/perl5/site_perl -MDate::Manip -e 'print Delta_Format DCalc("now",ParseDate \@ARGV),0,"%st\n"' Aug 18 23:59:59 2012 GMT

    Undefined subroutine &main::DCalc called at -e line 1.

Above is the error message I got after installing Date::Manip 6.21 (the latest version).

Any ideas what's wrong?

Thanks.
tel2
0
 
wilcoxonCommented:
I get the same error from ozo's one-liner.  DCalc doesn't appear to exist.  There is a DateCalc function but it doesn't return the number of days (46424837 is what I get today - number of seconds maybe?).  Delta_format has very poor documentation for its formats.
0
 
ozoCommented:
sorry, I don't know how DateCalc got turned into Dcalc
perl -MDate::Manip -e 'print Delta_Format DateCalc("now",ParseDate \@ARGV),0,"%dt\n"' Aug 18 23:59:59 2012 GMT
or
with only standard modules
perl -l -MTime::Local -e 'print((timegm((split":",$ARGV[2])[2,1,0],$ARGV[1],${{Jan=>1,Feb=>2,Mar=>3,Apr=>4,May=>5,Jun=>6,Jul=>7,Aug=>8,Sep=>9,Oct=>10,Nov=>11,Dec=>12}}{$ARGV[0]}-1,$ARGV[3]-1900)-time)/(60*60*24))' Aug 18 23:59:59 2012 GMT

0
 
wilcoxonCommented:
Sigh.  I hate typos.  I meant to try %dt earlier but typoed it to %sd (which clearly didn't work right).

Not unusual for an ozo answer - I learned something new (I didn't realize Time::Local was a standard module).

Obviously, either one of ozo's methods are probably better to use in production code but my solution works and will at least give you ideas on how date modules could do it behind the scenes...
0
 
tel2Commented:
It looks as if ozo's:
- Date::Manip solution reports in seconds, and takes $TZ into account,
- Time::Local solution reports in days, and ignores $TZ.
0
 
wilcoxonCommented:
See ozo's most recent comment - Date::Manip reports in days if you use %dt as the format.

The example has GMT for both timezones and the author doesn't indicate that there will ever be something else so it's reasonable to ignore timezone (my solution also ignores timezone).  Handling timezones is a major PITA without more advanced modules (not sure if Time::Local supports much as I've barely used it (usually using either Date::Calc or DateTime::TimeZone).
0
 
beer9Author Commented:
Thank you! :-)
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.