Solved

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

Posted on 2004-09-19
668 Views
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
Question by:divt
• 5
• 3
• 2
• +2

LVL 18

Assisted Solution

kandura earned 140 total points
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

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

"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

LVL 9

Expert Comment

thx kandura
excellent explanation
0

LVL 84

Expert Comment

return int(\$diff / 86400)
Could give the wrong answer if \$d1 and \$d2 cross a Daylight Saving Time transition.
0

LVL 18

Expert Comment

I'll leave that as an excercise for the reader ;)
0

LVL 18

Expert Comment

I'd like to see an example of that, ozo.
0

LVL 18

Expert Comment

daydiff('27/3/2004', '29/3/2004') = 1

(I'm in CET)

ok. better use timegm instead of timelocal.
0

LVL 5

Accepted Solution

ZiaTioN earned 180 total points
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

ozo earned 180 total points
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

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

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

Julian Day Number
0

## Featured Post

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…