diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 45ac498417..168bccf83c 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -275,6 +275,14 @@ def pygments_highlight(text, lang, style): def pygments_css(style): return None + +try: + import pytz + from pytz.exceptions import InvalidTimeError +except ImportError: + InvalidTimeError = Exception + + # `separators` argument to `json.dumps()` differs between 2.x and 3.x # See: http://bugs.python.org/issue22767 if six.PY3: @@ -339,6 +347,7 @@ def set_many(instance, field, value): field = getattr(instance, field) field.set(value) + def include(module, namespace=None, app_name=None): from django.conf.urls import include if django.VERSION < (1,9): diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 5a881c772a..7ee3f10167 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -33,7 +33,8 @@ from rest_framework import ISO_8601 from rest_framework.compat import ( - get_remote_field, unicode_repr, unicode_to_repr, value_from_object + InvalidTimeError, get_remote_field, unicode_repr, unicode_to_repr, + value_from_object ) from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings @@ -1085,6 +1086,7 @@ class DateTimeField(Field): default_error_messages = { 'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}.'), 'date': _('Expected a datetime but got a date.'), + 'make_aware': _('Invalid datetime for the timezone "{timezone}".') } datetime_parser = datetime.datetime.strptime @@ -1105,7 +1107,10 @@ def enforce_timezone(self, value): field_timezone = getattr(self, 'timezone', self.default_timezone()) if (field_timezone is not None) and not timezone.is_aware(value): - return timezone.make_aware(value, field_timezone) + try: + return 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 diff --git a/tests/test_fields.py b/tests/test_fields.py index 16221d4cca..968c41d3f8 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -12,7 +12,7 @@ from django.utils.timezone import utc import rest_framework -from rest_framework import serializers +from rest_framework import compat, serializers from rest_framework.fields import is_simple_callable try: @@ -1205,6 +1205,30 @@ class TestNaiveDateTimeField(FieldValues): field = serializers.DateTimeField(default_timezone=None) +class TestNaiveDayLightSavingTimeTimeZoneDateTimeField(FieldValues): + """ + Invalid values for `DateTimeField` with datetime in DST shift (non-existing or ambiguous) and timezone with DST. + Timezone America/New_York has DST shift from 2017-03-12T02:00:00 to 2017-03-12T03:00:00 and + from 2017-11-05T02:00:00 to 2017-11-05T01:00:00 in 2017. + """ + valid_inputs = {} + invalid_inputs = { + '2017-03-12T02:30:00': ['Invalid datetime for the timezone "America/New_York".'], + '2017-11-05T01:30:00': ['Invalid datetime for the timezone "America/New_York".'] + } + outputs = {} + + class MockTimezone: + @staticmethod + def localize(value, is_dst): + raise compat.InvalidTimeError() + + def __str__(self): + return 'America/New_York' + + field = serializers.DateTimeField(default_timezone=MockTimezone()) + + class TestTimeField(FieldValues): """ Valid and invalid values for `TimeField`.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: