From 3d00bdf9bb107c2fb777b9e95fece065fbbea212 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Mon, 3 Feb 2020 15:33:49 -0500 Subject: [PATCH 1/6] fix(fcm): Convert event_time to UTC - Check if the datetime object is naive or timezone aware - If a naive datetime object is provided then set the timezone to local timezone - Convert the event_time to UTC Zulu format --- firebase_admin/_messaging_encoder.py | 8 +++++++- tests/test_messaging.py | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/firebase_admin/_messaging_encoder.py b/firebase_admin/_messaging_encoder.py index c4da53f0d..ad10f2159 100644 --- a/firebase_admin/_messaging_encoder.py +++ b/firebase_admin/_messaging_encoder.py @@ -19,6 +19,7 @@ import math import numbers import re +import pytz import firebase_admin._messaging_utils as _messaging_utils @@ -324,7 +325,12 @@ def encode_android_notification(cls, notification): event_time = result.get('event_time') if event_time: - result['event_time'] = str(event_time.isoformat()) + 'Z' + if event_time.tzinfo is None: + local_tzinfo = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo + timezone = pytz.timezone(str(local_tzinfo)) + event_time = timezone.localize(event_time) + utc_event_time = event_time.astimezone(pytz.utc) + result['event_time'] = utc_event_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ') priority = result.get('notification_priority') if priority: diff --git a/tests/test_messaging.py b/tests/test_messaging.py index f8be4cd67..3af90eec5 100644 --- a/tests/test_messaging.py +++ b/tests/test_messaging.py @@ -547,7 +547,10 @@ def test_android_notification(self): click_action='ca', title_loc_key='tlk', body_loc_key='blk', title_loc_args=['t1', 't2'], body_loc_args=['b1', 'b2'], channel_id='c', ticker='ticker', sticky=True, - event_timestamp=datetime.datetime(2019, 10, 20, 15, 12, 23, 123), + event_timestamp=datetime.datetime( + 2019, 10, 20, 15, 12, 23, 123, + tzinfo=datetime.timezone(datetime.timedelta(hours=-5)) + ), local_only=False, priority='high', vibrate_timings_millis=[100, 50, 250], default_vibrate_timings=False, default_sound=True, @@ -577,7 +580,7 @@ def test_android_notification(self): 'channel_id': 'c', 'ticker': 'ticker', 'sticky': True, - 'event_time': '2019-10-20T15:12:23.000123Z', + 'event_time': '2019-10-20T20:12:23.000123Z', 'local_only': False, 'notification_priority': 'PRIORITY_HIGH', 'vibrate_timings': ['0.100000000s', '0.050000000s', '0.250000000s'], From cb9b392208ee2acdf22fc2527725457e0137416a Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Tue, 4 Feb 2020 12:06:39 -0500 Subject: [PATCH 2/6] Remove the third party library --- firebase_admin/_messaging_encoder.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/firebase_admin/_messaging_encoder.py b/firebase_admin/_messaging_encoder.py index ad10f2159..6b6036d68 100644 --- a/firebase_admin/_messaging_encoder.py +++ b/firebase_admin/_messaging_encoder.py @@ -19,7 +19,6 @@ import math import numbers import re -import pytz import firebase_admin._messaging_utils as _messaging_utils @@ -325,11 +324,7 @@ def encode_android_notification(cls, notification): event_time = result.get('event_time') if event_time: - if event_time.tzinfo is None: - local_tzinfo = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo - timezone = pytz.timezone(str(local_tzinfo)) - event_time = timezone.localize(event_time) - utc_event_time = event_time.astimezone(pytz.utc) + utc_event_time = event_time.astimezone(datetime.timezone.utc) result['event_time'] = utc_event_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ') priority = result.get('notification_priority') From a528c439b8b24672d9823ec514b32b6ff450e182 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Tue, 4 Feb 2020 16:26:28 -0500 Subject: [PATCH 3/6] Add new test case for naive event_timestamp --- tests/test_messaging.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_messaging.py b/tests/test_messaging.py index 3af90eec5..406223595 100644 --- a/tests/test_messaging.py +++ b/tests/test_messaging.py @@ -604,6 +604,29 @@ def test_android_notification(self): } check_encoding(msg, expected) + def test_android_notification_naive_event_timestamp(self): + event_time = datetime.datetime.now() + utc_event_time = event_time.astimezone(datetime.timezone.utc) + msg = messaging.Message( + topic='topic', + android=messaging.AndroidConfig( + notification=messaging.AndroidNotification( + title='t', + event_timestamp=event_time, + ) + ) + ) + expected = { + 'topic': 'topic', + 'android': { + 'notification': { + 'title': 't', + 'event_time': utc_event_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + }, + }, + } + check_encoding(msg, expected) + class TestLightSettingsEncoder: From 2d9c7b764258721fbe33985ccf6bfeb84323fdfe Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 5 Feb 2020 13:15:01 -0500 Subject: [PATCH 4/6] Consider naive datetimes are in UTC --- firebase_admin/_messaging_encoder.py | 5 +++-- firebase_admin/_messaging_utils.py | 3 ++- tests/test_messaging.py | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/firebase_admin/_messaging_encoder.py b/firebase_admin/_messaging_encoder.py index 6b6036d68..26a6a030e 100644 --- a/firebase_admin/_messaging_encoder.py +++ b/firebase_admin/_messaging_encoder.py @@ -324,8 +324,9 @@ def encode_android_notification(cls, notification): event_time = result.get('event_time') if event_time: - utc_event_time = event_time.astimezone(datetime.timezone.utc) - result['event_time'] = utc_event_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + if event_time.tzinfo is not None: + event_time = event_time.astimezone(datetime.timezone.utc) + result['event_time'] = event_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ') priority = result.get('notification_priority') if priority: diff --git a/firebase_admin/_messaging_utils.py b/firebase_admin/_messaging_utils.py index 3a1943c04..a93c5f33e 100644 --- a/firebase_admin/_messaging_utils.py +++ b/firebase_admin/_messaging_utils.py @@ -98,7 +98,8 @@ class AndroidNotification: the user clicks it (optional). event_timestamp: For notifications that inform users about events with an absolute time reference, sets the time that the event in the notification occurred as a - ``datetime.datetime`` instance. Notifications in the panel are sorted by this time + ``datetime.datetime`` instance. If the ``datetime.datetime`` instance is naive it will + be assumed to be in the UTC timezone. Notifications in the panel are sorted by this time (optional). local_only: Sets whether or not this notification is relevant only to the current device. Some notifications can be bridged to other devices for remote display, such as a Wear OS diff --git a/tests/test_messaging.py b/tests/test_messaging.py index 406223595..f2ef47cf8 100644 --- a/tests/test_messaging.py +++ b/tests/test_messaging.py @@ -606,7 +606,6 @@ def test_android_notification(self): def test_android_notification_naive_event_timestamp(self): event_time = datetime.datetime.now() - utc_event_time = event_time.astimezone(datetime.timezone.utc) msg = messaging.Message( topic='topic', android=messaging.AndroidConfig( @@ -621,7 +620,7 @@ def test_android_notification_naive_event_timestamp(self): 'android': { 'notification': { 'title': 't', - 'event_time': utc_event_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + 'event_time': event_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ') }, }, } From e073b80b394ac590b1419c46b3b0fd102d5212d0 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 5 Feb 2020 14:33:12 -0500 Subject: [PATCH 5/6] Add a comment explaning the logic --- firebase_admin/_messaging_encoder.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firebase_admin/_messaging_encoder.py b/firebase_admin/_messaging_encoder.py index 26a6a030e..48a3dd3cd 100644 --- a/firebase_admin/_messaging_encoder.py +++ b/firebase_admin/_messaging_encoder.py @@ -324,6 +324,8 @@ def encode_android_notification(cls, notification): event_time = result.get('event_time') if event_time: + # if the datetime instance is not naive (tzinfo is present), convert to UTC + # otherwise (tzinfo is None) assume the datetime instance is already in UTC if event_time.tzinfo is not None: event_time = event_time.astimezone(datetime.timezone.utc) result['event_time'] = event_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ') From 99bbc058079ed4eb7da083b0dc57db9e73159373 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 12 Feb 2020 11:29:16 -0500 Subject: [PATCH 6/6] Update docs --- firebase_admin/_messaging_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase_admin/_messaging_utils.py b/firebase_admin/_messaging_utils.py index a93c5f33e..d25ba5520 100644 --- a/firebase_admin/_messaging_utils.py +++ b/firebase_admin/_messaging_utils.py @@ -98,8 +98,8 @@ class AndroidNotification: the user clicks it (optional). event_timestamp: For notifications that inform users about events with an absolute time reference, sets the time that the event in the notification occurred as a - ``datetime.datetime`` instance. If the ``datetime.datetime`` instance is naive it will - be assumed to be in the UTC timezone. Notifications in the panel are sorted by this time + ``datetime.datetime`` instance. If the ``datetime.datetime`` instance is naive, it + defaults to be in the UTC timezone. Notifications in the panel are sorted by this time (optional). local_only: Sets whether or not this notification is relevant only to the current device. Some notifications can be bridged to other devices for remote display, such as a Wear OS