Solved

calculate number of days between 2 dates (without external modules)

Posted on 2004-09-19
13
672 Views
Last Modified: 2007-12-19
Problem illustration:
All inputs in format 'dd/mm/yyyy'

sample input A:
$date1 = '21/10/2004';
$date2 = '25/10/2004';

sample output A:
4

sample input B:
$date1 = '21/10/2004';
$date2 = '21/10/2005';

sample output B:
365


sample input C:
$date1 = '21/10/2004';
$date2 = '21/10/2020';

sample output C:
5844

Without using third party modules (Date::Calc etc.)
Must calculate correctly with leap years, etc.
Does this sound like reinventing the wheel?

Opinions welcome.

0
Comment
Question by:divt
  • 5
  • 3
  • 2
  • +2
13 Comments
 
LVL 18

Assisted Solution

by:kandura
kandura earned 140 total points
ID: 12095077
Yes.

Would you object to using modules that are part of the perl distribution?

    use Time::Local;
    sub daydiff {
        my ($d1, $d2) = @_;
        my $diff = date2time($d2) - date2time($d1);
        return int($diff / 86400);
    }

    sub date2time {
        my $date = shift;
        my ($d, $m, $y) = split /\//, $date;
        return timelocal(0,0,0,$d, $m-1, $y);
    }
0
 
LVL 9

Expert Comment

by:ronan_40060
ID: 12095151
Hello Kandura
I didnt not get the logic of  return timelocal(0,0,0,$d, $m-1, $y);
cud u pls elaborate
thx
ronan
0
 
LVL 18

Expert Comment

by:kandura
ID: 12095191
"timelocal" is defined in Time::Local. It does the reverse of "localtime" in that it constructs a unix timestamp from date parts:

    $time = timelocal($sec,$min,$hour,$mday,$mon,$year);

So timelocal(localtime($time)) == $time.
0
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!

 
LVL 9

Expert Comment

by:ronan_40060
ID: 12095230
thx kandura
excellent explanation
0
 
LVL 84

Expert Comment

by:ozo
ID: 12095549
return int($diff / 86400)
Could give the wrong answer if $d1 and $d2 cross a Daylight Saving Time transition.
0
 
LVL 18

Expert Comment

by:kandura
ID: 12095600
I'll leave that as an excercise for the reader ;)
0
 
LVL 18

Expert Comment

by:kandura
ID: 12095621
I'd like to see an example of that, ozo.
0
 
LVL 18

Expert Comment

by:kandura
ID: 12096392
   daydiff('27/3/2004', '29/3/2004') = 1

(I'm in CET)

ok. better use timegm instead of timelocal.
0
 
LVL 5

Accepted Solution

by:
ZiaTioN earned 180 total points
ID: 12097855
Ok,

This should be what you are looking for! No Modules too.

#!/usr/bin/perl -w

use strict;

my $sDate = '01/01/2004';
my $eDate = '01/01/2020';

my @months = qw(Jan=31 Feb=28 Mar=31 Apr=30 May=31 Jun=30
                Jul=31 Aug=31 Sep=30 Oct=31 Nov=30 Dec=31);

my ($sMonth, $sDay, $sYear) = split(/\//, $sDate);
my ($eMonth, $eDay, $eYear) = split(/\//, $eDate);
my ($m, $mTotal)            = split(/\=/, $months[$sMonth-1]);

my $yRef = $sYear;
my $mRef = $sMonth;
my $i    = 0;

while ($yRef <= $eYear) {
   my $leap;
   my $lRef  = $yRef / 4;
   my $lRef2 = $yRef / 100;
   my $lRef3 = $yRef / 400;

   if ($lRef !~ /\./) {
      if (($lRef2 !~ /\./) && ($lRef3 =~ /\./)) {
         $leap = 0;
      }else{
         $leap = 1;
      }
   }else{
      $leap = 0;
   }

   if ($yRef == $eYear) {
      while ($mRef <= $eMonth) {
         $mTotal = 29 if (($m eq "Feb") && ($leap == 1));

         if ($mRef == $eMonth) {
            $i++, $sDay++, while($sDay < $eDay);
         }else{
            $i++, $sDay++ while($sDay <= $mTotal);
         }
     
         $sDay = 1;
         $mRef++;
         ($m, $mTotal) = split(/\=/, $months[$mRef-1]);
      }
   }else{
      while ($mRef < 12) {
         $mTotal = 29 if (($m eq "Feb") && ($leap == 1));

         $i++, $sDay++ while($sDay <= $mTotal);
         $sDay = 1;
         $mRef++;
         ($m, $mTotal) = split(/\=/, $months[$mRef-1]);
      }
      $mRef = 0;
   }
   $yRef++;
}
print "There are $i days between $sDate and $eDate\n";
0
 
LVL 84

Assisted Solution

by:ozo
ozo earned 180 total points
ID: 12099625
Using timegm (or return sprintf"%.f",$diff / 86400; with timelocal) seems the most sensible approach
but if you insist on using no modules at all, you could try:

sub JDN{
    my($d,$m,$y)=$_[0]=~/(\d\d)\D(\d\d)\D(\d\d\d\d)/ or die "bad date format";
    if( $m < 3 ){ $m += 12; $y -= 1; }
    use integer;
    return 1721088+$d+($m-2)*367/12+$y*365+$y/4-$y/100+$y/400;
}
print JDN('21/10/2005')-JDN('21/10/2004'),"\n";
0
 
LVL 5

Expert Comment

by:ZiaTioN
ID: 12102938
I don't think he was looking for golfed but excellent obfuscated solution. I doubt most people will be able to even follow it but I enjoyed it! :-)
0
 
LVL 4

Author Comment

by:divt
ID: 12106570
Well, you may enjoy it, but I had a hard time digesting answers from you all experts! :-)

All answers yield the correct results for the illustratted problem.  Thank you for showing me the flexibility of perl.

By the way, Ozo, what does JDN stand for?

0
 
LVL 84

Expert Comment

by:ozo
ID: 12107140
Julian Day Number
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Suggested Solutions

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

679 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