Repository: learningequality/kolibri
Issue: DateTimeTzFields are serialized with incorrect times

Example saved this time in a `ChannelMetaDataCache` object (at around 14:00 local time)

`2017-06-06 14:44:12.582879(America/Los_Angeles)`

But it gets serialized as

`2017-06-06T07:44:12.582-07:00`

Expected is

`2017-06-06T14:44:12.582-07:00`


Changed files:
kolibri/core/test/test_datetimetzfield.py, kolibri/core/fields.py, kolibri/core/serializers.py

diff --git a/kolibri/core/fields.py b/kolibri/core/fields.py
index 825639a0f2..9ccbf5bbc0 100644
--- a/kolibri/core/fields.py
+++ b/kolibri/core/fields.py
@@ -25,7 +25,8 @@ def create_timezonestamp(value):
         tz = value.tzinfo.zone
     else:
         tz = timezone.get_current_timezone().zone
-    date_time_string = value.strftime(date_time_format)
+        value = timezone.make_aware(value, timezone.get_current_timezone())
+    date_time_string = value.astimezone(pytz.utc).strftime(date_time_format)
     tz_string = tz_format.format(tz=tz)
     value = db_storage_string.format(date_time_string=date_time_string, tz_string=tz_string)
     return value
diff --git a/kolibri/core/serializers.py b/kolibri/core/serializers.py
index 73455c63d1..f738a3591e 100644
--- a/kolibri/core/serializers.py
+++ b/kolibri/core/serializers.py
@@ -1,3 +1,5 @@
+import pytz
+
 from django.utils import timezone
 from rest_framework.serializers import DateTimeField, ModelSerializer
 from .fields import DateTimeTzField as DjangoDateTimeTzField
@@ -7,6 +9,8 @@ class DateTimeTzField(DateTimeField):
     def to_internal_value(self, data):
         data = super(DateTimeTzField, self).to_internal_value(data)
         tz = timezone.get_current_timezone()
+        if not data.tzinfo:
+            data = timezone.make_aware(data, pytz.utc)
         return data.astimezone(tz)
 
 
diff --git a/kolibri/core/test/test_datetimetzfield.py b/kolibri/core/test/test_datetimetzfield.py
index 4d007f493d..14f48c6ac6 100644
--- a/kolibri/core/test/test_datetimetzfield.py
+++ b/kolibri/core/test/test_datetimetzfield.py
@@ -4,7 +4,7 @@ import datetime
 import pytz
 
 from django.db import models
-from django.test import TestCase
+from django.test import override_settings, TestCase
 from django.utils import timezone
 from kolibri.core.fields import DateTimeTzField
 from kolibri.core.serializers import DateTimeTzField as DateTimeTzSerializerField
@@ -17,7 +17,8 @@ class DateTimeTzModel(models.Model):
     default_timestamp = DateTimeTzField(default=aware_datetime)
 
 
-class DateTimeTzFieldTestCase(TestCase):
+@override_settings(USE_TZ=True)
+class AwareDateTimeTzFieldTestCase(TestCase):
 
     def test_timestamp_utc_create(self):
         timezone.activate(pytz.utc)
@@ -25,6 +26,14 @@ class DateTimeTzFieldTestCase(TestCase):
         self.assertEqual(obj.timestamp.tzinfo, aware_datetime().tzinfo)
         timezone.deactivate()
 
+    def test_timestamp_utc_read(self):
+        # Regression test for https://github.com/learningequality/kolibri/issues/1602
+        timezone.activate(pytz.utc)
+        obj = DateTimeTzModel.objects.create(timestamp=aware_datetime())
+        obj.refresh_from_db()
+        self.assertEqual(obj.timestamp, aware_datetime())
+        timezone.deactivate()
+
     def test_timestamp_arbitrary_create(self):
         tz = pytz.timezone('Africa/Nairobi')
         timezone.activate(tz)
@@ -33,6 +42,16 @@ class DateTimeTzFieldTestCase(TestCase):
         self.assertEqual(obj.timestamp.tzinfo, timestamp.tzinfo)
         timezone.deactivate()
 
+    def test_timestamp_arbitrary_read(self):
+        # Regression test for https://github.com/learningequality/kolibri/issues/1602
+        tz = pytz.timezone('Africa/Nairobi')
+        timezone.activate(tz)
+        timestamp = aware_datetime()
+        obj = DateTimeTzModel.objects.create(timestamp=timestamp)
+        obj.refresh_from_db()
+        self.assertEqual(obj.timestamp, timestamp)
+        timezone.deactivate()
+
     def test_default_utc_create(self):
         timezone.activate(pytz.utc)
         obj = DateTimeTzModel.objects.create()
@@ -48,6 +67,26 @@ class DateTimeTzFieldTestCase(TestCase):
         timezone.deactivate()
 
 
+@override_settings(USE_TZ=False)
+class NaiveDateTimeTzFieldTestCase(TestCase):
+
+    def test_timestamp_create(self):
+        obj = DateTimeTzModel.objects.create(timestamp=aware_datetime())
+        self.assertEqual(obj.timestamp.tzinfo, aware_datetime().tzinfo)
+
+    def test_timestamp_utc_read(self):
+        # Regression test for https://github.com/learningequality/kolibri/issues/1602
+        obj = DateTimeTzModel.objects.create(timestamp=aware_datetime())
+        obj.refresh_from_db()
+        self.assertEqual(obj.timestamp, aware_datetime())
+
+    def test_default_utc_create(self):
+        timezone.activate(pytz.utc)
+        obj = DateTimeTzModel.objects.create()
+        self.assertEqual(obj.default_timestamp.tzinfo, pytz.utc)
+        timezone.deactivate()
+
+
 class DateTimeTzSerializerFieldTestCase(TestCase):
 
     def test_timestamp_utc_parse(self):