diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 4d4898e1a..2a182cfcf 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -306,6 +306,14 @@ May be a list including the string `'iso-8601'` or Python [strftime format][strf Default: `['iso-8601']` +#### DATETIME_TZ + +If set, used as the canonical timezone for `DateTimeField` serialization. If this is not set but Django timezone support is active, Django's timezone is instead used + +May be any of `None`, or a `datetime.tzinfo` object (such as `pytz.timezone('Asia/Kolkata')`) + +Default: None + #### DATE_FORMAT A format string that should be used by default for rendering the output of `DateField` serializer fields. If `None`, then `DateField` serializer fields will return Python `date` objects, and the date encoding will be determined by the renderer. diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 58e28ed4c..d5ff8110f 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1174,7 +1174,13 @@ class DateTimeField(Field): return value def default_timezone(self): - return timezone.get_current_timezone() if settings.USE_TZ else None + if settings.USE_TZ: + if api_settings.DATETIME_TZ: + return api_settings.DATETIME_TZ + else: + return timezone.get_current_timezone() + else: + return None def to_internal_value(self, value): input_formats = getattr(self, 'input_formats', api_settings.DATETIME_INPUT_FORMATS) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 6c581f8e8..8ccaeaae3 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -107,6 +107,7 @@ DEFAULTS = { 'DATETIME_FORMAT': ISO_8601, 'DATETIME_INPUT_FORMATS': (ISO_8601,), + 'DATETIME_TZ': None, 'TIME_FORMAT': ISO_8601, 'TIME_INPUT_FORMATS': (ISO_8601,), diff --git a/tests/test_fields.py b/tests/test_fields.py index eee794eaa..117daa487 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -14,6 +14,7 @@ from django.utils.timezone import activate, deactivate, override, utc import rest_framework from rest_framework import compat, serializers from rest_framework.fields import DjangoImageField, is_simple_callable +from rest_framework.settings import api_settings try: import pytz @@ -1280,6 +1281,42 @@ class TestTZWithDateTimeField(FieldValues): cls.field = serializers.DateTimeField(default_timezone=kolkata) +@pytest.mark.skipif(pytz is None, reason='pytz not installed') +@override_settings(TIME_ZONE='Asia/Kolkata', USE_TZ=True) +class TestDateTimeWithOverrideField(FieldValues, TestCase): + """ + Valid and invalid values for `DateTimeField` when not using UTC as the timezone. + """ + @classmethod + def setup_class(cls): + # use class setup method, as class-level attribute will still be evaluated even if test is skipped + kolkata = pytz.timezone('Asia/Kolkata') + paris = pytz.timezone('Europe/Paris') + # set the timezone to a specific timezone that isn't the django-level setting + cls.old_setting = api_settings.DATETIME_TZ + + api_settings.DATETIME_TZ = paris + + + cls.valid_inputs = { + '2016-12-19T10:00:00': paris.localize(datetime.datetime(2016, 12, 19, 10)), + # datetimes are reworked into the paris tz + '2016-12-19T10:00:00+05:30': paris.localize(datetime.datetime(2016, 12, 19, 5, 30)), + # naive datetimes are assumed to be in the paris tz + datetime.datetime(2016, 12, 19, 10): paris.localize(datetime.datetime(2016, 12, 19, 10)), + } + cls.invalid_inputs = {} + cls.outputs = { + datetime.datetime(2016, 12, 19, 10): '2016-12-19T10:00:00+01:00', + datetime.datetime(2016, 12, 19, 4, 30, tzinfo=utc): '2016-12-19T05:30:00+01:00', + } + cls.field = serializers.DateTimeField() + + @classmethod + def teardown_class(cls): + api_settings.DATETIME_TZ = cls.old_setting + + @pytest.mark.skipif(pytz is None, reason='pytz not installed') @override_settings(TIME_ZONE='UTC', USE_TZ=True) class TestDefaultTZDateTimeField(TestCase):