Solved

Date substraction in perl?

Posted on 2011-02-27
12
397 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
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 26

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
 
LVL 26

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 26

Assisted Solution

by:wilcoxon
wilcoxon earned 250 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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 26

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 26

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 26

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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

Suggested Solutions

A year or so back I was asked to have a play with MongoDB; within half an hour I had downloaded (http://www.mongodb.org/downloads),  installed and started the daemon, and had a console window open. After an hour or two of playing at the command …
There are many situations when we need to display the data in sorted order. For example: Student details by name or by rank or by total marks etc. If you are working on data driven based projects then you will use sorting techniques very frequently.…
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 gives a high-level tour of the interface of Marketo (a marketing automation tool to help businesses track and engage prospective customers and drive them to purchase). You will see the main areas including Marketing Activities, Design …

896 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

13 Experts available now in Live!

Get 1:1 Help Now