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