Repository: ckan/ckan

Issue:
Render_datetime can't handle dates before year 1900
Due to the python 2.7 feature datetime.strftime won't handle dates before year 1900 (http://bugs.python.org/issue1777412)

https://github.com/ckan/ckan/blob/master/ckan/lib/helpers.py#L947



Discussion:
What are the chances users would accept rendering ISO8601 format all the time? We wouldn't need strftime for that.

At least it should be documented that if users are allowed to input dates, use of render_datetime might create issues.

@Zharktas at the developer meeting today it was suggested to catch the exception for such dates and fall back to a manual rendering of YYYY-MM-DD in this helper. What do you think?

Sounds good, at least it would not cause an error anymore. We went with manual date render in our implementation: https://github.com/yhteentoimivuuspalvelut/ytp/blob/master/modules/ckanext-ytp-main/ckanext/ytp/common/helpers.py#L47 since we only need one date format.

@wardi, what is the progress of this one? can it be closed?
Still not fixed, and a really easy thing to contribute. I'll mark it as such
@wardi Can I take this one?
@klikstermkd absolutely! anything marked good for contribution is up for grabs

Resolving patch:
diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py
--- a/ckan/lib/helpers.py
+++ b/ckan/lib/helpers.py
@@ -1176,6 +1176,24 @@ def render_datetime(datetime_, date_format=None, with_hours=False):
 
     # if date_format was supplied we use it
     if date_format:
+
+        # See http://bugs.python.org/issue1777412
+        if datetime_.year < 1900:
+            year = str(datetime_.year)
+
+            date_format = re.sub('(?<!%)((%%)*)%y',
+                                 r'\g<1>{year}'.format(year=year[-2:]),
+                                 date_format)
+            date_format = re.sub('(?<!%)((%%)*)%Y',
+                                 r'\g<1>{year}'.format(year=year),
+                                 date_format)
+
+            datetime_ = datetime.datetime(2016, datetime_.month, datetime_.day,
+                                          datetime_.hour, datetime_.minute,
+                                          datetime_.second)
+
+            return datetime_.strftime(date_format)
+
         return datetime_.strftime(date_format)
     # the localised date
     return formatters.localised_nice_date(datetime_, show_date=True,


Test patch:
diff --git a/ckan/tests/legacy/lib/test_helpers.py b/ckan/tests/legacy/lib/test_helpers.py
--- a/ckan/tests/legacy/lib/test_helpers.py
+++ b/ckan/tests/legacy/lib/test_helpers.py
@@ -39,6 +39,20 @@ def test_render_datetime_blank(self):
         res = h.render_datetime(None)
         assert_equal(res, '')
 
+    def test_render_datetime_year_before_1900(self):
+        res = h.render_datetime('1875-04-13T20:40:20.123456', date_format='%Y')
+        assert_equal(res, '1875')
+
+        res = h.render_datetime('1875-04-13T20:40:20.123456', date_format='%y')
+        assert_equal(res, '75')
+
+    def test_render_datetime_year_before_1900_escape_percent(self):
+        res = h.render_datetime('1875-04-13', date_format='%%%y')
+        assert_equal(res, '%75')
+
+        res = h.render_datetime('1875-04-13', date_format='%%%Y')
+        assert_equal(res, '%1875')
+
     def test_datetime_to_date_str(self):
         res = datetime.datetime(2008, 4, 13, 20, 40, 20, 123456).isoformat()
         assert_equal(res, '2008-04-13T20:40:20.123456')