Solved

Date substraction in perl?

Posted on 2011-02-27
12
386 Views
Last Modified: 2012-05-11
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!
0
Comment
Question by:beer9
  • 6
  • 2
  • 2
  • +1
12 Comments
 
LVL 84

Accepted Solution

by:
ozo earned 250 total points
Comment Utility
perl -MDate::Manip -e 'print Delta_Format DCalc("now",ParseDate \@ARGV),0,"%st\n"' Aug 18 23:59:59 2012 GMT
0
 

Author Comment

by:beer9
Comment Utility
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
 
LVL 26

Expert Comment

by:wilcoxon
Comment Utility
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
 
LVL 26

Expert Comment

by:wilcoxon
Comment Utility
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
 
LVL 26

Assisted Solution

by:wilcoxon
wilcoxon earned 250 total points
Comment Utility
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
 
LVL 11

Expert Comment

by:tel2
Comment Utility
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
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 26

Expert Comment

by:wilcoxon
Comment Utility
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
 
LVL 84

Expert Comment

by:ozo
Comment Utility
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
 
LVL 26

Expert Comment

by:wilcoxon
Comment Utility
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
 
LVL 11

Expert Comment

by:tel2
Comment Utility
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
 
LVL 26

Expert Comment

by:wilcoxon
Comment Utility
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
 

Author Closing Comment

by:beer9
Comment Utility
Thank you! :-)
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

I've just discovered very important differences between Windows an Unix formats in Perl,at least 5.xx.. MOST IMPORTANT: Use Unix file format while saving Your script. otherwise it will have ^M s or smth likely weird in the EOL, Then DO NOT use m…
On Microsoft Windows, if  when you click or type the name of a .pl file, you get an error "is not recognized as an internal or external command, operable program or batch file", then this means you do not have the .pl file extension associated with …
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now