1# -*- coding: utf-8 -*- 2from __future__ import unicode_literals 3from ._common import unittest, WarningTestMixin, NotAValue 4 5import calendar 6from datetime import datetime, date, timedelta 7 8from dateutil.relativedelta import * 9 10 11class RelativeDeltaTest(WarningTestMixin, unittest.TestCase): 12 now = datetime(2003, 9, 17, 20, 54, 47, 282310) 13 today = date(2003, 9, 17) 14 15 def testInheritance(self): 16 # Ensure that relativedelta is inheritance-friendly. 17 class rdChildClass(relativedelta): 18 pass 19 20 ccRD = rdChildClass(years=1, months=1, days=1, leapdays=1, weeks=1, 21 hours=1, minutes=1, seconds=1, microseconds=1) 22 23 rd = relativedelta(years=1, months=1, days=1, leapdays=1, weeks=1, 24 hours=1, minutes=1, seconds=1, microseconds=1) 25 26 self.assertEqual(type(ccRD + rd), type(ccRD), 27 msg='Addition does not inherit type.') 28 29 self.assertEqual(type(ccRD - rd), type(ccRD), 30 msg='Subtraction does not inherit type.') 31 32 self.assertEqual(type(-ccRD), type(ccRD), 33 msg='Negation does not inherit type.') 34 35 self.assertEqual(type(ccRD * 5.0), type(ccRD), 36 msg='Multiplication does not inherit type.') 37 38 self.assertEqual(type(ccRD / 5.0), type(ccRD), 39 msg='Division does not inherit type.') 40 41 def testMonthEndMonthBeginning(self): 42 self.assertEqual(relativedelta(datetime(2003, 1, 31, 23, 59, 59), 43 datetime(2003, 3, 1, 0, 0, 0)), 44 relativedelta(months=-1, seconds=-1)) 45 46 self.assertEqual(relativedelta(datetime(2003, 3, 1, 0, 0, 0), 47 datetime(2003, 1, 31, 23, 59, 59)), 48 relativedelta(months=1, seconds=1)) 49 50 def testMonthEndMonthBeginningLeapYear(self): 51 self.assertEqual(relativedelta(datetime(2012, 1, 31, 23, 59, 59), 52 datetime(2012, 3, 1, 0, 0, 0)), 53 relativedelta(months=-1, seconds=-1)) 54 55 self.assertEqual(relativedelta(datetime(2003, 3, 1, 0, 0, 0), 56 datetime(2003, 1, 31, 23, 59, 59)), 57 relativedelta(months=1, seconds=1)) 58 59 def testNextMonth(self): 60 self.assertEqual(self.now+relativedelta(months=+1), 61 datetime(2003, 10, 17, 20, 54, 47, 282310)) 62 63 def testNextMonthPlusOneWeek(self): 64 self.assertEqual(self.now+relativedelta(months=+1, weeks=+1), 65 datetime(2003, 10, 24, 20, 54, 47, 282310)) 66 67 def testNextMonthPlusOneWeek10am(self): 68 self.assertEqual(self.today + 69 relativedelta(months=+1, weeks=+1, hour=10), 70 datetime(2003, 10, 24, 10, 0)) 71 72 def testNextMonthPlusOneWeek10amDiff(self): 73 self.assertEqual(relativedelta(datetime(2003, 10, 24, 10, 0), 74 self.today), 75 relativedelta(months=+1, days=+7, hours=+10)) 76 77 def testOneMonthBeforeOneYear(self): 78 self.assertEqual(self.now+relativedelta(years=+1, months=-1), 79 datetime(2004, 8, 17, 20, 54, 47, 282310)) 80 81 def testMonthsOfDiffNumOfDays(self): 82 self.assertEqual(date(2003, 1, 27)+relativedelta(months=+1), 83 date(2003, 2, 27)) 84 self.assertEqual(date(2003, 1, 31)+relativedelta(months=+1), 85 date(2003, 2, 28)) 86 self.assertEqual(date(2003, 1, 31)+relativedelta(months=+2), 87 date(2003, 3, 31)) 88 89 def testMonthsOfDiffNumOfDaysWithYears(self): 90 self.assertEqual(date(2000, 2, 28)+relativedelta(years=+1), 91 date(2001, 2, 28)) 92 self.assertEqual(date(2000, 2, 29)+relativedelta(years=+1), 93 date(2001, 2, 28)) 94 95 self.assertEqual(date(1999, 2, 28)+relativedelta(years=+1), 96 date(2000, 2, 28)) 97 self.assertEqual(date(1999, 3, 1)+relativedelta(years=+1), 98 date(2000, 3, 1)) 99 self.assertEqual(date(1999, 3, 1)+relativedelta(years=+1), 100 date(2000, 3, 1)) 101 102 self.assertEqual(date(2001, 2, 28)+relativedelta(years=-1), 103 date(2000, 2, 28)) 104 self.assertEqual(date(2001, 3, 1)+relativedelta(years=-1), 105 date(2000, 3, 1)) 106 107 def testNextFriday(self): 108 self.assertEqual(self.today+relativedelta(weekday=FR), 109 date(2003, 9, 19)) 110 111 def testNextFridayInt(self): 112 self.assertEqual(self.today+relativedelta(weekday=calendar.FRIDAY), 113 date(2003, 9, 19)) 114 115 def testLastFridayInThisMonth(self): 116 self.assertEqual(self.today+relativedelta(day=31, weekday=FR(-1)), 117 date(2003, 9, 26)) 118 119 def testNextWednesdayIsToday(self): 120 self.assertEqual(self.today+relativedelta(weekday=WE), 121 date(2003, 9, 17)) 122 123 def testNextWenesdayNotToday(self): 124 self.assertEqual(self.today+relativedelta(days=+1, weekday=WE), 125 date(2003, 9, 24)) 126 127 def test15thISOYearWeek(self): 128 self.assertEqual(date(2003, 1, 1) + 129 relativedelta(day=4, weeks=+14, weekday=MO(-1)), 130 date(2003, 4, 7)) 131 132 def testMillenniumAge(self): 133 self.assertEqual(relativedelta(self.now, date(2001, 1, 1)), 134 relativedelta(years=+2, months=+8, days=+16, 135 hours=+20, minutes=+54, seconds=+47, 136 microseconds=+282310)) 137 138 def testJohnAge(self): 139 self.assertEqual(relativedelta(self.now, 140 datetime(1978, 4, 5, 12, 0)), 141 relativedelta(years=+25, months=+5, days=+12, 142 hours=+8, minutes=+54, seconds=+47, 143 microseconds=+282310)) 144 145 def testJohnAgeWithDate(self): 146 self.assertEqual(relativedelta(self.today, 147 datetime(1978, 4, 5, 12, 0)), 148 relativedelta(years=+25, months=+5, days=+11, 149 hours=+12)) 150 151 def testYearDay(self): 152 self.assertEqual(date(2003, 1, 1)+relativedelta(yearday=260), 153 date(2003, 9, 17)) 154 self.assertEqual(date(2002, 1, 1)+relativedelta(yearday=260), 155 date(2002, 9, 17)) 156 self.assertEqual(date(2000, 1, 1)+relativedelta(yearday=260), 157 date(2000, 9, 16)) 158 self.assertEqual(self.today+relativedelta(yearday=261), 159 date(2003, 9, 18)) 160 161 def testYearDayBug(self): 162 # Tests a problem reported by Adam Ryan. 163 self.assertEqual(date(2010, 1, 1)+relativedelta(yearday=15), 164 date(2010, 1, 15)) 165 166 def testNonLeapYearDay(self): 167 self.assertEqual(date(2003, 1, 1)+relativedelta(nlyearday=260), 168 date(2003, 9, 17)) 169 self.assertEqual(date(2002, 1, 1)+relativedelta(nlyearday=260), 170 date(2002, 9, 17)) 171 self.assertEqual(date(2000, 1, 1)+relativedelta(nlyearday=260), 172 date(2000, 9, 17)) 173 self.assertEqual(self.today+relativedelta(yearday=261), 174 date(2003, 9, 18)) 175 176 def testAddition(self): 177 self.assertEqual(relativedelta(days=10) + 178 relativedelta(years=1, months=2, days=3, hours=4, 179 minutes=5, microseconds=6), 180 relativedelta(years=1, months=2, days=13, hours=4, 181 minutes=5, microseconds=6)) 182 183 def testAdditionToDatetime(self): 184 self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1), 185 datetime(2000, 1, 2)) 186 187 def testRightAdditionToDatetime(self): 188 self.assertEqual(relativedelta(days=1) + datetime(2000, 1, 1), 189 datetime(2000, 1, 2)) 190 191 def testAdditionInvalidType(self): 192 with self.assertRaises(TypeError): 193 relativedelta(days=3) + 9 194 195 def testAdditionUnsupportedType(self): 196 # For unsupported types that define their own comparators, etc. 197 self.assertIs(relativedelta(days=1) + NotAValue, NotAValue) 198 199 def testSubtraction(self): 200 self.assertEqual(relativedelta(days=10) - 201 relativedelta(years=1, months=2, days=3, hours=4, 202 minutes=5, microseconds=6), 203 relativedelta(years=-1, months=-2, days=7, hours=-4, 204 minutes=-5, microseconds=-6)) 205 206 def testRightSubtractionFromDatetime(self): 207 self.assertEqual(datetime(2000, 1, 2) - relativedelta(days=1), 208 datetime(2000, 1, 1)) 209 210 def testSubractionWithDatetime(self): 211 self.assertRaises(TypeError, lambda x, y: x - y, 212 (relativedelta(days=1), datetime(2000, 1, 1))) 213 214 def testSubtractionInvalidType(self): 215 with self.assertRaises(TypeError): 216 relativedelta(hours=12) - 14 217 218 def testSubtractionUnsupportedType(self): 219 self.assertIs(relativedelta(days=1) + NotAValue, NotAValue) 220 221 def testMultiplication(self): 222 self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1) * 28, 223 datetime(2000, 1, 29)) 224 self.assertEqual(datetime(2000, 1, 1) + 28 * relativedelta(days=1), 225 datetime(2000, 1, 29)) 226 227 def testMultiplicationUnsupportedType(self): 228 self.assertIs(relativedelta(days=1) * NotAValue, NotAValue) 229 230 def testDivision(self): 231 self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=28) / 28, 232 datetime(2000, 1, 2)) 233 234 def testDivisionUnsupportedType(self): 235 self.assertIs(relativedelta(days=1) / NotAValue, NotAValue) 236 237 def testBoolean(self): 238 self.assertFalse(relativedelta(days=0)) 239 self.assertTrue(relativedelta(days=1)) 240 241 def testComparison(self): 242 d1 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1, 243 minutes=1, seconds=1, microseconds=1) 244 d2 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1, 245 minutes=1, seconds=1, microseconds=1) 246 d3 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1, 247 minutes=1, seconds=1, microseconds=2) 248 249 self.assertEqual(d1, d2) 250 self.assertNotEqual(d1, d3) 251 252 def testInequalityTypeMismatch(self): 253 # Different type 254 self.assertFalse(relativedelta(year=1) == 19) 255 256 def testInequalityUnsupportedType(self): 257 self.assertIs(relativedelta(hours=3) == NotAValue, NotAValue) 258 259 def testInequalityWeekdays(self): 260 # Different weekdays 261 no_wday = relativedelta(year=1997, month=4) 262 wday_mo_1 = relativedelta(year=1997, month=4, weekday=MO(+1)) 263 wday_mo_2 = relativedelta(year=1997, month=4, weekday=MO(+2)) 264 wday_tu = relativedelta(year=1997, month=4, weekday=TU) 265 266 self.assertTrue(wday_mo_1 == wday_mo_1) 267 268 self.assertFalse(no_wday == wday_mo_1) 269 self.assertFalse(wday_mo_1 == no_wday) 270 271 self.assertFalse(wday_mo_1 == wday_mo_2) 272 self.assertFalse(wday_mo_2 == wday_mo_1) 273 274 self.assertFalse(wday_mo_1 == wday_tu) 275 self.assertFalse(wday_tu == wday_mo_1) 276 277 def testMonthOverflow(self): 278 self.assertEqual(relativedelta(months=273), 279 relativedelta(years=22, months=9)) 280 281 def testWeeks(self): 282 # Test that the weeks property is working properly. 283 rd = relativedelta(years=4, months=2, weeks=8, days=6) 284 self.assertEqual((rd.weeks, rd.days), (8, 8 * 7 + 6)) 285 286 rd.weeks = 3 287 self.assertEqual((rd.weeks, rd.days), (3, 3 * 7 + 6)) 288 289 def testRelativeDeltaRepr(self): 290 self.assertEqual(repr(relativedelta(years=1, months=-1, days=15)), 291 'relativedelta(years=+1, months=-1, days=+15)') 292 293 self.assertEqual(repr(relativedelta(months=14, seconds=-25)), 294 'relativedelta(years=+1, months=+2, seconds=-25)') 295 296 self.assertEqual(repr(relativedelta(month=3, hour=3, weekday=SU(3))), 297 'relativedelta(month=3, weekday=SU(+3), hour=3)') 298 299 def testRelativeDeltaFractionalYear(self): 300 with self.assertRaises(ValueError): 301 relativedelta(years=1.5) 302 303 def testRelativeDeltaFractionalMonth(self): 304 with self.assertRaises(ValueError): 305 relativedelta(months=1.5) 306 307 def testRelativeDeltaFractionalAbsolutes(self): 308 # Fractional absolute values will soon be unsupported, 309 # check for the deprecation warning. 310 with self.assertWarns(DeprecationWarning): 311 relativedelta(year=2.86) 312 313 with self.assertWarns(DeprecationWarning): 314 relativedelta(month=1.29) 315 316 with self.assertWarns(DeprecationWarning): 317 relativedelta(day=0.44) 318 319 with self.assertWarns(DeprecationWarning): 320 relativedelta(hour=23.98) 321 322 with self.assertWarns(DeprecationWarning): 323 relativedelta(minute=45.21) 324 325 with self.assertWarns(DeprecationWarning): 326 relativedelta(second=13.2) 327 328 with self.assertWarns(DeprecationWarning): 329 relativedelta(microsecond=157221.93) 330 331 def testRelativeDeltaFractionalRepr(self): 332 rd = relativedelta(years=3, months=-2, days=1.25) 333 334 self.assertEqual(repr(rd), 335 'relativedelta(years=+3, months=-2, days=+1.25)') 336 337 rd = relativedelta(hours=0.5, seconds=9.22) 338 self.assertEqual(repr(rd), 339 'relativedelta(hours=+0.5, seconds=+9.22)') 340 341 def testRelativeDeltaFractionalWeeks(self): 342 # Equivalent to days=8, hours=18 343 rd = relativedelta(weeks=1.25) 344 d1 = datetime(2009, 9, 3, 0, 0) 345 self.assertEqual(d1 + rd, 346 datetime(2009, 9, 11, 18)) 347 348 def testRelativeDeltaFractionalDays(self): 349 rd1 = relativedelta(days=1.48) 350 351 d1 = datetime(2009, 9, 3, 0, 0) 352 self.assertEqual(d1 + rd1, 353 datetime(2009, 9, 4, 11, 31, 12)) 354 355 rd2 = relativedelta(days=1.5) 356 self.assertEqual(d1 + rd2, 357 datetime(2009, 9, 4, 12, 0, 0)) 358 359 def testRelativeDeltaFractionalHours(self): 360 rd = relativedelta(days=1, hours=12.5) 361 d1 = datetime(2009, 9, 3, 0, 0) 362 self.assertEqual(d1 + rd, 363 datetime(2009, 9, 4, 12, 30, 0)) 364 365 def testRelativeDeltaFractionalMinutes(self): 366 rd = relativedelta(hours=1, minutes=30.5) 367 d1 = datetime(2009, 9, 3, 0, 0) 368 self.assertEqual(d1 + rd, 369 datetime(2009, 9, 3, 1, 30, 30)) 370 371 def testRelativeDeltaFractionalSeconds(self): 372 rd = relativedelta(hours=5, minutes=30, seconds=30.5) 373 d1 = datetime(2009, 9, 3, 0, 0) 374 self.assertEqual(d1 + rd, 375 datetime(2009, 9, 3, 5, 30, 30, 500000)) 376 377 def testRelativeDeltaFractionalPositiveOverflow(self): 378 # Equivalent to (days=1, hours=14) 379 rd1 = relativedelta(days=1.5, hours=2) 380 d1 = datetime(2009, 9, 3, 0, 0) 381 self.assertEqual(d1 + rd1, 382 datetime(2009, 9, 4, 14, 0, 0)) 383 384 # Equivalent to (days=1, hours=14, minutes=45) 385 rd2 = relativedelta(days=1.5, hours=2.5, minutes=15) 386 d1 = datetime(2009, 9, 3, 0, 0) 387 self.assertEqual(d1 + rd2, 388 datetime(2009, 9, 4, 14, 45)) 389 390 # Carry back up - equivalent to (days=2, hours=2, minutes=0, seconds=1) 391 rd3 = relativedelta(days=1.5, hours=13, minutes=59.5, seconds=31) 392 self.assertEqual(d1 + rd3, 393 datetime(2009, 9, 5, 2, 0, 1)) 394 395 def testRelativeDeltaFractionalNegativeDays(self): 396 # Equivalent to (days=-1, hours=-1) 397 rd1 = relativedelta(days=-1.5, hours=11) 398 d1 = datetime(2009, 9, 3, 12, 0) 399 self.assertEqual(d1 + rd1, 400 datetime(2009, 9, 2, 11, 0, 0)) 401 402 # Equivalent to (days=-1, hours=-9) 403 rd2 = relativedelta(days=-1.25, hours=-3) 404 self.assertEqual(d1 + rd2, 405 datetime(2009, 9, 2, 3)) 406 407 def testRelativeDeltaNormalizeFractionalDays(self): 408 # Equivalent to (days=2, hours=18) 409 rd1 = relativedelta(days=2.75) 410 411 self.assertEqual(rd1.normalized(), relativedelta(days=2, hours=18)) 412 413 # Equvalent to (days=1, hours=11, minutes=31, seconds=12) 414 rd2 = relativedelta(days=1.48) 415 416 self.assertEqual(rd2.normalized(), 417 relativedelta(days=1, hours=11, minutes=31, seconds=12)) 418 419 def testRelativeDeltaNormalizeFractionalDays(self): 420 # Equivalent to (hours=1, minutes=30) 421 rd1 = relativedelta(hours=1.5) 422 423 self.assertEqual(rd1.normalized(), relativedelta(hours=1, minutes=30)) 424 425 # Equivalent to (hours=3, minutes=17, seconds=5, microseconds=100) 426 rd2 = relativedelta(hours=3.28472225) 427 428 self.assertEqual(rd2.normalized(), 429 relativedelta(hours=3, minutes=17, seconds=5, microseconds=100)) 430 431 def testRelativeDeltaNormalizeFractionalMinutes(self): 432 # Equivalent to (minutes=15, seconds=36) 433 rd1 = relativedelta(minutes=15.6) 434 435 self.assertEqual(rd1.normalized(), 436 relativedelta(minutes=15, seconds=36)) 437 438 # Equivalent to (minutes=25, seconds=20, microseconds=25000) 439 rd2 = relativedelta(minutes=25.33375) 440 441 self.assertEqual(rd2.normalized(), 442 relativedelta(minutes=25, seconds=20, microseconds=25000)) 443 444 def testRelativeDeltaNormalizeFractionalSeconds(self): 445 # Equivalent to (seconds=45, microseconds=25000) 446 rd1 = relativedelta(seconds=45.025) 447 self.assertEqual(rd1.normalized(), 448 relativedelta(seconds=45, microseconds=25000)) 449 450 def testRelativeDeltaFractionalPositiveOverflow(self): 451 # Equivalent to (days=1, hours=14) 452 rd1 = relativedelta(days=1.5, hours=2) 453 self.assertEqual(rd1.normalized(), 454 relativedelta(days=1, hours=14)) 455 456 # Equivalent to (days=1, hours=14, minutes=45) 457 rd2 = relativedelta(days=1.5, hours=2.5, minutes=15) 458 self.assertEqual(rd2.normalized(), 459 relativedelta(days=1, hours=14, minutes=45)) 460 461 # Carry back up - equivalent to: 462 # (days=2, hours=2, minutes=0, seconds=2, microseconds=3) 463 rd3 = relativedelta(days=1.5, hours=13, minutes=59.50045, 464 seconds=31.473, microseconds=500003) 465 self.assertEqual(rd3.normalized(), 466 relativedelta(days=2, hours=2, minutes=0, 467 seconds=2, microseconds=3)) 468 469 def testRelativeDeltaFractionalNegativeOverflow(self): 470 # Equivalent to (days=-1) 471 rd1 = relativedelta(days=-0.5, hours=-12) 472 self.assertEqual(rd1.normalized(), 473 relativedelta(days=-1)) 474 475 # Equivalent to (days=-1) 476 rd2 = relativedelta(days=-1.5, hours=12) 477 self.assertEqual(rd2.normalized(), 478 relativedelta(days=-1)) 479 480 # Equivalent to (days=-1, hours=-14, minutes=-45) 481 rd3 = relativedelta(days=-1.5, hours=-2.5, minutes=-15) 482 self.assertEqual(rd3.normalized(), 483 relativedelta(days=-1, hours=-14, minutes=-45)) 484 485 # Equivalent to (days=-1, hours=-14, minutes=+15) 486 rd4 = relativedelta(days=-1.5, hours=-2.5, minutes=45) 487 self.assertEqual(rd4.normalized(), 488 relativedelta(days=-1, hours=-14, minutes=+15)) 489 490 # Carry back up - equivalent to: 491 # (days=-2, hours=-2, minutes=0, seconds=-2, microseconds=-3) 492 rd3 = relativedelta(days=-1.5, hours=-13, minutes=-59.50045, 493 seconds=-31.473, microseconds=-500003) 494 self.assertEqual(rd3.normalized(), 495 relativedelta(days=-2, hours=-2, minutes=0, 496 seconds=-2, microseconds=-3)) 497 498 def testInvalidYearDay(self): 499 with self.assertRaises(ValueError): 500 relativedelta(yearday=367) 501 502 def testAddTimedeltaToUnpopulatedRelativedelta(self): 503 td = timedelta( 504 days=1, 505 seconds=1, 506 microseconds=1, 507 milliseconds=1, 508 minutes=1, 509 hours=1, 510 weeks=1 511 ) 512 513 expected = relativedelta( 514 weeks=1, 515 days=1, 516 hours=1, 517 minutes=1, 518 seconds=1, 519 microseconds=1001 520 ) 521 522 self.assertEqual(expected, relativedelta() + td) 523 524 def testAddTimedeltaToPopulatedRelativeDelta(self): 525 td = timedelta( 526 days=1, 527 seconds=1, 528 microseconds=1, 529 milliseconds=1, 530 minutes=1, 531 hours=1, 532 weeks=1 533 ) 534 535 rd = relativedelta( 536 year=1, 537 month=1, 538 day=1, 539 hour=1, 540 minute=1, 541 second=1, 542 microsecond=1, 543 years=1, 544 months=1, 545 days=1, 546 weeks=1, 547 hours=1, 548 minutes=1, 549 seconds=1, 550 microseconds=1 551 ) 552 553 expected = relativedelta( 554 year=1, 555 month=1, 556 day=1, 557 hour=1, 558 minute=1, 559 second=1, 560 microsecond=1, 561 years=1, 562 months=1, 563 weeks=2, 564 days=2, 565 hours=2, 566 minutes=2, 567 seconds=2, 568 microseconds=1002, 569 ) 570 571 self.assertEqual(expected, rd + td) 572