Solved

Date substraction in perl?

Posted on 2011-02-27
12
407 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
Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

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

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

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

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…
Checking the Alert Log in AWS RDS Oracle can be a pain through their user interface.  I made a script to download the Alert Log, look for errors, and email me the trace files.  In this article I'll describe what I did and share my script.
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 is used to tweak the memory usage for your computer, it is used for servers more so than workstations but just be careful editing registry settings as it may cause irreversible results. I hold no responsibility for anything you do to the regist…

813 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

11 Experts available now in Live!

Get 1:1 Help Now