mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-02 11:30:12 +03:00
Support serializing date/datetime values before 1900 on python 2
Python 2 does not support strftime() for datetimes prior to 1900. This is fixed in Python 3.x but the python developers have signaled that they do not intend to backport the fix. The module django.utils.datetime_safe was written to workaround this issue. I've tried to use a light hand, only using the datetime_safe functions where serialization would otherwise throw a ValueError.
This commit is contained in:
parent
812d3478bd
commit
fce3b59953
|
@ -18,7 +18,7 @@ from django.core.validators import (
|
|||
)
|
||||
from django.forms import FilePathField as DjangoFilePathField
|
||||
from django.forms import ImageField as DjangoImageField
|
||||
from django.utils import six, timezone
|
||||
from django.utils import datetime_safe, six, timezone
|
||||
from django.utils.dateparse import (
|
||||
parse_date, parse_datetime, parse_duration, parse_time
|
||||
)
|
||||
|
@ -1152,6 +1152,12 @@ class DateTimeField(Field):
|
|||
self.timezone = default_timezone
|
||||
super(DateTimeField, self).__init__(*args, **kwargs)
|
||||
|
||||
def ensure_pre_1900_safe(self, value):
|
||||
if isinstance(value, datetime.date):
|
||||
if six.PY2 and value.year < 1900:
|
||||
value = datetime_safe.new_datetime(value)
|
||||
return value
|
||||
|
||||
def enforce_timezone(self, value):
|
||||
"""
|
||||
When `self.default_timezone` is `None`, always return naive datetimes.
|
||||
|
@ -1162,16 +1168,18 @@ class DateTimeField(Field):
|
|||
if field_timezone is not None:
|
||||
if timezone.is_aware(value):
|
||||
try:
|
||||
return value.astimezone(field_timezone)
|
||||
value = value.astimezone(field_timezone)
|
||||
except OverflowError:
|
||||
self.fail('overflow')
|
||||
try:
|
||||
return timezone.make_aware(value, field_timezone)
|
||||
except InvalidTimeError:
|
||||
self.fail('make_aware', timezone=field_timezone)
|
||||
else:
|
||||
try:
|
||||
value = timezone.make_aware(value, field_timezone)
|
||||
except InvalidTimeError:
|
||||
self.fail('make_aware', timezone=field_timezone)
|
||||
elif (field_timezone is None) and timezone.is_aware(value):
|
||||
return timezone.make_naive(value, utc)
|
||||
return value
|
||||
value = timezone.make_naive(value, utc)
|
||||
|
||||
return self.ensure_pre_1900_safe(value)
|
||||
|
||||
def default_timezone(self):
|
||||
return timezone.get_current_timezone() if settings.USE_TZ else None
|
||||
|
@ -1236,6 +1244,12 @@ class DateField(Field):
|
|||
self.input_formats = input_formats
|
||||
super(DateField, self).__init__(*args, **kwargs)
|
||||
|
||||
def ensure_pre_1900_safe(self, value):
|
||||
if isinstance(value, datetime.date):
|
||||
if six.PY2 and value.year < 1900:
|
||||
value = datetime_safe.new_date(value)
|
||||
return value
|
||||
|
||||
def to_internal_value(self, value):
|
||||
input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS)
|
||||
|
||||
|
@ -1243,7 +1257,7 @@ class DateField(Field):
|
|||
self.fail('datetime')
|
||||
|
||||
if isinstance(value, datetime.date):
|
||||
return value
|
||||
return self.ensure_pre_1900_safe(value)
|
||||
|
||||
for input_format in input_formats:
|
||||
if input_format.lower() == ISO_8601:
|
||||
|
@ -1253,14 +1267,14 @@ class DateField(Field):
|
|||
pass
|
||||
else:
|
||||
if parsed is not None:
|
||||
return parsed
|
||||
return self.ensure_pre_1900_safe(parsed)
|
||||
else:
|
||||
try:
|
||||
parsed = self.datetime_parser(value, input_format)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
else:
|
||||
return parsed.date()
|
||||
return self.ensure_pre_1900_safe(parsed.date())
|
||||
|
||||
humanized_format = humanize_datetime.date_formats(input_formats)
|
||||
self.fail('invalid', format=humanized_format)
|
||||
|
@ -1283,6 +1297,8 @@ class DateField(Field):
|
|||
'read-only field and deal with timezone issues explicitly.'
|
||||
)
|
||||
|
||||
value = self.ensure_pre_1900_safe(value)
|
||||
|
||||
if output_format.lower() == ISO_8601:
|
||||
return value.isoformat()
|
||||
|
||||
|
|
|
@ -1120,6 +1120,7 @@ class TestDateField(FieldValues):
|
|||
valid_inputs = {
|
||||
'2001-01-01': datetime.date(2001, 1, 1),
|
||||
datetime.date(2001, 1, 1): datetime.date(2001, 1, 1),
|
||||
'1800-01-01': datetime.date(1800, 1, 1),
|
||||
}
|
||||
invalid_inputs = {
|
||||
'abc': ['Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].'],
|
||||
|
@ -1129,6 +1130,8 @@ class TestDateField(FieldValues):
|
|||
outputs = {
|
||||
datetime.date(2001, 1, 1): '2001-01-01',
|
||||
'2001-01-01': '2001-01-01',
|
||||
datetime.date(1800, 1, 1): '1800-01-01',
|
||||
'1800-01-01': '1800-01-01',
|
||||
six.text_type('2016-01-10'): '2016-01-10',
|
||||
None: None,
|
||||
'': None,
|
||||
|
@ -1174,6 +1177,18 @@ class TestNoOutputFormatDateField(FieldValues):
|
|||
field = serializers.DateField(format=None)
|
||||
|
||||
|
||||
class TestPre1900DateField(FieldValues):
|
||||
"""
|
||||
Values for `DateField` prior to 1900
|
||||
"""
|
||||
valid_inputs = {}
|
||||
invalid_inputs = {}
|
||||
outputs = {
|
||||
datetime.date(1800, 1, 1): '01 Jan 1800',
|
||||
}
|
||||
field = serializers.DateField(format='%d %b %Y')
|
||||
|
||||
|
||||
class TestDateTimeField(FieldValues):
|
||||
"""
|
||||
Valid and invalid values for `DateTimeField`.
|
||||
|
@ -1348,6 +1363,18 @@ class TestNaiveDayLightSavingTimeTimeZoneDateTimeField(FieldValues):
|
|||
field = serializers.DateTimeField(default_timezone=MockTimezone())
|
||||
|
||||
|
||||
class TestPre1900DateTimeField(FieldValues):
|
||||
"""
|
||||
Values for `DateTimeField` prior to 1900.
|
||||
"""
|
||||
valid_inputs = {}
|
||||
invalid_inputs = {}
|
||||
outputs = {
|
||||
datetime.datetime(1800, 1, 1, 13, 00): '01:00PM, 01 Jan 1800',
|
||||
}
|
||||
field = serializers.DateTimeField(format='%I:%M%p, %d %b %Y')
|
||||
|
||||
|
||||
class TestTimeField(FieldValues):
|
||||
"""
|
||||
Valid and invalid values for `TimeField`.
|
||||
|
|
Loading…
Reference in New Issue
Block a user