diff --git a/lib/modules/Calendar.pmod/YMD.pike b/lib/modules/Calendar.pmod/YMD.pike index b9a8ef7f5fb346c1c2102b2b2898f56cde21eb10..47f193a1b47b118627489797fc27f2061db21da9 100644 --- a/lib/modules/Calendar.pmod/YMD.pike +++ b/lib/modules/Calendar.pmod/YMD.pike @@ -2226,11 +2226,27 @@ class cDay static TimeRange _move(int x,YMD step) { - if (step->is_year) - return year()->add(x,step)->place(this,1); + if (step->is_year) { + TimeRange stepped = year()->add(x,step); + if (TimeRange placed = stepped->place(this,0)) + return placed; + // If we couldn't place our day in the target year it means + // we're on a leap day and the target year doesn't have any. + // We return the closest day in the same month. + TimeRange placed = stepped->place (month()); + if (md == CALUNKNOWN) make_month(); + return placed->day (md < placed->number_of_days() ? md : -1); + } - if (step->is_month) - return month()->add(x,step)->place(this,1); + if (step->is_month) { + TimeRange stepped = month()->add(x,step); + if (TimeRange placed = stepped->place(this,0)) + return placed; + // The target month is shorter and our date doesn't exist in + // it. We return the closest (i.e. last) day of the target + // month. + return stepped->day (-1); + } if (step->is_week) return week()->add(x,step)->place(this,1); diff --git a/lib/modules/Calendar.pmod/testsuite.in b/lib/modules/Calendar.pmod/testsuite.in index 838c55fe2608c31fef52341a768dcf236755c63f..8ddfd616540a7cba62edf6486e728fb3fd837ee7 100644 --- a/lib/modules/Calendar.pmod/testsuite.in +++ b/lib/modules/Calendar.pmod/testsuite.in @@ -1,5 +1,5 @@ START_MARKER -dnl $Id: testsuite.in,v 1.15 2008/01/23 15:23:54 mast Exp $ +dnl $Id: testsuite.in,v 1.16 2008/02/05 21:34:47 mast Exp $ dnl NOTE: dnl *every* time the Calendar tests have failed, it's not the @@ -139,4 +139,22 @@ test_eq([[Calendar.ISO.Week (2008, 1)->set_size (Calendar.ISO.Day())->year_no()] test_eq([[Calendar.ISO.Week (2008, 1)->year_no()]], 2008) test_eq([[Calendar.ISO.Year (Calendar.ISO.Week (2008, 1))->year_no()]], 2008) +test_eq([[Calendar.ISO.Day (2007, 5, 31)->add (1, Calendar.ISO.Month())]], Calendar.ISO.Day (2007, 6, 30)) +test_eq([[Calendar.ISO.Day (2007, 5, 31)->add (2, Calendar.ISO.Month())]], Calendar.ISO.Day (2007, 7, 31)) +test_eq([[Calendar.ISO.Day (2007, 5, 31)->add (1, Calendar.ISO.Month())->add (1, Calendar.ISO.Month())]], Calendar.ISO.Day (2007, 7, 30)) +test_eq([[Calendar.ISO.Day (2007, 5, 31)->add (-1, Calendar.ISO.Month())]], Calendar.ISO.Day (2007, 4, 30)) +test_eq([[Calendar.ISO.Day (2007, 5, 31)->add (-2, Calendar.ISO.Month())]], Calendar.ISO.Day (2007, 3, 31)) +test_eq([[Calendar.ISO.Day (2007, 5, 31)->add (-1, Calendar.ISO.Month())->add (1, Calendar.ISO.Month())]], Calendar.ISO.Day (2007, 5, 30)) + +test_eq([[Calendar.ISO.Day (1900, 1, 31)->add (1, Calendar.ISO.Month())]], Calendar.ISO.Day (1900, 2, 28)) +test_eq([[Calendar.ISO.Day (2000, 1, 31)->add (1, Calendar.ISO.Month())]], Calendar.ISO.Day (2000, 2, 29)) +test_eq([[Calendar.ISO.Day (2004, 1, 31)->add (1, Calendar.ISO.Month())]], Calendar.ISO.Day (2004, 2, 29)) + +test_eq([[Calendar.ISO.Day (2004, 2, 29)->add (1, Calendar.ISO.Year())]], Calendar.ISO.Day (2005, 2, 28)) +test_eq([[Calendar.ISO.Day (2004, 2, 29)->add (4, Calendar.ISO.Year())]], Calendar.ISO.Day (2008, 2, 29)) +test_eq([[Calendar.ISO.Day (2004, 2, 29)->add (2, Calendar.ISO.Year())->add (2, Calendar.ISO.Year())]], Calendar.ISO.Day (2008, 2, 28)) +test_eq([[Calendar.ISO.Day (2004, 2, 29)->add (-1, Calendar.ISO.Year())]], Calendar.ISO.Day (2003, 2, 28)) +test_eq([[Calendar.ISO.Day (2004, 2, 29)->add (-4, Calendar.ISO.Year())]], Calendar.ISO.Day (2000, 2, 29)) +test_eq([[Calendar.ISO.Day (2004, 2, 29)->add (-2, Calendar.ISO.Year())->add (2, Calendar.ISO.Year())]], Calendar.ISO.Day (2004, 2, 28)) + END_MARKER