diff --git a/opentech/apply/activity/messaging.py b/opentech/apply/activity/messaging.py
index d3c24d144d5e1136b2f149fe2daa09130169a697..e5e77f64e3d6c13ee7c1914795be104c0685b29d 100644
--- a/opentech/apply/activity/messaging.py
+++ b/opentech/apply/activity/messaging.py
@@ -7,6 +7,7 @@ from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth import get_user_model
 from django.template.loader import render_to_string
+from django.utils import timezone
 
 from .models import INTERNAL, PUBLIC
 from .options import MESSAGES
@@ -332,6 +333,7 @@ class ActivityAdapter(AdapterBase):
         Activity.actions.create(
             user=user,
             submission=submission,
+            timestamp=timezone.now(),
             message=message,
             visibility=visibility,
             related_object=related_object,
diff --git a/opentech/apply/activity/migrations/0022_add_versioning_to_comments.py b/opentech/apply/activity/migrations/0022_add_versioning_to_comments.py
index e883730f1d56a1767f021b2b37772d72ca56bd63..10034b8d281f0e15ab24d0a15c7c44ce5499551d 100644
--- a/opentech/apply/activity/migrations/0022_add_versioning_to_comments.py
+++ b/opentech/apply/activity/migrations/0022_add_versioning_to_comments.py
@@ -11,6 +11,11 @@ class Migration(migrations.Migration):
     ]
 
     operations = [
+        migrations.AlterField(
+            model_name='activity',
+            name='timestamp',
+            field=models.DateTimeField(),
+        ),
         migrations.AddField(
             model_name='activity',
             name='current',
diff --git a/opentech/apply/activity/models.py b/opentech/apply/activity/models.py
index fcd0c488f0fb63a9b5cd02b6c74eeb3389102ba9..089686efeeb4264c43c9e802eae6d69e720e480e 100644
--- a/opentech/apply/activity/models.py
+++ b/opentech/apply/activity/models.py
@@ -82,7 +82,7 @@ class ActionManager(ActivityBaseManager):
 
 
 class Activity(models.Model):
-    timestamp = models.DateTimeField(auto_now_add=True)
+    timestamp = models.DateTimeField()
     type = models.CharField(choices=ACTIVITY_TYPES.items(), max_length=30)
     user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
     submission = models.ForeignKey('funds.ApplicationSubmission', related_name='activities', on_delete=models.CASCADE)
diff --git a/opentech/apply/activity/tests/factories.py b/opentech/apply/activity/tests/factories.py
index 30c09ab31a5971a7300269b9af699093afeca13d..fb312efc0b9cd7bb634709535b9b320b45b09adb 100644
--- a/opentech/apply/activity/tests/factories.py
+++ b/opentech/apply/activity/tests/factories.py
@@ -1,6 +1,7 @@
 import uuid
 
 import factory
+from django.utils import timezone
 
 from opentech.apply.activity.models import Activity, Event, INTERNAL, Message, MESSAGES, REVIEWER
 from opentech.apply.funds.tests.factories import ApplicationSubmissionFactory
@@ -18,6 +19,7 @@ class CommentFactory(factory.DjangoModelFactory):
     submission = factory.SubFactory(ApplicationSubmissionFactory)
     user = factory.SubFactory(UserFactory)
     message = factory.Faker('sentence')
+    timestamp = factory.LazyFunction(timezone.now)
 
     @classmethod
     def _get_manager(cls, model_class):
diff --git a/opentech/apply/activity/views.py b/opentech/apply/activity/views.py
index 07ccbd32c0358a21384d9ac4eab48519af0808e9..cba63a229760a14bc68c2d3907b140cd876ecf89 100644
--- a/opentech/apply/activity/views.py
+++ b/opentech/apply/activity/views.py
@@ -1,4 +1,5 @@
 from django.views.generic import CreateView
+from django.utils import timezone
 
 from opentech.apply.utils.views import DelegatedViewMixin
 
@@ -57,6 +58,7 @@ class CommentFormView(DelegatedViewMixin, CreateView):
         form.instance.user = self.request.user
         form.instance.submission = self.kwargs['submission']
         form.instance.type = COMMENT
+        form.instance.timestamp = timezone.now()
         response = super().form_valid(form)
         messenger(
             MESSAGES.COMMENT,
diff --git a/opentech/apply/determinations/views.py b/opentech/apply/determinations/views.py
index 0adae9a6fe7c05839464e4dfbf46a2877ad067ab..a5e65f32d0d16335708f4c0cd52d278dfaf6280a 100644
--- a/opentech/apply/determinations/views.py
+++ b/opentech/apply/determinations/views.py
@@ -6,6 +6,7 @@ from django.core.exceptions import PermissionDenied
 from django.http import HttpResponseRedirect
 from django.shortcuts import get_object_or_404
 from django.urls import reverse_lazy
+from django.utils import timezone
 from django.utils.decorators import method_decorator
 from django.utils.translation import ugettext_lazy as _
 from django.views.generic import DetailView, CreateView
@@ -135,6 +136,7 @@ class BatchDeterminationCreateView(CreateView):
                     # We keep a record of the message sent to the user in the comment
                     Activity.comments.create(
                         message=determination.stripped_message,
+                        timestamp=timezone.now(),
                         user=self.request.user,
                         submission=submission,
                         related_object=determination,
@@ -252,6 +254,7 @@ class DeterminationCreateOrUpdateView(CreateOrUpdateView):
                 # We keep a record of the message sent to the user in the comment
                 Activity.comments.create(
                     message=self.object.stripped_message,
+                    timestamp=timezone.now(),
                     user=self.request.user,
                     submission=self.submission,
                     related_object=self.object,
diff --git a/opentech/apply/funds/serializers.py b/opentech/apply/funds/serializers.py
index 7b12eb0842d62b4fe12a3b878d75348b536bd81e..d76d8ad1c849108aac8f101830c3616a73186dc3 100644
--- a/opentech/apply/funds/serializers.py
+++ b/opentech/apply/funds/serializers.py
@@ -232,10 +232,11 @@ class CommentCreateSerializer(serializers.ModelSerializer):
     class Meta:
         model = Activity
         fields = ('id', 'timestamp', 'user', 'message', 'visibility', 'edited')
+        read_only_fields = ('timestamp', 'edited',)
 
 
 class CommentEditSerializer(CommentCreateSerializer):
     user = serializers.StringRelatedField()
 
     class Meta(CommentCreateSerializer.Meta):
-        read_only_fields = ('visibility', 'edited',)
+        read_only_fields = ('timestamp', 'visibility', 'edited',)
diff --git a/opentech/apply/funds/tests/test_api_views.py b/opentech/apply/funds/tests/test_api_views.py
index 68251a09e5110500728dfde2941cd3ea915d6b13..df9cb2aab1c150399bdf0d9118102b2f8de98026 100644
--- a/opentech/apply/funds/tests/test_api_views.py
+++ b/opentech/apply/funds/tests/test_api_views.py
@@ -1,5 +1,6 @@
 from django.test import TestCase, override_settings
 from django.urls import reverse_lazy
+from django.utils import timezone
 
 from opentech.apply.activity.models import Activity, PUBLIC, PRIVATE
 from opentech.apply.activity.tests.factories import CommentFactory
@@ -30,16 +31,23 @@ class TestCommentEdit(TestCase):
 
         response = self.post_to_edit(comment.pk, new_message)
 
-        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.status_code, 200, response.json())
         self.assertEqual(Activity.objects.count(), 2)
 
         comment.refresh_from_db()
+
+        # Match the behaviour of DRF
+        time = comment.timestamp.astimezone(timezone.get_current_timezone()).isoformat()
+        if time.endswith('+00:00'):
+            time = time[:-6] + 'Z'
+
+        self.assertEqual(time, response.json()['timestamp'])
         self.assertFalse(comment.current)
         self.assertEqual(response.json()['message'], new_message)
 
     def test_incorrect_id_denied(self):
         response = self.post_to_edit(10000)
-        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.status_code, 403, response.json())
 
     def test_does_nothing_if_same_message(self):
         user = UserFactory()
@@ -63,5 +71,20 @@ class TestCommentEdit(TestCase):
             },
         )
 
-        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.status_code, 200, response.json())
         self.assertEqual(response.json()['visibility'], PRIVATE)
+
+    def test_out_of_order_does_nothing(self):
+        user = UserFactory()
+        comment = CommentFactory(user=user)
+        self.client.force_login(user)
+
+        new_message = 'hi there'
+        newer_message = 'hello there'
+
+        response_one = self.post_to_edit(comment.pk, new_message)
+        response_two = self.post_to_edit(comment.pk, newer_message)
+
+        self.assertEqual(response_one.status_code, 200, response_one.json())
+        self.assertEqual(response_two.status_code, 404, response_two.json())
+        self.assertEqual(Activity.objects.count(), 2)