From f527af0d052edb48b841e593935d6ccaa87a168b Mon Sep 17 00:00:00 2001
From: Art Lukyanchyk <artiom.lukyanchyk@hs-hannover.de>
Date: Thu, 12 Jul 2018 18:44:54 +0200
Subject: [PATCH] Add working timezone support for datetime serialization

---
 pikatasks/utils.py | 38 ++++++++++++++++++++++++++------------
 1 file changed, 26 insertions(+), 12 deletions(-)

diff --git a/pikatasks/utils.py b/pikatasks/utils.py
index 7f978da..9f59caa 100644
--- a/pikatasks/utils.py
+++ b/pikatasks/utils.py
@@ -2,34 +2,48 @@ import json
 import pika
 import logging
 import ssl
-from datetime import datetime
+from datetime import datetime, timezone
 from . import settings
 
 
 logger = logging.getLogger("pika-tasks")
 
-
 all_tasks = set()  # each registered task will show up here
 
-
-DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"  # need it to de/serialize datetime stored in JSON
+DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f%Z%z"  # used for serialization, I leave here both %Z%z for compatibility in case we ever want to improve timezone-related stuff
 
 
 def serialize(stuff):
     return json.dumps(stuff).encode("utf-8")
 
 
-def deserialize(binary):
-    return json.loads(binary.decode("utf-8"))
+def deserialize(bytes):
+    return json.loads(bytes.decode("utf-8"))
 
 
 def serialize_datetime(dt):
-    return datetime.strftime(dt, DATETIME_FORMAT)
-
-
-def deserialize_datetime(text):
-    """ Throws ValueError when fails to parse the text """
-    return datetime.strptime(text, DATETIME_FORMAT)
+    """
+    :param dt: datetime (timezone-aware)
+    :return: str that can be deserialized by deserialize_datetime()
+    """
+    if not dt.tzinfo:
+        logger.warning("Naive datetime received by serialize_datetime() and will be treated as local: {dt}. Avoid using naive datetime objects.".format(dt=dt))
+    utc_dt = dt.astimezone(timezone.utc)
+    return datetime.strftime(utc_dt, DATETIME_FORMAT)
+
+
+def deserialize_datetime(text, utc=False):
+    """
+    :param text: str created by serialize_datetime()
+    :param utc: set to True if you want this function to return UTC datetime
+    :return: datetime (timezone-aware)
+    """
+    dt = datetime.strptime(text, DATETIME_FORMAT)
+    assert dt.tzinfo, "ok, now that's weird, no tzinfo, but there must have been %z in the DATETIME_FORMAT"
+    if utc:
+        return dt.astimezone(timezone.utc)
+    else:
+        return dt.astimezone()  # not pytz, just old c++ stuff, but still a timezone with correct utc offset
 
 
 def get_ssl_options(settings):
-- 
GitLab