From 9f38fd179eacf9631f4a811f4d3f09b7c883563c Mon Sep 17 00:00:00 2001 From: Hemna Date: Tue, 19 Jan 2021 11:19:53 -0500 Subject: [PATCH] Fixed TimePlugin timezone issue The existing time plugin had a hard coded PDT for pacific timezone, when it wasn't. This patch adds some real timezone conversion from utc to the tz of the running aprsd server. This will eventually allow us to use either the tz of the running aprsd and/or the tz of the calling callsign if we can just get the tz string from the location beacon of the caller's callsign. --- aprsd/plugins/time.py | 33 ++++++++++++++++++++++++++------- dev-requirements.txt | 2 +- requirements.in | 1 + requirements.txt | 8 +++++--- tests/test_plugin.py | 27 ++++++++++++++++++--------- 5 files changed, 51 insertions(+), 20 deletions(-) diff --git a/aprsd/plugins/time.py b/aprsd/plugins/time.py index 3c0c3b9..4510ec3 100644 --- a/aprsd/plugins/time.py +++ b/aprsd/plugins/time.py @@ -2,6 +2,7 @@ import logging import time from aprsd import fuzzyclock, plugin +import pytz LOG = logging.getLogger("APRSD") @@ -13,16 +14,34 @@ class TimePlugin(plugin.APRSDPluginBase): command_regex = "^[tT]" command_name = "time" + def _get_local_tz(self): + return pytz.timezone(time.strftime("%Z")) + + def _get_utcnow(self): + return pytz.datetime.datetime.utcnow() + def command(self, fromcall, message, ack): LOG.info("TIME COMMAND") - stm = time.localtime() - h = stm.tm_hour - m = stm.tm_min - cur_time = fuzzyclock.fuzzy(h, m, 1) - reply = "{} ({}:{} PDT) ({})".format( + # So we can mock this in unit tests + localzone = self._get_local_tz() + + # This is inefficient for now, but this enables + # us to add the ability to provide time in the TZ + # of the caller, if we can get the TZ from their callsign location + # This also accounts for running aprsd in different timezones + utcnow = self._get_utcnow() + gmt_t = pytz.utc.localize(utcnow) + local_t = gmt_t.astimezone(localzone) + + local_short_str = local_t.strftime("%H:%M %Z") + local_hour = local_t.strftime("%H") + local_min = local_t.strftime("%M") + cur_time = fuzzyclock.fuzzy(int(local_hour), int(local_min), 1) + + reply = "{} ({}) ({})".format( cur_time, - str(h), - str(m).rjust(2, "0"), + local_short_str, message.rstrip(), ) + return reply diff --git a/dev-requirements.txt b/dev-requirements.txt index 57100ae..2d5082c 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -160,7 +160,7 @@ typing-extensions==3.7.4.3 # mypy urllib3==1.26.2 # via requests -virtualenv==20.2.2 +virtualenv==20.4.0 # via tox webencodings==0.5.1 # via bleach diff --git a/requirements.in b/requirements.in index e81f136..c023737 100644 --- a/requirements.in +++ b/requirements.in @@ -10,3 +10,4 @@ thesmuggler aprslib py3-validate-email pre-commit +pytz diff --git a/requirements.txt b/requirements.txt index ed86b36..5eba174 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,13 +28,13 @@ filelock==3.0.12 # via # py3-validate-email # virtualenv -identify==1.5.11 +identify==1.5.13 # via pre-commit idna==2.10 # via # py3-validate-email # requests -imapclient==2.1.0 +imapclient==2.2.0 # via -r requirements.in jinja2==2.11.2 # via click-completion @@ -50,6 +50,8 @@ pre-commit==2.9.3 # via -r requirements.in py3-validate-email==0.2.12 # via -r requirements.in +pytz==2020.5 + # via -r requirements.in pyyaml==5.3.1 # via # -r requirements.in @@ -70,5 +72,5 @@ toml==0.10.2 # via pre-commit urllib3==1.26.2 # via requests -virtualenv==20.2.2 +virtualenv==20.4.0 # via pre-commit diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 83d9662..fecd16f 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -9,6 +9,7 @@ from aprsd.plugins import ping as ping_plugin from aprsd.plugins import query as query_plugin from aprsd.plugins import time as time_plugin from aprsd.plugins import version as version_plugin +import pytz class TestPlugin(unittest.TestCase): @@ -68,13 +69,21 @@ class TestPlugin(unittest.TestCase): actual = query.run(self.fromcall, message, self.ack) mock_restart.assert_called_once() - @mock.patch("time.localtime") - def test_time(self, mock_time): + @mock.patch("aprsd.plugins.time.TimePlugin._get_local_tz") + @mock.patch("aprsd.plugins.time.TimePlugin._get_utcnow") + def test_time(self, mock_utcnow, mock_localtz): + utcnow = pytz.datetime.datetime.utcnow() + mock_utcnow.return_value = utcnow + tz = pytz.timezone("US/Pacific") + mock_localtz.return_value = tz + + gmt_t = pytz.utc.localize(utcnow) + local_t = gmt_t.astimezone(tz) + fake_time = mock.MagicMock() - h = fake_time.tm_hour = 16 - m = fake_time.tm_min = 12 - fake_time.tm_sec = 55 - mock_time.return_value = fake_time + h = int(local_t.strftime("%H")) + m = int(local_t.strftime("%M")) + fake_time.tm_sec = 13 time = time_plugin.TimePlugin(self.config) fromcall = "KFART" @@ -87,10 +96,10 @@ class TestPlugin(unittest.TestCase): cur_time = fuzzy(h, m, 1) message = "time" - expected = "{} ({}:{} PDT) ({})".format( + local_short_str = local_t.strftime("%H:%M %Z") + expected = "{} ({}) ({})".format( cur_time, - str(h), - str(m).rjust(2, "0"), + local_short_str, message.rstrip(), ) actual = time.run(fromcall, message, ack)