diff --git a/json_field/fields.py b/json_field/fields.py index 4566fb0..38b7a13 100644 --- a/json_field/fields.py +++ b/json_field/fields.py @@ -9,27 +9,36 @@ from django.utils import simplejson as json from django.db import models -from django.core import exceptions from django.utils.translation import ugettext_lazy as _ -from django.core.exceptions import ImproperlyConfigured +from django.utils.dateparse import parse_date, parse_datetime import re import decimal import datetime import six -try: - from dateutil import parser as date_parser -except ImportError: - raise ImproperlyConfigured('The "dateutil" library is required and was not found.') try: JSON_DECODE_ERROR = json.JSONDecodeError # simplejson except AttributeError: JSON_DECODE_ERROR = ValueError # other -TIME_RE = re.compile(r'^\d{2}:\d{2}:\d{2}') -DATE_RE = re.compile(r'^\d{4}-\d{2}-\d{2}(?!T)') -DATETIME_RE = re.compile(r'^\d{4}-\d{2}-\d{2}T') + +# django pure implementation has missed trailing $ in time regex, so reimplement it +time_re = re.compile( + r'(?P\d{1,2}):(?P\d{1,2})' + r'(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?$' +) + + +def parse_time(value): + match = time_re.match(value) + if match: + kw = match.groupdict() + if kw['microsecond']: + kw['microsecond'] = kw['microsecond'].ljust(6, '0') + kw = dict((k, int(v)) for k, v in six.iteritems(kw) if v is not None) + return datetime.time(**kw) + class JSONEncoder(json.JSONEncoder): """ @@ -79,21 +88,12 @@ def decode(self, obj, *args, **kwargs): if self._is_recursive(value): obj[key] = self.decode(value, recurse=True) elif isinstance(obj, six.string_types): - if TIME_RE.match(obj): - try: - return date_parser.parse(obj).time() - except ValueError: - pass - if DATE_RE.match(obj): - try: - return date_parser.parse(obj).date() - except ValueError: - pass - if DATETIME_RE.match(obj): - try: - return date_parser.parse(obj) - except ValueError: - pass + try: + dt = parse_time(obj) or parse_date(obj) or parse_datetime(obj) + except ValueError: + dt = None + if dt: + return dt return obj class Creator(object): diff --git a/test_project/app/tests.py b/test_project/app/tests.py index d34d264..6a013d9 100644 --- a/test_project/app/tests.py +++ b/test_project/app/tests.py @@ -2,7 +2,7 @@ import inspect -from json_field.fields import JSON_DECODE_ERROR +from json_field.fields import JSONDecoder, JSON_DECODE_ERROR from test_project.app.models import Test from test_project.app.forms import TestForm, OptionalForm, \ @@ -183,3 +183,50 @@ def test_creator_plays_nice_with_module_inspect(self): # invisible for inspection. data = dict(inspect.getmembers(Test)) self.assertIn('json', data) + + def test_decode_date(self): + decode = JSONDecoder().decode + + # Valid inputs + self.assertEqual(decode('"2012-04-23"'), datetime.date(2012, 4, 23)) + self.assertEqual(decode('"2012-4-9"'), datetime.date(2012, 4, 9)) + + # Invalid inputs + self.assertEqual(decode('"20120423"'), '20120423') + self.assertEqual(decode('"2012-04-56"'), '2012-04-56') + + # Invalid formatting + self.assertEqual(decode('"2012-04-23 | Test"'), '2012-04-23 | Test') + + def test_decode_time(self): + decode = JSONDecoder().decode + + # Valid inputs + self.assertEqual(decode('"09:15:00"'), datetime.time(9, 15)) + self.assertEqual(decode('"10:10"'), datetime.time(10, 10)) + self.assertEqual(decode('"10:20:30.400"'), datetime.time(10, 20, 30, 400000)) + self.assertEqual(decode('"4:8:16"'), datetime.time(4, 8, 16)) + + # Invalid inputs + self.assertEqual(decode('"091500"'), '091500') + self.assertEqual(decode('"09:15:90"'), '09:15:90') + + # Invalid formatting + self.assertEqual(decode('"09:15:00 | Test"'), '09:15:00 | Test') + + def test_decode_datetime(self): + decode = JSONDecoder().decode + + # Valid inputs + self.assertEqual(decode('"2012-04-23T09:15:00"'), + datetime.datetime(2012, 4, 23, 9, 15)) + self.assertEqual(decode('"2012-4-9 4:8:16"'), + datetime.datetime(2012, 4, 9, 4, 8, 16)) + + # Invalid inputs + self.assertEqual(decode('"20120423091500"'), '20120423091500') + self.assertEqual(decode('"2012-04-56T09:15:90"'), '2012-04-56T09:15:90') + + # Invalid formatting + self.assertEqual(decode('"2012-04-23T09:15:00 | Test"'), + '2012-04-23T09:15:00 | Test')