Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Date substraction in perl?

Posted on 2011-02-27
12
Medium Priority
?
436 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 2
  • 2
  • +1
12 Comments
 
LVL 84

Accepted Solution

by:
ozo earned 1000 total points
ID: 34991133
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
ID: 34991250
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 27

Expert Comment

by:wilcoxon
ID: 34993804
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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 27

Expert Comment

by:wilcoxon
ID: 34993816
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 27

Assisted Solution

by:wilcoxon
wilcoxon earned 1000 total points
ID: 34993835
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 12

Expert Comment

by:tel2
ID: 35001359
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
 
LVL 27

Expert Comment

by:wilcoxon
ID: 35001446
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
ID: 35003074
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 27

Expert Comment

by:wilcoxon
ID: 35003321
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 12

Expert Comment

by:tel2
ID: 35003691
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 27

Expert Comment

by:wilcoxon
ID: 35003740
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
ID: 35059650
Thank you! :-)
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Email validation in proper way is  very important validation required in any web pages. This code is self explainable except that Regular Expression which I used for pattern matching. I originally published as a thread on my website : http://www…
I have been pestered over the years to produce and distribute regular data extracts, and often the request have explicitly requested the data be emailed as an Excel attachement; specifically Excel, as it appears: CSV files confuse (no Red or Green h…
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…
Six Sigma Control Plans

705 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