Subject: | DateTime::subtract() does not honor end_of_month |
DateTime::subtract() does not honor a given end_of_month parameter. This is because the
subtract() just makes a duration object and calls add_duration() with the inverse of that
duration object, which causes the end_of_month parameter tro get lost in the shuffle.
Fix attacted.
--
Chris Reinhardt
perl@triv.org
Subject: | subtract_eom_fix.diff |
diff --git a/lib/DateTime.pm b/lib/DateTime.pm
index bf97833..4c39a68 100644
--- a/lib/DateTime.pm
+++ b/lib/DateTime.pm
@@ -1630,8 +1630,15 @@ sub add {
sub subtract {
my $self = shift;
-
- return $self->subtract_duration( $self->duration_class->new(@_) );
+ my %p = @_;
+
+ my %extras;
+ $extras{end_of_month} = delete $p{end_of_month}
+ if exists $p{end_of_month};
+
+ my $duration = $self->duration_class->new( %p )->inverse( %extras );
+
+ return $self->add_duration( $duration );
}
sub subtract_duration { return $_[0]->add_duration( $_[1]->inverse ) }
diff --git a/lib/DateTime/Duration.pm b/lib/DateTime/Duration.pm
index bf82a1c..196c5ea 100644
--- a/lib/DateTime/Duration.pm
+++ b/lib/DateTime/Duration.pm
@@ -409,8 +409,8 @@ of the month the new date will also be. For instance, adding one
month to Feb 29, 2000 will result in Mar 31, 2000.
For positive durations, the "end_of_month" parameter defaults to wrap.
-For negative durations, the default is "limit". This should match how
-most people "intuitively" expect datetime math to work.
+For negative durations, the default is "preserve". This should match
+how most people "intuitively" expect datetime math to work.
=item * clone
diff --git a/t/10subtract.t b/t/10subtract.t
index 43d1c97..3260f70 100644
--- a/t/10subtract.t
+++ b/t/10subtract.t
@@ -427,4 +427,54 @@ use DateTime;
);
}
+{
+ my $dt1 = DateTime->new(
+ year => 2005, month => 2, day => 28,
+ time_zone => 'UTC'
+ );
+
+ my $dt2 = $dt1->clone->subtract(months => 1, end_of_month => 'wrap');
+
+ is($dt2->month, 1, 'feb 28 - 1 month in jan');
+ is($dt2->day, 28, 'and on the 28th with wrap');
+}
+
+
+{
+ my $dt1 = DateTime->new(
+ year => 2005, month => 2, day => 28,
+ time_zone => 'UTC'
+ );
+
+ my $dt2 = $dt1->clone->subtract(months => 1, end_of_month => 'limit');
+
+ is($dt2->month, 1, 'feb 28 - 1 month in jan');
+ is($dt2->day, 28, 'and on the 28th with limit');
+}
+
+
+{
+ my $dt1 = DateTime->new(
+ year => 2005, month => 2, day => 28,
+ time_zone => 'UTC'
+ );
+
+ my $dt2 = $dt1->clone->subtract(months => 1, end_of_month => 'preserve');
+
+ is($dt2->month, 1, 'feb 28 - 1 month in jan');
+ is($dt2->day, 31, 'and on the 31th with preserve');
+}
+
+{
+ my $dt1 = DateTime->new(
+ year => 2005, month => 2, day => 28,
+ time_zone => 'UTC'
+ );
+
+ my $dt2 = $dt1->clone->subtract(months => 1);
+
+ is($dt2->month, 1, 'feb 28 - 1 month in jan');
+ is($dt2->day, 31, 'and on the 31th with default (preserve)');
+}
+
done_testing();