From 1e0c78ff8ea07631b679aeb6316cb7b4c9f4061f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Gro=C3=9F?= Date: Wed, 27 Sep 2023 22:05:30 +0200 Subject: [PATCH] cli: Display datetimes in local timezone Previously the start and end datetimes were always printed out in the timezone that they appear in the calendar entry. In 7a8d584b duration support was added and an attempt was already made to display the datetime in the local timezone. Unfortunately in that specific case the `start.astimezone(start.tzinfo)` is a no-op and does absolutely nothing. Unlike the name suggests, `astimezone()` adjusts the date and time data, such that they match the passed tzinfo, but since that is the same timezone data as before, nothing changes. [0] In order to properly convert to the user's local timezone, we need to call the method with no arguments. With this the timezone is always properly displayed, which makes up for a much nicer UX for users of the cli. The test has to be adapted to expect the datetime in the local timezone, hence we cannot hardcode the entire expected string anymore. [0] https://docs.python.org/3/library/datetime.html#datetime.datetime.astimezone --- CHANGES.rst | 4 +++- src/icalendar/cli.py | 4 ++-- src/icalendar/tests/test_cli_tool.py | 30 ++++++++++++++++++++-------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 4df8d725..7d0b6da4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,7 +6,9 @@ Changelog Minor changes: -- ... +- The cli utility now displays start and end datetimes in the user's local timezone. + Ref: #561 + [vimpostor] Breaking changes: diff --git a/src/icalendar/cli.py b/src/icalendar/cli.py index 80ea5619..ac53963c 100755 --- a/src/icalendar/cli.py +++ b/src/icalendar/cli.py @@ -52,10 +52,10 @@ def view(event): end = event.decoded('dtend', default=start) duration = event.decoded('duration', default=end - start) if isinstance(start, datetime): - start = start.astimezone(start.tzinfo) + start = start.astimezone() start = start.strftime('%c') if isinstance(end, datetime): - end = end.astimezone(end.tzinfo) + end = end.astimezone() end = end.strftime('%c') return f""" Organizer: {organizer} diff --git a/src/icalendar/tests/test_cli_tool.py b/src/icalendar/tests/test_cli_tool.py index 20ba8709..0ae9b5ab 100644 --- a/src/icalendar/tests/test_cli_tool.py +++ b/src/icalendar/tests/test_cli_tool.py @@ -1,6 +1,11 @@ import unittest +from datetime import tzinfo, datetime from icalendar import Calendar, cli +try: + import zoneinfo +except ModuleNotFoundError: + from backports import zoneinfo INPUT = ''' BEGIN:VCALENDAR @@ -21,7 +26,7 @@ ORGANIZER:organizer@test.test ATTENDEE:attendee1@example.com ATTENDEE:attendee2@test.test -SUMMARY:Test summury +SUMMARY:Test summary DTSTART;TZID=Europe/Warsaw:20220820T200000 DTEND;TZID=Europe/Warsaw:20220820T203000 LOCATION:New Amsterdam, 1010 Test Street @@ -36,13 +41,22 @@ END:VCALENDAR ''' -PROPER_OUTPUT = ''' Organizer: organizer +def local_datetime(dt): + return datetime.strptime(dt, "%Y%m%dT%H%M%S").replace(tzinfo=zoneinfo.ZoneInfo("Europe/Warsaw")).astimezone().strftime('%c') + +# datetimes are displayed in the local timezone, so we cannot just hardcode them +firststart = local_datetime('20220820T103400') +firstend = local_datetime('20220820T113400') +secondstart = local_datetime('20220820T200000') +secondend = local_datetime('20220820T203000') + +PROPER_OUTPUT = f""" Organizer: organizer Attendees: attendee1 attendee2 Summary : Test Summary - Starts : Sat Aug 20 10:34:00 2022 - End : Sat Aug 20 11:34:00 2022 + Starts : {firststart} + End : {firstend} Duration : 1:00:00 Location : New Amsterdam, 1000 Sunrise Test Street Comment : Comment @@ -53,9 +67,9 @@ Attendees: attendee1 attendee2 - Summary : Test summury - Starts : Sat Aug 20 20:00:00 2022 - End : Sat Aug 20 20:30:00 2022 + Summary : Test summary + Starts : {secondstart} + End : {secondend} Duration : 0:30:00 Location : New Amsterdam, 1010 Test Street Comment : @@ -75,7 +89,7 @@ Description: -''' +""" class CLIToolTest(unittest.TestCase): def test_output_is_proper(self):