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 4df8d72..7d0b6da 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 80ea561..ac53963 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 20ba870..0ae9b5a 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 @@ BEGIN:VEVENT 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:VEVENT 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 @@ PROPER_OUTPUT = ''' Organizer: organizer 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 @@ PROPER_OUTPUT = ''' Organizer: organizer Description: -''' +""" class CLIToolTest(unittest.TestCase): def test_output_is_proper(self):