Commits

Brandon Nielsen committed dcf8392

Start if ISO week date parsing.

Comments (0)

Files changed (2)

aniso8601/__init__.py

     #to 1900-01-01
     #
     #Since no additional resolution is provided, the month is set to 1, and
-    #day is set to 1.
+    #day is set to 1
 
     if len(yearstr) == 4:
         return datetime.date(int(yearstr), 1, 1)
         return parseddatetime.date()
     else:
         raise ValueError('String is not a valid ISO8601 calendar date.')
+
+def parse_week_date(datestr):
+    #datestr is of the format YYYY-Www, YYYYWww, YYYY-Www-D, YYYYWwwD
+    #
+    #W is the week number prefix, ww is the week number, between 1 and 53
+    #0 is not a valid week number, which differs from the Python implementation
+    #
+    #D is the weekday number, between 1 and 7, which differs from the Python
+    #implementation which is between 0 and 6
+
+    isoyear = int(datestr[0:4])
+    gregorianyearstart = _iso_year_start(isoyear)
+
+    #Week number will be the two characters after the W
+    windex = datestr.find('W')
+    isoweeknumber = int(datestr[windex + 1:windex + 3])
+
+    if isoweeknumber == 0:
+        raise ValueError('00 is not a valid ISO8601 weeknumber.')
+
+    datestrlen = len(datestr)
+
+    if datestr.find('-') != -1:
+        if datestrlen == 8:
+            #YYYY-Www
+            #Suss out the date
+            return gregorianyearstart + datetime.timedelta(weeks=isoweeknumber - 1, days=0)
+        elif datestrlen == 10:
+            #YYYY-Www-D
+            isoday = int(datestr[9:10])
+
+            return gregorianyearstart + datetime.timedelta(weeks=isoweeknumber - 1, days=isoday - 1)
+        else:
+            raise ValueError('String is not a valid ISO8601 week date.')
+    else:
+        if datestrlen == 7:
+            #YYYYWww
+            return gregorianyearstart + datetime.timedelta(weeks=isoweeknumber - 1, days=0)
+        elif datestrlen == 8:
+            #YYYYWwwD
+            isoday = int(datestr[7:8])
+
+            return gregorianyearstart + datetime.timedelta(weeks=isoweeknumber - 1, days=isoday - 1)
+        else:
+            raise ValueError('String is not a valid ISO8601 week date.')
+
+def _iso_year_start(isoyear):
+    #Given an ISO year, returns the equivalent of the start of the year on the
+    #Gregorian calendar (which is used by Python)
+    #Stolen from:
+    #http://stackoverflow.com/questions/304256/whats-the-best-way-to-find-the-inverse-of-datetime-isocalendar
+
+    #Determine the location of the 4th of January, the first week of the ISO
+    #year in the week containing the 4th of January
+    #http://en.wikipedia.org/wiki/ISO_week_date
+    fourth_jan = datetime.date(isoyear, 1, 4)
+
+    #Note the conversion from ISO day (1 - 7) and Python day (0 - 6)
+    delta = datetime.timedelta(fourth_jan.isoweekday() - 1)
+
+    #Return the start of the year
+    return fourth_jan - delta

aniso8601/test_aniso8601.py

 
         with self.assertRaises(ValueError):
             aniso8601.parse_calendar_date('198104')
+
+    def test_parse_week_date(self):
+        date = aniso8601.parse_week_date('2004-W53')
+        self.assertEqual(date.year, 2004)
+        self.assertEqual(date.month, 12)
+        self.assertEqual(date.weekday(), 0)
+
+        date = aniso8601.parse_week_date('2009-W01')
+        self.assertEqual(date.year, 2008)
+        self.assertEqual(date.month, 12)
+        self.assertEqual(date.weekday(), 0)
+
+        date = aniso8601.parse_week_date('2009-W53')
+        self.assertEqual(date.year, 2009)
+        self.assertEqual(date.month, 12)
+        self.assertEqual(date.weekday(), 0)
+
+        date = aniso8601.parse_week_date('2010-W01')
+        self.assertEqual(date.year, 2010)
+        self.assertEqual(date.month, 1)
+        self.assertEqual(date.weekday(), 0)
+
+        date = aniso8601.parse_week_date('2004-W53-6')
+        self.assertEqual(date.year, 2005)
+        self.assertEqual(date.month, 1)
+        self.assertEqual(date.day, 1)
+
+        date = aniso8601.parse_week_date('2009-W01-1')
+        self.assertEqual(date.year, 2008)
+        self.assertEqual(date.month, 12)
+        self.assertEqual(date.day, 29)
+
+        date = aniso8601.parse_week_date('2009-W53-7')
+        self.assertEqual(date.year, 2010)
+        self.assertEqual(date.month, 1)
+        self.assertEqual(date.day, 3)
+
+        date = aniso8601.parse_week_date('2010-W01-1')
+        self.assertEqual(date.year, 2010)
+        self.assertEqual(date.month, 1)
+        self.assertEqual(date.day, 4)
+
+        date = aniso8601.parse_week_date('2004W53')
+        self.assertEqual(date.year, 2004)
+        self.assertEqual(date.month, 12)
+        self.assertEqual(date.weekday(), 0)
+
+        date = aniso8601.parse_week_date('2009W01')
+        self.assertEqual(date.year, 2008)
+        self.assertEqual(date.month, 12)
+        self.assertEqual(date.weekday(), 0)
+
+        date = aniso8601.parse_week_date('2009W53')
+        self.assertEqual(date.year, 2009)
+        self.assertEqual(date.month, 12)
+        self.assertEqual(date.weekday(), 0)
+
+        date = aniso8601.parse_week_date('2010W01')
+        self.assertEqual(date.year, 2010)
+        self.assertEqual(date.month, 1)
+        self.assertEqual(date.weekday(), 0)
+
+        date = aniso8601.parse_week_date('2004W536')
+        self.assertEqual(date.year, 2005)
+        self.assertEqual(date.month, 1)
+        self.assertEqual(date.day, 1)
+
+        date = aniso8601.parse_week_date('2009W011')
+        self.assertEqual(date.year, 2008)
+        self.assertEqual(date.month, 12)
+        self.assertEqual(date.day, 29)
+
+        date = aniso8601.parse_week_date('2009W537')
+        self.assertEqual(date.year, 2010)
+        self.assertEqual(date.month, 1)
+        self.assertEqual(date.day, 3)
+
+        date = aniso8601.parse_week_date('2010W011')
+        self.assertEqual(date.year, 2010)
+        self.assertEqual(date.month, 1)
+        self.assertEqual(date.day, 4)