Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
Hi Monks,
I want to check if two given dates have a difference of maximum 6 months. I go about like this:
use Date::Calc qw(:all);
use strict;
my @data_date_confirm=();
my @data_date_birth=();
my $diab_confirm = '29/08/2013';
my $date_birth = '21/02/2013';
if($diab_confirm=~/(\d+)\/(\d+)\/(\d+)/)
{
my $day_confirm = $1;
my $month_confirm = $2;
my $year_confirm = $3;
@data_date_confirm = ($year_confirm, $month_confirm, $day_conf
+irm);
}
if($date_birth=~/(\d+)\/(\d+)\/(\d+)/)
{
my $day_birth = $1;
my $month_birth = $2;
my $year_birth = $3;
@data_date_birth = ($year_birth, $month_birth, $day_birth);
}
my $dd = Delta_Days(@data_date_birth, @data_date_confirm);
my $months_until_confirm = sprintf("%.1f", $dd/30);
if($months_until_confirm<=6)
{
print "They are different by more than 6 months!\n";
}
I realise though that this is not entirely correct, since I divide by 30 days. Is it a way to be more precise?
Thank you!
Re: More accurate way to calculate Date difference
by haukex (Archbishop) on Aug 11, 2018 at 08:05 UTC
|
It's important to be clear on definitions and provide meaningful examples. For example, sometimes a "month" is defined as exactly 30 days, but since you're asking this question, I'm guessing that's not what you want. For example, using DateTime math:
2016-01-29 + 30 days = 2016-02-28 2017-01-29 + 30 days = 2017-02-28
2016-01-30 + 30 days = 2016-02-29 2017-01-30 + 30 days = 2017-03-01
vs.
2016-01-29 + 1 month = 2016-02-29 2017-01-29 + 1 month = 2017-03-01
2016-01-30 + 1 month = 2016-03-01 2017-01-30 + 1 month = 2017-03-02
I would recommend this: First, calculate the "deadline" date by adding your "6 months" to the $date_birth. Then, use the methods provided by the library to see if the $diab_confirm is past that date or not. For example, in Date::Calc that would probably be Add_Delta_YM for the first part, and Date_to_Days or Delta_Days for the second.
Personally, I like DateTime, and its objects overload the comparison operators:
use warnings;
use strict;
use DateTime;
use DateTime::Format::Strptime;
my $date_birth = '21/02/2013';
my $diab_confirm = '29/08/2013';
my $strp = DateTime::Format::Strptime->new(on_error=>'croak',
pattern => '%d/%m/%Y');
my $dt_birth = $strp->parse_datetime($date_birth);
my $dt_confirm = $strp->parse_datetime($diab_confirm);
print " birth: ", $dt_birth->ymd, "\n";
print " confirm: ", $dt_confirm->ymd, "\n";
my $dt_deadline = $dt_birth->clone->add(months=>6);
print "deadline: ", $dt_deadline->ymd, "\n";
if ($dt_confirm > $dt_deadline) {
print $dt_confirm->ymd, " > ", $dt_deadline->ymd, "\n";
}
else {
print $dt_confirm->ymd, " <= ", $dt_deadline->ymd, "\n";
}
__END__
birth: 2013-02-21
confirm: 2013-08-29
deadline: 2013-08-21
2013-08-29 > 2013-08-21
As always, the more test cases the better! | [reply] [d/l] [select] |
Re: More accurate way to calculate Date difference
by hippo (Bishop) on Aug 11, 2018 at 09:20 UTC
|
use strict;
use warnings;
use Time::Piece;
use Test::More;
my $fmt = '%d/%m/%Y';
my $date = Time::Piece->strptime ('21/02/2013', $fmt);
my @within = ('12/08/2013');
my @without = ('29/08/2013');
my $mon = 6;
plan tests => @within + @without;
for my $other (@within) {
ok (within_months ($mon, $date, Time::Piece->strptime ($other, $fm
+t)),
"$other is within $mon months of $date");
}
for my $other (@without) {
ok (! within_months ($mon, $date, Time::Piece->strptime ($other, $
+fmt)),
"$other is not within $mon months of $date");
}
sub within_months {
my ($mon, @d) = @_;
if ($d[0] > $d[1]) { push @d, shift @d };
my $start = $d[0]->add_months ($mon);
return $start > $d[1];
}
Feel free to expand the @within and @without arrays with more test cases. You can also change $mon to whatever number you require. Time::Piece is in core so there are no extra dependencies needed here. | [reply] [d/l] [select] |
Re: More accurate way to calculate Date difference
by Athanasius (Archbishop) on Aug 11, 2018 at 07:46 UTC
|
use strict;
use warnings;
use DateTime;
my $dt1 = DateTime->new
(
year => 2013,
month => 9,
day => 29,
);
my $dt2 = DateTime->new
(
year => 2013,
month => 3,
day => 29,
);
my $dt3 = DateTime->new
(
year => 2013,
month => 3,
day => 30,
);
my $dur1 = $dt2->subtract_datetime($dt1);
printf "29/03/2013 to 29/09/2013: %s months\n", $dur1->months;
my $dur2 = $dt3->subtract_datetime($dt1);
printf "30/03/2013 to 29/09/2013: %s months\n", $dur2->months;
Output:
17:44 >perl 1919_SoPW.pl
29/03/2013 to 29/09/2013: 6 months
30/03/2013 to 29/09/2013: 5 months
17:44 >
Update: Please see the correction by haukex, below.
Hope that helps,
| [reply] [d/l] [select] |
|
Unfortunately, one has to be careful with DateTime::Duration objects' accessors: ->month only returns the "months" component of the difference. You usually want ->in_units instead, but note the limitations of conversion - for example, you can't convert a number of months to a number of days without knowing which months we're talking about (and that information is no longer available if we only have a duration), so you have to get both the months and days, and in_units will do the conversion from years to months for you.
use warnings;
use strict;
use DateTime;
my $dt1 = DateTime->new(year => 2013, month => 10, day => 1);
my $dt2 = DateTime->new(year => 2018, month => 3, day => 31);
my $dur1 = $dt2->subtract_datetime($dt1);
print $dt1->ymd, " to ", $dt2->ymd, ": ",
$dur1->months, " months - oops!\n";
my ($days,$months) = $dur1->in_units('days','months');
print $dt1->ymd, " to ", $dt2->ymd, ": ",
"$months months, $days days", "\n";
__END__
2013-10-01 to 2018-03-31: 5 months - oops!
2013-10-01 to 2018-03-31: 53 months, 30 days
| [reply] [d/l] [select] |
|
That is great!
Thank you so much!
| [reply] |
Re: More accurate way to calculate Date difference
by choroba (Cardinal) on Aug 11, 2018 at 08:03 UTC
|
See a recent question here: Date comparison
($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord
}map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
| [reply] [d/l] |
|
|