[Python-modules-commits] [python-aniso8601] 02/02: Update upstream to 1.2.1

Jonathan Carter highvoltage-guest at moszumanska.debian.org
Sat May 27 14:16:59 UTC 2017


This is an automated email from the git hooks/post-receive script.

highvoltage-guest pushed a commit to branch upstream
in repository python-aniso8601.

commit d2ab5faefb4a3734c2d45b2b0d02de6b0aa3208b
Author: Jonathan Carter <jcarter at linux.com>
Date:   Sat May 27 16:16:54 2017 +0200

    Update upstream to 1.2.1
---
 PKG-INFO                                           |  12 +-
 README.rst                                         |  10 +-
 aniso8601.egg-info/PKG-INFO                        |  12 +-
 aniso8601/__pycache__/date.cpython-35.pyc          | Bin 4272 -> 4272 bytes
 aniso8601/__pycache__/interval.cpython-35.pyc      | Bin 2386 -> 2769 bytes
 aniso8601/date.py                                  |   4 +-
 aniso8601/date.pyc                                 | Bin 4558 -> 4558 bytes
 aniso8601/interval.py                              |  94 ++++---
 aniso8601/interval.pyc                             | Bin 2532 -> 2945 bytes
 .../tests/__pycache__/test_date.cpython-35.pyc     | Bin 6124 -> 7422 bytes
 .../tests/__pycache__/test_duration.cpython-35.pyc | Bin 9385 -> 13143 bytes
 .../tests/__pycache__/test_interval.cpython-35.pyc | Bin 10254 -> 18472 bytes
 .../tests/__pycache__/test_time.cpython-35.pyc     | Bin 12924 -> 12990 bytes
 .../tests/__pycache__/test_timezone.cpython-35.pyc | Bin 3907 -> 4177 bytes
 aniso8601/tests/test_date.py                       |  21 +-
 aniso8601/tests/test_date.pyc                      | Bin 6748 -> 8190 bytes
 aniso8601/tests/test_duration.py                   | 287 +++++++++++----------
 aniso8601/tests/test_duration.pyc                  | Bin 10074 -> 14012 bytes
 aniso8601/tests/test_interval.py                   | 228 ++++++++++++++--
 aniso8601/tests/test_interval.pyc                  | Bin 10769 -> 19364 bytes
 aniso8601/tests/test_time.py                       |   2 +-
 aniso8601/tests/test_time.pyc                      | Bin 14261 -> 14267 bytes
 aniso8601/tests/test_timezone.py                   |  23 +-
 aniso8601/tests/test_timezone.pyc                  | Bin 4386 -> 4658 bytes
 debian/.git-dpm                                    |  11 -
 debian/changelog                                   |  28 --
 debian/compat                                      |   1 -
 debian/control                                     |  45 ----
 debian/copyright                                   |  37 ---
 debian/python-aniso8601.docs                       |   1 -
 debian/python3-aniso8601.docs                      |   1 -
 debian/rules                                       |   7 -
 debian/source/format                               |   1 -
 debian/watch                                       |   3 -
 setup.py                                           |   2 +-
 35 files changed, 463 insertions(+), 367 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index bf0cdb6..36b28ae 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: aniso8601
-Version: 1.2.0
+Version: 1.2.1
 Summary: A library for parsing ISO 8601 strings.
 Home-page: https://bitbucket.org/nielsenb/aniso8601
 Author: Brandon Nielsen
