Repository: falconry/falcon
Pull request #1053 (2017-05-21 06:40:53)

Added a get_param_as_datetime() method to fetch query param as datetime
Solution to #1007.

codecov[bot]: # [Codecov](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=h1) Report
> Merging [#1053](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=desc) into [master](https://codecov.io/gh/falconry/falcon/commit/5da8aad15bb150889621c1eb122202865ee21416?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/falconry/falcon/pull/1053/graphs/tree.svg?width=650&src=pr&token=zOtkfNQ3h6&height=150)](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree)

```diff
@@          Coverage Diff           @@
##           master   #1053   +/-   ##
======================================
  Coverage     100%    100%           
======================================
  Files          31      30    -1     
  Lines        2190    2177   -13     
  Branches      339     341    +2     
======================================
- Hits         2190    2177   -13
```


| [Impacted Files](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [falcon/request.py](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree#diff-ZmFsY29uL3JlcXVlc3QucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [falcon/testing/helpers.py](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree#diff-ZmFsY29uL3Rlc3RpbmcvaGVscGVycy5weQ==) | `100% <0%> (ø)` | :arrow_up: |
| [falcon/testing/client.py](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree#diff-ZmFsY29uL3Rlc3RpbmcvY2xpZW50LnB5) | `100% <0%> (ø)` | :arrow_up: |
| [falcon/bench/queues/api.py](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree#diff-ZmFsY29uL2JlbmNoL3F1ZXVlcy9hcGkucHk=) | `100% <0%> (ø)` | :arrow_up: |
| [falcon/response.py](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree#diff-ZmFsY29uL3Jlc3BvbnNlLnB5) | `100% <0%> (ø)` | :arrow_up: |
| [falcon/bench/nuts/nuts/controllers/\_\_init\_\_.py](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree#diff-ZmFsY29uL2JlbmNoL251dHMvbnV0cy9jb250cm9sbGVycy9fX2luaXRfXy5weQ==) | `100% <0%> (ø)` | :arrow_up: |
| [falcon/constants/media\_types.py](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=tree#diff-ZmFsY29uL2NvbnN0YW50cy9tZWRpYV90eXBlcy5weQ==) | | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=footer). Last update [5da8aad...dd4e778](https://codecov.io/gh/falconry/falcon/pull/1053?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

kgritesh: @kgriffs fixed as per your review. Pls have a look

Patch:
diff --git a/falcon/request.py b/falcon/request.py
index 8b8f972..40ed7e4 100644
--- a/falcon/request.py
+++ b/falcon/request.py
@@ -1181,17 +1181,17 @@ class Request(object):
 
         raise errors.HTTPMissingParam(name)
 
-    def get_param_as_date(self, name, format_string='%Y-%m-%d',
-                          required=False, store=None):
-        """Return the value of a query string parameter as a date.
+    def get_param_as_datetime(self, name, format_string='%Y-%m-%dT%H:%M:%SZ',
+                              required=False, store=None):
+        """Return the value of a query string parameter as a datetime.
 
         Args:
             name (str): Parameter name, case-sensitive (e.g., 'ids').
 
         Keyword Args:
             format_string (str): String used to parse the param value
-                into a date. Any format recognized by strptime() is
-                supported (default ``"%Y-%m-%d"``).
+                into a datetime. Any format recognized by strptime() is
+                supported (default ``'%Y-%m-%dT%H:%M:%SZ'``).
             required (bool): Set to ``True`` to raise
                 ``HTTPBadRequest`` instead of returning ``None`` when the
                 parameter is not found (default ``False``).
@@ -1199,8 +1199,8 @@ class Request(object):
                 the value of the param, but only if the param is found (default
                 ``None``).
         Returns:
-            datetime.date: The value of the param if it is found and can be
-            converted to a ``date`` according to the supplied format
+            datetime.datetime: The value of the param if it is found and can be
+            converted to a ``datetime`` according to the supplied format
             string. If the param is not found, returns ``None`` unless
             required is ``True``.
 
@@ -1216,11 +1216,51 @@ class Request(object):
             return None
 
         try:
-            date = strptime(param_value, format_string).date()
+            date_time = strptime(param_value, format_string)
         except ValueError:
             msg = 'The date value does not match the required format.'
             raise errors.HTTPInvalidParam(msg, name)
 
+        if store is not None:
+            store[name] = date_time
+
+        return date_time
+
+    def get_param_as_date(self, name, format_string='%Y-%m-%d',
+                          required=False, store=None):
+        """Return the value of a query string parameter as a date.
+
+        Args:
+            name (str): Parameter name, case-sensitive (e.g., 'ids').
+
+        Keyword Args:
+            format_string (str): String used to parse the param value
+                into a date. Any format recognized by strptime() is
+                supported (default ``"%Y-%m-%d"``).
+            required (bool): Set to ``True`` to raise
+                ``HTTPBadRequest`` instead of returning ``None`` when the
+                parameter is not found (default ``False``).
+            store (dict): A ``dict``-like object in which to place
+                the value of the param, but only if the param is found (default
+                ``None``).
+        Returns:
+            datetime.date: The value of the param if it is found and can be
+            converted to a ``date`` according to the supplied format
+            string. If the param is not found, returns ``None`` unless
+            required is ``True``.
+
+        Raises:
+            HTTPBadRequest: A required param is missing from the request.
+            HTTPInvalidParam: A transform function raised an instance of
+                ``ValueError``.
+        """
+
+        date_time = self.get_param_as_datetime(name, format_string, required)
+        if date_time:
+            date = date_time.date()
+        else:
+            return None
+
         if store is not None:
             store[name] = date
 


Test patch:
diff --git a/tests/test_query_params.py b/tests/test_query_params.py
index 4be4674..e90567d 100644
--- a/tests/test_query_params.py
+++ b/tests/test_query_params.py
@@ -1,4 +1,5 @@
-from datetime import date
+from datetime import date, datetime
+
 try:
     import ujson as json
 except ImportError:
@@ -578,6 +579,52 @@ class TestQueryParams(object):
         with pytest.raises(HTTPInvalidParam):
             req.get_param_as_date('thedate', format_string=format_string)
 
+    def test_get_datetime_valid(self, simulate_request, client, resource):
+        client.app.add_route('/', resource)
+        date_value = '2015-04-20T10:10:10Z'
+        query_string = 'thedate={0}'.format(date_value)
+        simulate_request(client=client, path='/', query_string=query_string)
+        req = resource.captured_req
+        assert req.get_param_as_datetime('thedate') == datetime(2015, 4, 20, 10, 10, 10)
+
+    def test_get_datetime_missing_param(self, simulate_request, client, resource):
+        client.app.add_route('/', resource)
+        query_string = 'notthedate=2015-04-20T10:10:10Z'
+        simulate_request(client=client, path='/', query_string=query_string)
+        req = resource.captured_req
+        assert req.get_param_as_datetime('thedate') is None
+
+    def test_get_datetime_valid_with_format(self, simulate_request, client, resource):
+        client.app.add_route('/', resource)
+        date_value = '20150420 10:10:10'
+        query_string = 'thedate={0}'.format(date_value)
+        format_string = '%Y%m%d %H:%M:%S'
+        simulate_request(client=client, path='/', query_string=query_string)
+        req = resource.captured_req
+        assert req.get_param_as_datetime(
+            'thedate', format_string=format_string) == datetime(2015, 4, 20, 10, 10, 10)
+
+    def test_get_datetime_store(self, simulate_request, client, resource):
+        client.app.add_route('/', resource)
+        datetime_value = '2015-04-20T10:10:10Z'
+        query_string = 'thedate={0}'.format(datetime_value)
+        simulate_request(client=client, path='/', query_string=query_string)
+        req = resource.captured_req
+        store = {}
+        req.get_param_as_datetime('thedate', store=store)
+        assert len(store) != 0
+        assert store.get('thedate') == datetime(2015, 4, 20, 10, 10, 10)
+
+    def test_get_datetime_invalid(self, simulate_request, client, resource):
+        client.app.add_route('/', resource)
+        date_value = 'notarealvalue'
+        query_string = 'thedate={0}'.format(date_value)
+        format_string = '%Y%m%dT%H:%M:%S'
+        simulate_request(client=client, path='/', query_string=query_string)
+        req = resource.captured_req
+        with pytest.raises(HTTPInvalidParam):
+            req.get_param_as_datetime('thedate', format_string=format_string)
+
     def test_get_dict_valid(self, simulate_request, client, resource):
         client.app.add_route('/', resource)
         payload_dict = {'foo': 'bar'}