diff --git a/plugins/Geography/plugin.py b/plugins/Geography/plugin.py index 44404705b..2578fe18f 100644 --- a/plugins/Geography/plugin.py +++ b/plugins/Geography/plugin.py @@ -110,6 +110,21 @@ class Geography(callbacks.Plugin): _("Could not find the timezone of this location."), Raise=True ) + def _format_utc_offset(self, offset_seconds): + sign = "+" if offset_seconds >= 0 else "-" + + # Make modulos work as expected + offset_seconds = abs(offset_seconds) + + (offset_minutes, offset_seconds) = divmod(offset_seconds, 60) + (offset_hours, offset_minutes) = divmod(offset_minutes, 60) + offset = f"{offset_hours}:{offset_minutes:02}:{offset_seconds:02}" + + # hide seconds and minutes if they are zero + offset = re.sub("(:00)+$", "", offset) + + return f"UTC{sign}{offset}" + @wrap(["text"]) def timezone(self, irc, msg, args, query): """ @@ -134,33 +149,28 @@ class Geography(callbacks.Plugin): if timezone is None: continue - offset = str(datetime.datetime.now(tz=timezone).utcoffset()) - if not offset.startswith("-"): - offset = "+" + offset - - # hide seconds and minutes if they are zero - offset = re.sub("(:00)+$", "", offset) + offset_seconds = int( + datetime.datetime.now(tz=timezone).utcoffset().total_seconds()) + offset = self._format_utc_offset(offset_seconds) # Extract a human-friendly name, depending on the type of # the timezone object: if hasattr(timezone, "key"): # instance of zoneinfo.ZoneInfo - irc.reply(format("%s (currently UTC%s)", timezone.key, offset)) + irc.reply(format("%s (currently %s)", timezone.key, offset)) return elif hasattr(timezone, "zone"): # instance of pytz.timezone - irc.reply(format("%s (currently UTC%s)", timezone.zone, offset)) + irc.reply(format("%s (currently %s)", timezone.zone, offset)) return else: # probably datetime.timezone built from a constant offset try: - offset = timezone.utcoffset(now).seconds + offset = int(timezone.utcoffset(now).total_seconds()) except NotImplementedError: continue - hours = int(offset / 3600) - minutes = int(offset / 60 % 60) - irc.reply("UTC+%0.2i:%0.2i" % (hours, minutes)) + irc.reply(self._format_utc_offset(offset)) return irc.error( diff --git a/plugins/Geography/test.py b/plugins/Geography/test.py index 89399e1b3..2685bb74e 100644 --- a/plugins/Geography/test.py +++ b/plugins/Geography/test.py @@ -68,41 +68,85 @@ class GeographyTimezoneTestCase(PluginTestCase): @mock def testTimezonePytz(self): tz = pytz.timezone("Europe/Paris") - with patch.object(wikidata, "timezone_from_uri", return_value=tz): self.assertRegexp( "timezone Foo Bar", r"Europe/Paris \(currently UTC\+[12]\)" ) + tz = pytz.timezone("America/New_York") + with patch.object(wikidata, "timezone_from_uri", return_value=tz): + self.assertRegexp( + "timezone New York", r"America/New_York \(currently UTC-[45]\)" + ) + + tz = pytz.timezone("Canada/Newfoundland") + with patch.object(wikidata, "timezone_from_uri", return_value=tz): + self.assertRegexp( + "timezone Newfoundland", + r"Canada/Newfoundland \(currently UTC-[23]:30\)" + ) + + tz = pytz.timezone("Asia/Kolkata") + with patch.object(wikidata, "timezone_from_uri", return_value=tz): + self.assertRegexp( + "timezone Delhi", r"Asia/Kolkata \(currently UTC\+5:30\)" + ) + @skipIf(not zoneinfo, "Python is older than 3.9") @mock def testTimezoneZoneinfo(self): tz = zoneinfo.ZoneInfo("Europe/Paris") - with patch.object(wikidata, "timezone_from_uri", return_value=tz): self.assertRegexp( "timezone Foo Bar", r"Europe/Paris \(currently UTC\+[12]\)" ) + tz = zoneinfo.ZoneInfo("America/New_York") + with patch.object(wikidata, "timezone_from_uri", return_value=tz): + self.assertRegexp( + "timezone New York", r"America/New_York \(currently UTC-[45]\)" + ) + + tz = zoneinfo.ZoneInfo("Canada/Newfoundland") + with patch.object(wikidata, "timezone_from_uri", return_value=tz): + self.assertRegexp( + "timezone Newfoundland", + r"Canada/Newfoundland \(currently UTC-[23]:30\)" + ) + + tz = zoneinfo.ZoneInfo("Asia/Kolkata") + with patch.object(wikidata, "timezone_from_uri", return_value=tz): + self.assertRegexp( + "timezone Delhi", r"Asia/Kolkata \(currently UTC\+5:30\)" + ) + @skipIf(not zoneinfo, "Python is older than 3.9") @mock def testTimezoneAbsolute(self): tz = datetime.timezone(datetime.timedelta(hours=4)) - with patch.object(wikidata, "timezone_from_uri", return_value=tz): - self.assertResponse("timezone Foo Bar", "UTC+04:00") + self.assertResponse("timezone Foo Bar", "UTC+4") tz = datetime.timezone(datetime.timedelta(hours=4, minutes=30)) - with patch.object(wikidata, "timezone_from_uri", return_value=tz): - self.assertResponse("timezone Foo Bar", "UTC+04:30") + self.assertResponse("timezone Foo Bar", "UTC+4:30") + + tz = datetime.timezone(datetime.timedelta(hours=-4, minutes=30)) + with patch.object(wikidata, "timezone_from_uri", return_value=tz): + self.assertResponse("timezone Foo Bar", "UTC-3:30") @skipIf(not network, "Network test") def testTimezoneIntegration(self): self.assertRegexp( "timezone Metz, France", r"Europe/Paris \(currently UTC\+[12]\)" ) - self.assertResponse("timezone Saint-Denis, La RĂ©union", "UTC+04:00") + self.assertResponse("timezone Saint-Denis, La RĂ©union", "UTC+4") + self.assertRegexp( + "timezone Delhi", r"Asia/Kolkata \(currently UTC\+5:30\)" + ) + self.assertRegexp( + "timezone Newfoundland", r"UTC-[23]:30" + ) class GeographyLocaltimeTestCase(PluginTestCase):