diff --git a/djblets/util/serializers.py b/djblets/util/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a7ac969279fd491bc47aa419d81315c4941d2db
--- /dev/null
+++ b/djblets/util/serializers.py
@@ -0,0 +1,34 @@
+import datetime
+
+from django.core.serializers.json import DjangoJSONEncoder
+from django.utils.encoding import force_text
+from django.utils.functional import Promise
+
+
+class DjbletsJSONEncoder(DjangoJSONEncoder):
+    """Encodes data into JSON-compatible structures.
+
+    This is a specialization of DjangoJSONEncoder that converts
+    lazily ugettext strings to real strings, and chops off the milliseconds
+    and microseconds of datetimes.
+    """
+    def default(self, obj):
+        if isinstance(obj, Promise):
+            # Handles initializing lazily created ugettext messages.
+            return force_text(obj)
+        elif isinstance(obj, datetime.datetime):
+            # This is like DjangoJSONEncoder's datetime encoding
+            # implementation, except that it filters out the milliseconds
+            # in addition to microseconds. This ensures consistency between
+            # database-stored timestamps and serialized objects.
+            r = obj.isoformat()
+
+            if obj.microsecond:
+                r = r[:19] + r[26:]
+
+            if r.endswith('+00:00'):
+                r = r[:-6] + 'Z'
+
+            return r
+
+        return super(DjbletsJSONEncoder, self).default(obj)
diff --git a/djblets/util/templatetags/djblets_js.py b/djblets/util/templatetags/djblets_js.py
index 0eab82f08f96f6d5987751e72d1627d91cca0746..85056efea8680f5421203c69cc6bfe8d61c4ba4c 100644
--- a/djblets/util/templatetags/djblets_js.py
+++ b/djblets/util/templatetags/djblets_js.py
@@ -29,30 +29,14 @@ import json
 
 from django import template
 from django.core.serializers import serialize
-from django.core.serializers.json import DjangoJSONEncoder
 from django.db.models.query import QuerySet
 from django.utils import six
-from django.utils.encoding import force_text
-from django.utils.functional import Promise
 from django.utils.safestring import mark_safe
 
+from djblets.util.serializers import DjbletsJSONEncoder
 
-register = template.Library()
-
-
-class LazyEncoder(DjangoJSONEncoder):
-    """
-    Encoder to take care of initializing lazily created ugettext messages.
 
-    In the case of lazy translation objects, they need to be converted to
-    strings before passed as a JSON object. If the passed object is a
-    Promise, do a force_text on it to resolve the Promise.
-    """
-    def default(self, obj):
-        if isinstance(obj, Promise):
-            return force_text(obj)
-
-        return super(LazyEncoder, self).default(obj)
+register = template.Library()
 
 
 @register.simple_tag
@@ -89,6 +73,6 @@ def json_dumps(value, indent=None):
     if isinstance(value, QuerySet):
         result = serialize('json', value, indent=indent)
     else:
-        result = json.dumps(value, indent=indent, cls=LazyEncoder)
+        result = json.dumps(value, indent=indent, cls=DjbletsJSONEncoder)
 
     return mark_safe(result)
diff --git a/djblets/webapi/encoders.py b/djblets/webapi/encoders.py
index 3a2e6ffe99de2dd521839e4c641b826d1bff8b99..7e91fc667e9389dee790c5e7801961181235f3a7 100644
--- a/djblets/webapi/encoders.py
+++ b/djblets/webapi/encoders.py
@@ -1,28 +1,12 @@
 from __future__ import unicode_literals
 
-import datetime
-
 from django.contrib.auth.models import User, Group
-from django.core.serializers.json import DjangoJSONEncoder
 from django.db.models.query import QuerySet
 
+from djblets.util.serializers import DjbletsJSONEncoder
 from djblets.webapi.core import WebAPIEncoder
 
 
-def encode_datetime(o):
-    """Encode datetime objects.
-
-    Like DjangoJSONEncoder's datetime encoding implementation, but filters out
-    milliseconds in addition to microseconds.
-    """
-    r = o.isoformat()
-    if o.microsecond:
-        r = r[:19] + r[26:]
-    if r.endswith('+00:00'):
-        r = r[:-6] + 'Z'
-    return r
-
-
 class BasicAPIEncoder(WebAPIEncoder):
     """
     A basic encoder that encodes dates, times, QuerySets, Users, and Groups.
@@ -45,11 +29,9 @@ class BasicAPIEncoder(WebAPIEncoder):
                 'id': o.id,
                 'name': o.name,
             }
-        elif isinstance(o, datetime.datetime):
-            return encode_datetime(o)
         else:
             try:
-                return DjangoJSONEncoder().default(o)
+                return DjbletsJSONEncoder().default(o)
             except TypeError:
                 return None
 
@@ -65,10 +47,8 @@ class ResourceAPIEncoder(WebAPIEncoder):
             return list(o)
         elif resource:
             return resource.serialize_object(o, *args, **kwargs)
-        elif isinstance(o, datetime.datetime):
-            return encode_datetime(o)
         else:
             try:
-                return DjangoJSONEncoder().default(o)
+                return DjbletsJSONEncoder().default(o)
             except TypeError:
                 return None