@@ -244,7 +244,7 @@ Description: ===========
           >>> list(aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00'))
           Traceback (most recent call last):
             File "<stdin>", line 1, in <module>
-            File "aniso8601/__init__.py", line 140, in date_generator_unbounded
+            File "aniso8601/interval.py", line 156, in _date_generator_unbounded
               currentdate += timedelta
           OverflowError: date value out of range
         
@@ -262,11 +262,13 @@ Description: ===========
           >>> aniso8601.parse_interval('P1.1Y/2001-02-28', relative=True)
           Traceback (most recent call last):
             File "<stdin>", line 1, in <module>
-            File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/interval.py", line 51, in parse_interval
+            File "aniso8601/interval.py", line 33, in parse_interval
+              interval_parts = _parse_interval_parts(isointervalstr, intervaldelimiter, datetimedelimiter, relative)
+            File "aniso8601/interval.py", line 84, in _parse_interval_parts
               duration = parse_duration(firstpart, relative=relative)
-            File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/duration.py", line 29, in parse_duration
+            File "aniso8601/duration.py", line 29, in parse_duration
               return _parse_duration_prescribed(isodurationstr, relative)
-            File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/duration.py", line 150, in _parse_duration_prescribed
+            File "aniso8601/duration.py", line 150, in _parse_duration_prescribed
               raise ValueError('Fractional months and years are not defined for relative intervals.')
           ValueError: Fractional months and years are not defined for relative intervals.
         
diff --git a/README.rst b/README.rst
index 623203a..28d5dc0 100644
--- a/README.rst
+++ b/README.rst
@@ -236,7 +236,7 @@ Note that you should never try to convert a generator produced by an unbounded i
   >>> list(aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00'))
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
-    File "aniso8601/__init__.py", line 140, in date_generator_unbounded
+    File "aniso8601/interval.py", line 156, in _date_generator_unbounded
       currentdate += timedelta
   OverflowError: date value out of range
 
@@ -254,11 +254,13 @@ Fractional years and months do not make sense for relative intervals. A ValueErr
   >>> aniso8601.parse_interval('P1.1Y/2001-02-28', relative=True)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
-    File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/interval.py", line 51, in parse_interval
+    File "aniso8601/interval.py", line 33, in parse_interval
+      interval_parts = _parse_interval_parts(isointervalstr, intervaldelimiter, datetimedelimiter, relative)
+    File "aniso8601/interval.py", line 84, in _parse_interval_parts
       duration = parse_duration(firstpart, relative=relative)
-    File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/duration.py", line 29, in parse_duration
+    File "aniso8601/duration.py", line 29, in parse_duration
       return _parse_duration_prescribed(isodurationstr, relative)
-    File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/duration.py", line 150, in _parse_duration_prescribed
+    File "aniso8601/duration.py", line 150, in _parse_duration_prescribed
       raise ValueError('Fractional months and years are not defined for relative intervals.')
   ValueError: Fractional months and years are not defined for relative intervals.
 
diff --git a/aniso8601.egg-info/PKG-INFO b/aniso8601.egg-info/PKG-INFO
index bf0cdb6..36b28ae 100644
--- a/aniso8601.egg-info/PKG-INFO
+++ b/aniso8601.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: aniso8601
-Version: 1.2.0
+Version: 1.2.1
 Summary: A library for parsing ISO 8601 strings.
 Home-page: https://bitbucket.org/nielsenb/aniso8601
 Author: Brandon Nielsen
@@ -244,7 +244,7 @@ Description: ===========
           >>> list(aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00'))
           Traceback (most recent call last):
             File "<stdin>", line 1, in <module>
-            File "aniso8601/__init__.py", line 140, in date_generator_unbounded
+            File "aniso8601/interval.py", line 156, in _date_generator_unbounded
               currentdate += timedelta
           OverflowError: date value out of range
         
@@ -262,11 +262,13 @@ Description: ===========
           >>> aniso8601.parse_interval('P1.1Y/2001-02-28', relative=True)
           Traceback (most recent call last):
             File "<stdin>", line 1, in <module>
-            File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/interval.py", line 51, in parse_interval
+            File "aniso8601/interval.py", line 33, in parse_interval
+              interval_parts = _parse_interval_parts(isointervalstr, intervaldelimiter, datetimedelimiter, relative)
+            File "aniso8601/interval.py", line 84, in _parse_interval_parts
               duration = parse_duration(firstpart, relative=relative)
-            File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/duration.py", line 29, in parse_duration
+            File "aniso8601/duration.py", line 29, in parse_duration
               return _parse_duration_prescribed(isodurationstr, relative)
-            File "/home/nielsenb/Jetfuse/aniso8601_working/aniso8601/aniso8601/duration.py", line 150, in _parse_duration_prescribed
+            File "aniso8601/duration.py", line 150, in _parse_duration_prescribed
               raise ValueError('Fractional months and years are not defined for relative intervals.')
           ValueError: Fractional months and years are not defined for relative intervals.
         
diff --git a/aniso8601/__pycache__/date.cpython-35.pyc b/aniso8601/__pycache__/date.cpython-35.pyc
index 8fca26c..219044c 100644
Binary files a/aniso8601/__pycache__/date.cpython-35.pyc and b/aniso8601/__pycache__/date.cpython-35.pyc differ
diff --git a/aniso8601/__pycache__/interval.cpython-35.pyc b/aniso8601/__pycache__/interval.cpython-35.pyc
index 51a39e4..67b1c1c 100644
Binary files a/aniso8601/__pycache__/interval.cpython-35.pyc and b/aniso8601/__pycache__/interval.cpython-35.pyc differ
diff --git a/aniso8601/date.py b/aniso8601/date.py
index ef2cf96..2b9b713 100644
--- a/aniso8601/date.py
+++ b/aniso8601/date.py
@@ -25,13 +25,15 @@ def get_date_resolution(isodatestr):
     #YYYYDDD
     if isodatestr.startswith('+') or isodatestr.startswith('-'):
         raise NotImplementedError('ISO 8601 extended year representation not supported.')
+
     isodatestrlen = len(isodatestr)
 
     if isodatestr.find('W') != -1:
         #Handle ISO 8601 week date format
         hyphens_present = 1 if isodatestr.find('-') != -1 else 0
         week_date_len = 7 + hyphens_present
-        weekday_date_len = 8 + 2*hyphens_present
+        weekday_date_len = 8 + 2 * hyphens_present
+
         if isodatestrlen == week_date_len:
             #YYYY-Www
             #YYYYWww
diff --git a/aniso8601/date.pyc b/aniso8601/date.pyc
index 73f649c..6a4c869 100644
Binary files a/aniso8601/date.pyc and b/aniso8601/date.pyc differ
diff --git a/aniso8601/interval.py b/aniso8601/interval.py
index b869b09..838f6e9 100644
--- a/aniso8601/interval.py
+++ b/aniso8601/interval.py
@@ -30,6 +30,39 @@ def parse_interval(isointervalstr, intervaldelimiter='/', datetimedelimiter='T',
     #Is expressly not supported as there is no way to provide the addtional
     #required context.
 
+    interval_parts = _parse_interval_parts(isointervalstr, intervaldelimiter, datetimedelimiter, relative)
+
+    return (interval_parts[0], interval_parts[1])
+
+def parse_repeating_interval(isointervalstr, intervaldelimiter='/', datetimedelimiter='T', relative=False):
+    #Given a string representing an ISO 8601 interval repating, return a
+    #generator of datetime.date or date.datetime objects representing the
+    #dates specified by the repeating interval. Valid formats are:
+    #
+    #Rnn/<interval>
+    #R/<interval>
+
+    if isointervalstr[0] != 'R':
+        raise ValueError('ISO 8601 repeating interval must start with an R.')
+
+    #Parse the number of iterations
+    iterationpart, intervalpart = isointervalstr.split(intervaldelimiter, 1)
+
+    if len(iterationpart) > 1:
+        iterations = int(iterationpart[1:])
+    else:
+        iterations = None
+
+    interval_parts = _parse_interval_parts(intervalpart, intervaldelimiter, datetimedelimiter, relative=relative)
+
+    #Now, build and return the generator
+    if iterations != None:
+        return _date_generator(interval_parts[0], interval_parts[2], iterations)
+    else:
+        return _date_generator_unbounded(interval_parts[0], interval_parts[2])
+
+def _parse_interval_parts(isointervalstr, intervaldelimiter='/', datetimedelimiter='T', relative=False):
+    #Returns a tuple containing the start of the interval, the end of the interval, and the interval timedelta
     firstpart, secondpart = isointervalstr.split(intervaldelimiter)
 
     if firstpart[0] == 'P':
@@ -45,7 +78,7 @@ def parse_interval(isointervalstr, intervaldelimiter='/', datetimedelimiter='T',
             duration = parse_duration(firstpart, relative=relative)
             enddatetime = parse_datetime(secondpart, delimiter=datetimedelimiter)
 
-            return (enddatetime, enddatetime - duration)
+            return (enddatetime, enddatetime - duration, -duration)
         else:
             #<end> must just be a date
             duration = parse_duration(firstpart, relative=relative)
@@ -53,18 +86,18 @@ def parse_interval(isointervalstr, intervaldelimiter='/', datetimedelimiter='T',
 
             #See if we need to upconvert to datetime to preserve resolution
             if firstpart.find(datetimedelimiter) != -1:
-                return (enddate, datetime.combine(enddate, datetime.min.time()) - duration)
+                return (enddate, datetime.combine(enddate, datetime.min.time()) - duration, -duration)
             else:
-                return (enddate, enddate - duration)
+                return (enddate, enddate - duration, -duration)
     elif secondpart[0] == 'P':
         #<start>/<duration>
         #We need to figure out if <start> is a date, or a datetime
         if firstpart.find(datetimedelimiter) != -1:
-            #<end> is a datetime
+            #<start> is a datetime
             duration = parse_duration(secondpart, relative=relative)
             startdatetime = parse_datetime(firstpart, delimiter=datetimedelimiter)
 
-            return (startdatetime, startdatetime + duration)
+            return (startdatetime, startdatetime + duration, duration)
         else:
             #<start> must just be a date
             duration = parse_duration(secondpart, relative=relative)
@@ -72,52 +105,35 @@ def parse_interval(isointervalstr, intervaldelimiter='/', datetimedelimiter='T',
 
             #See if we need to upconvert to datetime to preserve resolution
             if secondpart.find(datetimedelimiter) != -1:
-                return (startdate, datetime.combine(startdate, datetime.min.time()) + duration)
+                return (startdate, datetime.combine(startdate, datetime.min.time()) + duration, duration)
             else:
-                return (startdate, startdate + duration)
+                return (startdate, startdate + duration, duration)
     else:
         #<start>/<end>
         if firstpart.find(datetimedelimiter) != -1 and secondpart.find(datetimedelimiter) != -1:
             #Both parts are datetimes
-            return (parse_datetime(firstpart, delimiter=datetimedelimiter), parse_datetime(secondpart, delimiter=datetimedelimiter))
+            start_datetime = parse_datetime(firstpart, delimiter=datetimedelimiter)
+            end_datetime = parse_datetime(secondpart, delimiter=datetimedelimiter)
+
+            return (start_datetime, end_datetime, end_datetime - start_datetime)
         elif firstpart.find(datetimedelimiter) != -1 and secondpart.find(datetimedelimiter) == -1:
             #First part is a datetime, second part is a date
-            return (parse_datetime(firstpart, delimiter=datetimedelimiter), parse_date(secondpart))
+            start_datetime = parse_datetime(firstpart, delimiter=datetimedelimiter)
+            end_date = parse_date(secondpart)
+
+            return (start_datetime, end_date, datetime.combine(end_date, datetime.min.time()) - start_datetime)
         elif firstpart.find(datetimedelimiter) == -1 and secondpart.find(datetimedelimiter) != -1:
             #First part is a date, second part is a datetime
-            return (parse_date(firstpart), parse_datetime(secondpart, delimiter=datetimedelimiter))
+            start_date = parse_date(firstpart)
+            end_datetime = parse_datetime(secondpart, delimiter=datetimedelimiter)
+
+            return (start_date, end_datetime, end_datetime - datetime.combine(start_date, datetime.min.time()))
         else:
             #Both parts are dates
-            return (parse_date(firstpart), parse_date(secondpart))
-
-def parse_repeating_interval(isointervalstr, intervaldelimiter='/', datetimedelimiter='T', relative=False):
-    #Given a string representing an ISO 8601 interval repating, return a
-    #generator of datetime.date or date.datetime objects representing the
-    #dates specified by the repeating interval. Valid formats are:
-    #
-    #Rnn/<interval>
-    #R/<interval>
-
-    if isointervalstr[0] != 'R':
-        raise ValueError('ISO 8601 repeating interval must start with an R.')
+            start_date = parse_date(firstpart)
+            end_date = parse_date(secondpart)
 
-    #Parse the number of iterations
-    iterationpart, intervalpart = isointervalstr.split(intervaldelimiter, 1)
-
-    if len(iterationpart) > 1:
-        iterations = int(iterationpart[1:])
-    else:
-        iterations = None
-
-    interval = parse_interval(intervalpart, intervaldelimiter, datetimedelimiter, relative=relative)
-
-    intervaltimedelta = interval[1] - interval[0]
-
-    #Now, build and return the generator
-    if iterations != None:
-        return _date_generator(interval[0], intervaltimedelta, iterations)
-    else:
-        return _date_generator_unbounded(interval[0], intervaltimedelta)
+            return (start_date, end_date, end_date - start_date)
 
 def _date_generator(startdate, timedelta, iterations):
     currentdate = startdate
diff --git a/aniso8601/interval.pyc b/aniso8601/interval.pyc
index 613da26..9350dcb 100644
Binary files a/aniso8601/interval.pyc and b/aniso8601/interval.pyc differ
diff --git a/aniso8601/tests/__pycache__/test_date.cpython-35.pyc b/aniso8601/tests/__pycache__/test_date.cpython-35.pyc
index 2f19fc9..18aa4b2 100644
Binary files a/aniso8601/tests/__pycache__/test_date.cpython-35.pyc and b/aniso8601/tests/__pycache__/test_date.cpython-35.pyc differ
diff --git a/aniso8601/tests/__pycache__/test_duration.cpython-35.pyc b/aniso8601/tests/__pycache__/test_duration.cpython-35.pyc
index fbe63a7..e1e62f9 100644
Binary files a/aniso8601/tests/__pycache__/test_duration.cpython-35.pyc and b/aniso8601/tests/__pycache__/test_duration.cpython-35.pyc differ
diff --git a/aniso8601/tests/__pycache__/test_interval.cpython-35.pyc b/aniso8601/tests/__pycache__/test_interval.cpython-35.pyc
index 7f19e2a..56ff437 100644
Binary files a/aniso8601/tests/__pycache__/test_interval.cpython-35.pyc and b/aniso8601/tests/__pycache__/test_interval.cpython-35.pyc differ
diff --git a/aniso8601/tests/__pycache__/test_time.cpython-35.pyc b/aniso8601/tests/__pycache__/test_time.cpython-35.pyc
index aa4f68a..6b22f8d 100644
Binary files a/aniso8601/tests/__pycache__/test_time.cpython-35.pyc and b/aniso8601/tests/__pycache__/test_time.cpython-35.pyc differ
diff --git a/aniso8601/tests/__pycache__/test_timezone.cpython-35.pyc b/aniso8601/tests/__pycache__/test_timezone.cpython-35.pyc
index bfe3762..5bfd382 100644
Binary files a/aniso8601/tests/__pycache__/test_timezone.cpython-35.pyc and b/aniso8601/tests/__pycache__/test_timezone.cpython-35.pyc differ
diff --git a/aniso8601/tests/test_date.py b/aniso8601/tests/test_date.py
index 0e8ec61..83e3bc8 100644
--- a/aniso8601/tests/test_date.py
+++ b/aniso8601/tests/test_date.py
@@ -11,22 +11,29 @@ import unittest
 from aniso8601.date import parse_date, _parse_year, _parse_calendar_day, _parse_calendar_month, _parse_week_day, _parse_week, _parse_ordinal_date, get_date_resolution
 from aniso8601.resolution import DateResolution
 
-class TestDateFunctions(unittest.TestCase):
-    def test_get_date_resolution(self):
+class TestDateResolutionFunctions(unittest.TestCase):
+    def test_get_date_resolution_year(self):
         self.assertEqual(get_date_resolution('2013'), DateResolution.Year)
         self.assertEqual(get_date_resolution('0001'), DateResolution.Year)
         self.assertEqual(get_date_resolution('19'), DateResolution.Year)
-        self.assertEqual(get_date_resolution('1981-04-05'), DateResolution.Day)
-        self.assertEqual(get_date_resolution('19810405'), DateResolution.Day)
+
+    def test_get_date_resolution_month(self):
         self.assertEqual(get_date_resolution('1981-04'), DateResolution.Month)
+
+    def test_get_date_resolution_week(self):
         self.assertEqual(get_date_resolution('2004-W53'), DateResolution.Week)
         self.assertEqual(get_date_resolution('2009-W01'), DateResolution.Week)
-        self.assertEqual(get_date_resolution('2004-W53-6'), DateResolution.Weekday)
         self.assertEqual(get_date_resolution('2004W53'), DateResolution.Week)
+
+    def test_get_date_resolution_year_weekday(self):
+        self.assertEqual(get_date_resolution('2004-W53-6'), DateResolution.Weekday)
         self.assertEqual(get_date_resolution('2004W536'), DateResolution.Weekday)
+
+    def test_get_date_resolution_year_ordinal(self):
         self.assertEqual(get_date_resolution('1981-095'), DateResolution.Ordinal)
         self.assertEqual(get_date_resolution('1981095'), DateResolution.Ordinal)
 
+class TestDateParserFunctions(unittest.TestCase):
     def test_parse_date(self):
         date = parse_date('2013')
         self.assertEqual(date.year, 2013)
@@ -109,6 +116,8 @@ class TestDateFunctions(unittest.TestCase):
         self.assertEqual(date.month, 1)
         self.assertEqual(date.day, 1)
 
+    def test_parse_year_nonzero(self):
+        #0 isn't a valid year
         with self.assertRaises(ValueError):
             _parse_year('0')
 
@@ -129,6 +138,8 @@ class TestDateFunctions(unittest.TestCase):
         self.assertEqual(date.month, 4)
         self.assertEqual(date.day, 1)
 
+    def test_parse_calendar_month_nohyphen(self):
+        #Hyphen is required
         with self.assertRaises(ValueError):
             _parse_calendar_month('198104')
 
diff --git a/aniso8601/tests/test_date.pyc b/aniso8601/tests/test_date.pyc
index 57d0d79..e855a80 100644
Binary files a/aniso8601/tests/test_date.pyc and b/aniso8601/tests/test_date.pyc differ
diff --git a/aniso8601/tests/test_duration.py b/aniso8601/tests/test_duration.py
index a80a5c1..2909f52 100644
--- a/aniso8601/tests/test_duration.py
+++ b/aniso8601/tests/test_duration.py
@@ -10,55 +10,8 @@ import unittest
 
 from aniso8601.duration import parse_duration, _parse_duration_prescribed, _parse_duration_combined, _parse_duration_element, _has_any_component, _component_order_correct
 
-class TestDurationFunctions(unittest.TestCase):
+class TestDurationParserFunctions(unittest.TestCase):
     def test_parse_duration(self):
-        with self.assertRaises(ValueError):
-            #Duration must start with a P
-            parse_duration('1Y2M3DT4H54M6S')
-
-        with self.assertRaises(ValueError):
-            #Week designator cannot be combined with other time designators
-            #https://bitbucket.org/nielsenb/aniso8601/issues/2/week-designators-should-not-be-combinable
-            parse_duration('P1Y2W')
-
-        #Ensure durations are required to be in the correct order
-        #https://bitbucket.org/nielsenb/aniso8601/issues/7/durations-with-time-components-before-t
-        #https://bitbucket.org/nielsenb/aniso8601/issues/8/durations-with-components-in-wrong-order
-        with self.assertRaises(ValueError):
-            parse_duration('P1S')
-
-        with self.assertRaises(ValueError):
-            parse_duration('P1D1S')
-
-        with self.assertRaises(ValueError):
-            parse_duration('P1H1M')
-
-        with self.assertRaises(ValueError):
-            parse_duration('1Y2M3D1SPT1M')
-
-        with self.assertRaises(ValueError):
-            parse_duration('P1Y2M3D2MT1S')
-
-        with self.assertRaises(ValueError):
-            parse_duration('P2M3D1ST1Y1M')
-
-        with self.assertRaises(ValueError):
-            parse_duration('P1Y2M2MT3D1S')
-
-        with self.assertRaises(ValueError):
-            parse_duration('P1D1Y1M')
-
-        with self.assertRaises(ValueError):
-            parse_duration('PT1S1H')
-
-        #Don't allow garbage after the duration
-        #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
-        with self.assertRaises(ValueError):
-            parse_duration('P1Dasdfasdf')
-
-        with self.assertRaises(ValueError):
-            parse_duration('P0003-06-04T12:30:05.5asdfasdf')
-
         resultduration = parse_duration('P1Y2M3DT4H54M6S')
         self.assertEqual(resultduration.days, 428)
         self.assertEqual(resultduration.seconds, 17646)
@@ -149,68 +102,58 @@ class TestDurationFunctions(unittest.TestCase):
         #Verify overflows
         self.assertEqual(parse_duration('PT36H'), parse_duration('P1DT12H'))
 
-    def test_parse_duration_relative(self):
-        resultduration = parse_duration('P1Y2M3DT4H54M6.5S', relative=True)
-        self.assertEqual(resultduration.years, 1)
-        self.assertEqual(resultduration.months, 2)
-        self.assertEqual(resultduration.days, 3)
-        self.assertEqual(resultduration.hours, 4)
-        self.assertEqual(resultduration.minutes, 54)
-        self.assertEqual(resultduration.seconds, 6.5)
-
-        resultduration = parse_duration('P0003-06-04T12:30:05.5', relative=True)
-        self.assertEqual(resultduration.years, 3)
-        self.assertEqual(resultduration.months, 6)
-        self.assertEqual(resultduration.days, 4)
-        self.assertEqual(resultduration.hours, 12)
-        self.assertEqual(resultduration.minutes, 30)
-        self.assertEqual(resultduration.seconds, 5)
-        self.assertEqual(resultduration.microseconds, 500000)
-
-    def test_parse_duration_prescribed(self):
+    def test_parse_duration_nop(self):
         with self.assertRaises(ValueError):
-            #Multiple fractions are not allowed
-            _parse_duration_prescribed('P1Y2M3DT4H5.1234M6.1234S', False)
+            #Duration must start with a P
+            parse_duration('1Y2M3DT4H54M6S')
 
+    def test_parse_duration_weekcombination(self):
         with self.assertRaises(ValueError):
-            #Fraction only allowed on final component
-            _parse_duration_prescribed('P1Y2M3DT4H5.1234M6S', False)
+            #Week designator cannot be combined with other time designators
+            #https://bitbucket.org/nielsenb/aniso8601/issues/2/week-designators-should-not-be-combinable
+            parse_duration('P1Y2W')
 
+    def test_parse_duration_outoforder(self):
         #Ensure durations are required to be in the correct order
         #https://bitbucket.org/nielsenb/aniso8601/issues/7/durations-with-time-components-before-t
         #https://bitbucket.org/nielsenb/aniso8601/issues/8/durations-with-components-in-wrong-order
         with self.assertRaises(ValueError):
-            parse_duration('P1S', False)
+            parse_duration('P1S')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1D1S', False)
+            parse_duration('P1D1S')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1H1M', False)
+            parse_duration('P1H1M')
 
         with self.assertRaises(ValueError):
-            parse_duration('1Y2M3D1SPT1M', False)
+            parse_duration('1Y2M3D1SPT1M')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1Y2M3D2MT1S', False)
+            parse_duration('P1Y2M3D2MT1S')
 
         with self.assertRaises(ValueError):
-            parse_duration('P2M3D1ST1Y1M', False)
+            parse_duration('P2M3D1ST1Y1M')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1Y2M2MT3D1S', False)
+            parse_duration('P1Y2M2MT3D1S')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1D1Y1M', False)
+            parse_duration('P1D1Y1M')
 
         with self.assertRaises(ValueError):
-            parse_duration('PT1S1H', False)
+            parse_duration('PT1S1H')
 
+    def test_parse_duration_suffixgarbage(self):
         #Don't allow garbage after the duration
         #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
         with self.assertRaises(ValueError):
-            parse_duration('P1Dasdfasdf', False)
+            parse_duration('P1Dasdfasdf')
+
+        with self.assertRaises(ValueError):
+            parse_duration('P0003-06-04T12:30:05.5asdfasdf')
 
+    def test_parse_duration_prescribed(self):
         resultduration = _parse_duration_prescribed('P1Y2M3DT4H54M6S', False)
         self.assertEqual(resultduration.days, 428)
         self.assertEqual(resultduration.seconds, 17646)
@@ -289,60 +232,113 @@ class TestDurationFunctions(unittest.TestCase):
         self.assertEqual(resultduration.seconds, 43200)
 
         #Verify overflows
-        self.assertEqual(parse_duration('PT36H', False), parse_duration('P1DT12H', False))
+        self.assertEqual(parse_duration('PT36H'), parse_duration('P1DT12H'))
 
-    def test_parse_duration_prescribed_relative(self):
+    def test_parse_duration_prescribed_multiplefractions(self):
         with self.assertRaises(ValueError):
             #Multiple fractions are not allowed
-            _parse_duration_prescribed('P1Y2M3DT4H5.1234M6.1234S', True)
+            _parse_duration_prescribed('P1Y2M3DT4H5.1234M6.1234S', False)
 
+    def test_parse_duration_prescribed_middlefraction(self):
         with self.assertRaises(ValueError):
             #Fraction only allowed on final component
-            _parse_duration_prescribed('P1Y2M3DT4H5.1234M6S', True)
+            _parse_duration_prescribed('P1Y2M3DT4H5.1234M6S', False)
 
+    def test_parse_duration_prescribed_outoforder(self):
         #Ensure durations are required to be in the correct order
         #https://bitbucket.org/nielsenb/aniso8601/issues/7/durations-with-time-components-before-t
         #https://bitbucket.org/nielsenb/aniso8601/issues/8/durations-with-components-in-wrong-order
         with self.assertRaises(ValueError):
-            parse_duration('P1S', True)
+            parse_duration('P1S')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1D1S', True)
+            parse_duration('P1D1S')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1H1M', True)
+            parse_duration('P1H1M')
 
         with self.assertRaises(ValueError):
-            parse_duration('1Y2M3D1SPT1M', True)
+            parse_duration('1Y2M3D1SPT1M')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1Y2M3D2MT1S', True)
+            parse_duration('P1Y2M3D2MT1S')
 
         with self.assertRaises(ValueError):
-            parse_duration('P2M3D1ST1Y1M', True)
+            parse_duration('P2M3D1ST1Y1M')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1Y2M2MT3D1S', True)
+            parse_duration('P1Y2M2MT3D1S')
 
         with self.assertRaises(ValueError):
-            parse_duration('P1D1Y1M', True)
+            parse_duration('P1D1Y1M')
 
         with self.assertRaises(ValueError):
-            parse_duration('PT1S1H', True)
+            parse_duration('PT1S1H')
 
+    def test_parse_duration_prescribed_suffixgarbage(self):
         #Don't allow garbage after the duration
         #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
         with self.assertRaises(ValueError):
-            parse_duration('P1Dasdfasdf', True)
+            parse_duration('P1Dasdfasdf')
 
-        #Fractional months and years are not defined
-        #https://github.com/dateutil/dateutil/issues/40
-        with self.assertRaises(ValueError):
-            _parse_duration_prescribed('P1.5Y', True)
+    def test_parse_duration_combined(self):
+        resultduration = _parse_duration_combined('P0003-06-04T12:30:05', False)
+        self.assertEqual(resultduration.days, 1279)
+        self.assertEqual(resultduration.seconds, 45005)
+        self.assertEqual(resultduration.microseconds, 0)
+
+        resultduration = _parse_duration_combined('P0003-06-04T12:30:05.5', False)
+        self.assertEqual(resultduration.days, 1279)
+        self.assertEqual(resultduration.seconds, 45005)
+        self.assertEqual(resultduration.microseconds, 500000)
 
+    def test_parse_duration_combined_suffixgarbage(self):
+        #Don't allow garbage after the duration
+        #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
         with self.assertRaises(ValueError):
-            _parse_duration_prescribed('P1.5M', True)
+            parse_duration('P0003-06-04T12:30:05.5asdfasdf')
+
+    def test_parse_duration_element(self):
+        self.assertEqual(_parse_duration_element('P1Y2M3D', 'Y'), 1)
+        self.assertEqual(_parse_duration_element('P1Y2M3D', 'M'), 2)
+        self.assertEqual(_parse_duration_element('P1Y2M3D', 'D'), 3)
+        self.assertEqual(_parse_duration_element('T4H5M6.1234S', 'H'), 4)
+        self.assertEqual(_parse_duration_element('T4H5M6.1234S', 'M'), 5)
+        self.assertEqual(_parse_duration_element('T4H5M6.1234S', 'S'), 6.1234)
+        self.assertEqual(_parse_duration_element('PT4H54M6,5S', 'H'), 4)
+        self.assertEqual(_parse_duration_element('PT4H54M6,5S', 'M'), 54)
+        self.assertEqual(_parse_duration_element('PT4H54M6,5S', 'S'), 6.5)
+
+    def test_has_any_component(self):
+        self.assertTrue(_has_any_component('P1Y', ['Y', 'M']))
+        self.assertFalse(_has_any_component('P1Y', ['M', 'D']))
+
+    def test_component_order_correct(self):
+        self.assertTrue(_component_order_correct('P1Y1M1D', ['P', 'Y', 'M', 'D']))
+        self.assertTrue(_component_order_correct('P1Y1M', ['P', 'Y', 'M', 'D']))
+        self.assertFalse(_component_order_correct('P1D1Y1M', ['P', 'Y', 'M', 'D']))
+        self.assertFalse(_component_order_correct('PT1S1H', ['T', 'H', 'M', 'S']))
+
+class TestRelativeDurationParserFunctions(unittest.TestCase):
+    def test_parse_duration_relative(self):
+        resultduration = parse_duration('P1Y2M3DT4H54M6.5S', relative=True)
+        self.assertEqual(resultduration.years, 1)
+        self.assertEqual(resultduration.months, 2)
+        self.assertEqual(resultduration.days, 3)
+        self.assertEqual(resultduration.hours, 4)
+        self.assertEqual(resultduration.minutes, 54)
+        self.assertEqual(resultduration.seconds, 6.5)
+
+        resultduration = parse_duration('P0003-06-04T12:30:05.5', relative=True)
+        self.assertEqual(resultduration.years, 3)
+        self.assertEqual(resultduration.months, 6)
+        self.assertEqual(resultduration.days, 4)
+        self.assertEqual(resultduration.hours, 12)
+        self.assertEqual(resultduration.minutes, 30)
+        self.assertEqual(resultduration.seconds, 5)
+        self.assertEqual(resultduration.microseconds, 500000)
 
+    def test_parse_duration_prescribed_relative(self):
         resultduration = _parse_duration_prescribed('P1Y', True)
         self.assertEqual(resultduration.years, 1)
 
@@ -357,28 +353,66 @@ class TestDurationFunctions(unittest.TestCase):
         resultduration = _parse_duration_prescribed('P1.5W', True)
         self.assertEqual(resultduration.days, 10.5) #Fractional weeks are allowed
 
-    def test_parse_duration_combined(self):
-        #Don't allow garbage after the duration
-        #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
+    def test_parse_duration_prescribed_relative_multiplefractions(self):
         with self.assertRaises(ValueError):
-            parse_duration('P0003-06-04T12:30:05.5asdfasdf', True)
+            #Multiple fractions are not allowed
+            _parse_duration_prescribed('P1Y2M3DT4H5.1234M6.1234S', True)
 
-        resultduration = _parse_duration_combined('P0003-06-04T12:30:05', False)
-        self.assertEqual(resultduration.days, 1279)
-        self.assertEqual(resultduration.seconds, 45005)
-        self.assertEqual(resultduration.microseconds, 0)
+    def test_parse_duration_prescribed_relative_middlefraction(self):
+        with self.assertRaises(ValueError):
+            #Fraction only allowed on final component
+            _parse_duration_prescribed('P1Y2M3DT4H5.1234M6S', True)
 
-        resultduration = _parse_duration_combined('P0003-06-04T12:30:05.5', False)
-        self.assertEqual(resultduration.days, 1279)
-        self.assertEqual(resultduration.seconds, 45005)
-        self.assertEqual(resultduration.microseconds, 500000)
+    def test_parse_duration_prescribed_relative_outoforder(self):
+        #Ensure durations are required to be in the correct order
+        #https://bitbucket.org/nielsenb/aniso8601/issues/7/durations-with-time-components-before-t
+        #https://bitbucket.org/nielsenb/aniso8601/issues/8/durations-with-components-in-wrong-order
+        with self.assertRaises(ValueError):
+            parse_duration('P1S', True)
 
-    def test_parse_duration_combined_relative(self):
+        with self.assertRaises(ValueError):
+            parse_duration('P1D1S', True)
+
+        with self.assertRaises(ValueError):
+            parse_duration('P1H1M', True)
+
+        with self.assertRaises(ValueError):
+            parse_duration('1Y2M3D1SPT1M', True)
+
+        with self.assertRaises(ValueError):
+            parse_duration('P1Y2M3D2MT1S', True)
+
+        with self.assertRaises(ValueError):
+            parse_duration('P2M3D1ST1Y1M', True)
+
+        with self.assertRaises(ValueError):
+            parse_duration('P1Y2M2MT3D1S', True)
+
+        with self.assertRaises(ValueError):
+            parse_duration('P1D1Y1M', True)
+
+        with self.assertRaises(ValueError):
+            parse_duration('PT1S1H', True)
+
+    def test_parse_duration_prescribed_relative_suffixgarbage(self):
         #Don't allow garbage after the duration
         #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
         with self.assertRaises(ValueError):
-            parse_duration('P0003-06-04T12:30:05.5asdfasdf', True)
+            parse_duration('P1Dasdfasdf', True)
+
+    def test_parse_duration_prescribed_relative_fractionalyear(self):
+        #Fractional months and years are not defined
+        #https://github.com/dateutil/dateutil/issues/40
+        with self.assertRaises(ValueError):
+            _parse_duration_prescribed('P1.5Y', True)
+
+    def test_parse_duration_prescribed_relative_fractionalmonth(self):
+        #Fractional months and years are not defined
+        #https://github.com/dateutil/dateutil/issues/40
+        with self.assertRaises(ValueError):
+            _parse_duration_prescribed('P1.5M', True)
 
+    def test_parse_duration_combined_relative(self):
         resultduration = _parse_duration_combined('P0003-06-04T12:30:05', True)
         self.assertEqual(resultduration.years, 3)
         self.assertEqual(resultduration.months, 6)
@@ -396,23 +430,8 @@ class TestDurationFunctions(unittest.TestCase):
         self.assertEqual(resultduration.seconds, 5)
         self.assertEqual(resultduration.microseconds, 500000)
 
-    def test_parse_duration_element(self):
-        self.assertEqual(_parse_duration_element('P1Y2M3D', 'Y'), 1)
-        self.assertEqual(_parse_duration_element('P1Y2M3D', 'M'), 2)
-        self.assertEqual(_parse_duration_element('P1Y2M3D', 'D'), 3)
-        self.assertEqual(_parse_duration_element('T4H5M6.1234S', 'H'), 4)
-        self.assertEqual(_parse_duration_element('T4H5M6.1234S', 'M'), 5)
-        self.assertEqual(_parse_duration_element('T4H5M6.1234S', 'S'), 6.1234)
-        self.assertEqual(_parse_duration_element('PT4H54M6,5S', 'H'), 4)
-        self.assertEqual(_parse_duration_element('PT4H54M6,5S', 'M'), 54)
-        self.assertEqual(_parse_duration_element('PT4H54M6,5S', 'S'), 6.5)
-
-    def test_has_any_component(self):
-        self.assertTrue(_has_any_component('P1Y', ['Y', 'M']))
-        self.assertFalse(_has_any_component('P1Y', ['M', 'D']))
-
-    def test_component_order_correct(self):
-        self.assertTrue(_component_order_correct('P1Y1M1D', ['P', 'Y', 'M', 'D']))
-        self.assertTrue(_component_order_correct('P1Y1M', ['P', 'Y', 'M', 'D']))
-        self.assertFalse(_component_order_correct('P1D1Y1M', ['P', 'Y', 'M', 'D']))
-        self.assertFalse(_component_order_correct('PT1S1H', ['T', 'H', 'M', 'S']))
+    def test_parse_duration_combined_relative_suffixgarbage(self):
+        #Don't allow garbage after the duration
+        #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
+        with self.assertRaises(ValueError):
+            parse_duration('P0003-06-04T12:30:05.5asdfasdf', True)
diff --git a/aniso8601/tests/test_duration.pyc b/aniso8601/tests/test_duration.pyc
index 9e201d5..1601d0e 100644
Binary files a/aniso8601/tests/test_duration.pyc and b/aniso8601/tests/test_duration.pyc differ
diff --git a/aniso8601/tests/test_interval.py b/aniso8601/tests/test_interval.py
index a4eb555..74c2fe9 100644
--- a/aniso8601/tests/test_interval.py
+++ b/aniso8601/tests/test_interval.py
@@ -8,26 +8,13 @@
 
 import unittest
 import datetime
+import dateutil.relativedelta
 
 from aniso8601 import compat
-from aniso8601.interval import parse_interval, parse_repeating_interval
+from aniso8601.interval import parse_interval, parse_repeating_interval, _parse_interval_parts
 
-class TestIntervalFunctions(unittest.TestCase):
+class TestIntervalParserFunctions(unittest.TestCase):
     def test_parse_interval(self):
-        #Don't allow garbage after the duration
-        #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
-        with self.assertRaises(ValueError):
-            parse_interval('2001/P1Dasdf')
-
-        with self.assertRaises(ValueError):
-            parse_interval('P1Dasdf/2001')
-
-        with self.assertRaises(ValueError):
-            parse_interval('2001/P0003-06-04T12:30:05.5asdfasdf')
-
-        with self.assertRaises(ValueError):
-            parse_interval('P0003-06-04T12:30:05.5asdfasdf/2001')
-
         resultinterval = parse_interval('P1M/1981-04-05T01:01:00')
         self.assertEqual(resultinterval[0], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
         self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=3, day=6, hour=1, minute=1))
@@ -88,22 +75,23 @@ class TestIntervalFunctions(unittest.TestCase):
         self.assertEqual(resultinterval[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1))
         self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
 
-    def test_parse_interval_relative(self):
+    def test_parse_interval_suffixgarbage(self):
         #Don't allow garbage after the duration
         #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
         with self.assertRaises(ValueError):
-            parse_interval('2001/P1Dasdf', relative=True)
+            parse_interval('2001/P1Dasdf')
 
         with self.assertRaises(ValueError):
-            parse_interval('P1Dasdf/2001', relative=True)
+            parse_interval('P1Dasdf/2001')
 
         with self.assertRaises(ValueError):
-            parse_interval('2001/P0003-06-04T12:30:05.5asdfasdf', relative=True)
+            parse_interval('2001/P0003-06-04T12:30:05.5asdfasdf')
 
         with self.assertRaises(ValueError):
-            parse_interval('P0003-06-04T12:30:05.5asdfasdf/2001', relative=True)
-
+            parse_interval('P0003-06-04T12:30:05.5asdfasdf/2001')
 
+class TestRelativeIntervalParserFunctions(unittest.TestCase):
+    def test_parse_interval_relative(self):
         resultinterval = parse_interval('P1M/1981-04-05T01:01:00', relative=True)
         self.assertEqual(resultinterval[0], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
         self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=3, day=5, hour=1, minute=1))
@@ -198,15 +186,23 @@ class TestIntervalFunctions(unittest.TestCase):
         self.assertEqual(resultinterval[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1))
         self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
 
-    def test_parse_repeating_interval(self):
+    def test_parse_interval_relative_suffixgarbage(self):
         #Don't allow garbage after the duration
         #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
         with self.assertRaises(ValueError):
-            parse_interval('R3/1981-04-05/P1Dasdf')
+            parse_interval('2001/P1Dasdf', relative=True)
 
         with self.assertRaises(ValueError):
-            parse_interval('R3/1981-04-05/P0003-06-04T12:30:05.5asdfasdf')
+            parse_interval('P1Dasdf/2001', relative=True)
+
+        with self.assertRaises(ValueError):
+            parse_interval('2001/P0003-06-04T12:30:05.5asdfasdf', relative=True)
 
+        with self.assertRaises(ValueError):
+            parse_interval('P0003-06-04T12:30:05.5asdfasdf/2001', relative=True)
+
+class TestRepeatingIntervalParserFunctions(unittest.TestCase):
+    def test_parse_repeating_interval(self):
         results = list(parse_repeating_interval('R3/1981-04-05/P1D'))
         self.assertEqual(results[0], datetime.date(year=1981, month=4, day=5))
         self.assertEqual(results[1], datetime.date(year=1981, month=4, day=6))
@@ -230,15 +226,16 @@ class TestIntervalFunctions(unittest.TestCase):
         for dateindex in compat.range(0, 11):
              self.assertEqual(next(resultgenerator), datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1) - dateindex * datetime.timedelta(hours=1, minutes=2))
 
-    def test_parse_repeating_interval_relative(self):
+    def test_parse_repeating_interval_suffixgarbage(self):
         #Don't allow garbage after the duration
         #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
         with self.assertRaises(ValueError):
-            parse_interval('R3/1981-04-05/P1Dasdf', relative=True)
+            parse_interval('R3/1981-04-05/P1Dasdf')
 
         with self.assertRaises(ValueError):
             parse_interval('R3/1981-04-05/P0003-06-04T12:30:05.5asdfasdf')
 
+    def test_parse_repeating_interval_relative(self):
         results = list(parse_repeating_interval('R3/1981-04-05/P1D', relative=True))
         self.assertEqual(results[0], datetime.date(year=1981, month=4, day=5))
         self.assertEqual(results[1], datetime.date(year=1981, month=4, day=6))
@@ -257,7 +254,184 @@ class TestIntervalFunctions(unittest.TestCase):
         self.assertEqual(results[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1))
         self.assertEqual(results[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
 
+        #Make sure relative is correctly applied for months
+        #https://bitbucket.org/nielsenb/aniso8601/issues/12/month-intervals-calculated-incorrectly-or
+        results = list(parse_repeating_interval('R4/2017-04-30T00:00:00/P1M', relative=True))
+        self.assertEqual(results[0], datetime.datetime(year=2017, month=4, day=30))
+        self.assertEqual(results[1], datetime.datetime(year=2017, month=5, day=30))
+        self.assertEqual(results[2], datetime.datetime(year=2017, month=6, day=30))
+        self.assertEqual(results[3], datetime.datetime(year=2017, month=7, day=30))
+
         resultgenerator = parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00', relative=True)
 
         for dateindex in compat.range(0, 11):
              self.assertEqual(next(resultgenerator), datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1) - dateindex * datetime.timedelta(hours=1, minutes=2))
+
+    def test_parse_repeating_interval_relative_suffixgarbage(self):
+        #Don't allow garbage after the duration
+        #https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
+        with self.assertRaises(ValueError):
+            parse_interval('R3/1981-04-05/P1Dasdf', relative=True)
+
+        with self.assertRaises(ValueError):
+            parse_interval('R3/1981-04-05/P0003-06-04T12:30:05.5asdfasdf')
+
+class TestIntervalPartParserFunctions(unittest.TestCase):
+    def test_parse_interval_parts(self):
+        resultinterval = _parse_interval_parts('P1M/1981-04-05T01:01:00')
+        self.assertEqual(resultinterval[0], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
+        self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=3, day=6, hour=1, minute=1))
+        self.assertEqual(resultinterval[2], -datetime.timedelta(days=30))
+
+        resultinterval = _parse_interval_parts('P1M/1981-04-05')
+        self.assertEqual(resultinterval[0], datetime.date(year=1981, month=4, day=5))
+        self.assertEqual(resultinterval[1], datetime.date(year=1981, month=3, day=6))
+        self.assertEqual(resultinterval[2], -datetime.timedelta(days=30))
+
+        resultinterval = _parse_interval_parts('PT1H/2014-11-12')
+        self.assertEqual(resultinterval[0], datetime.date(year=2014, month=11, day=12))
+        self.assertEqual(resultinterval[1], datetime.datetime(year=2014, month=11, day=11, hour=23))
+        self.assertEqual(resultinterval[2], -datetime.timedelta(hours=1))
+
+        resultinterval = _parse_interval_parts('PT4H54M6.5S/2014-11-12')
+        self.assertEqual(resultinterval[0], datetime.date(year=2014, month=11, day=12))
+        self.assertEqual(resultinterval[1], datetime.datetime(year=2014, month=11, day=11, hour=19, minute=5, second=53, microsecond=500000))
+        self.assertEqual(resultinterval[2], -datetime.timedelta(hours=4, minutes=54, seconds=6.5))
+
+        resultinterval = _parse_interval_parts('1981-04-05T01:01:00/P1M1DT1M')
+        self.assertEqual(resultinterval[0], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
+        self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=5, day=6, hour=1, minute=2))
+        self.assertEqual(resultinterval[2], datetime.timedelta(days=31, minutes=1))
+
+        resultinterval = _parse_interval_parts('1981-04-05/P1M1D')
+        self.assertEqual(resultinterval[0], datetime.date(year=1981, month=4, day=5))
+        self.assertEqual(resultinterval[1], datetime.date(year=1981, month=5, day=6))
+        self.assertEqual(resultinterval[2], datetime.timedelta(days=31))
+
+        resultinterval = _parse_interval_parts('2014-11-12/PT1H')
+        self.assertEqual(resultinterval[0], datetime.date(year=2014, month=11, day=12))
+        self.assertEqual(resultinterval[1], datetime.datetime(year=2014, month=11, day=12, hour=1, minute=0))
+        self.assertEqual(resultinterval[2], datetime.timedelta(hours=1))
+
+        resultinterval = _parse_interval_parts('2014-11-12/PT4H54M6.5S')
+        self.assertEqual(resultinterval[0], datetime.date(year=2014, month=11, day=12))
+        self.assertEqual(resultinterval[1], datetime.datetime(year=2014, month=11, day=12, hour=4, minute=54, second=6, microsecond=500000))
+        self.assertEqual(resultinterval[2], datetime.timedelta(hours=4, minutes=54, seconds=6.5))
+
+        resultinterval = _parse_interval_parts('1980-03-05T01:01:00/1981-04-05T01:01:00')
+        self.assertEqual(resultinterval[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1))
+        self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
+        self.assertEqual(resultinterval[2], datetime.timedelta(days=396)) #One year, one month (30 days)
+
+        resultinterval = _parse_interval_parts('1980-03-05T01:01:00/1981-04-05')
+        self.assertEqual(resultinterval[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1))
+        self.assertEqual(resultinterval[1], datetime.date(year=1981, month=4, day=5))
+        self.assertEqual(resultinterval[2], datetime.timedelta(days=395, hours=22, minutes=59))
+
+        resultinterval = _parse_interval_parts('1980-03-05/1981-04-05T01:01:00')
+        self.assertEqual(resultinterval[0], datetime.date(year=1980, month=3, day=5))
+        self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))
+        self.assertEqual(resultinterval[2], datetime.timedelta(days=396, hours=1, minutes=1))
+
+        resultinterval = _parse_interval_parts('1980-03-05/1981-04-05')
+        self.assertEqual(resultinterval[0], datetime.date(year=1980, month=3, day=5))
+        self.assertEqual(resultinterval[1], datetime.date(year=1981, month=4, day=5))
+        self.assertEqual(resultinterval[2], datetime.timedelta(days=396))
+
... 386 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-aniso8601.git



More information about the Python-modules-commits mailing list