niccokunzmann/python-recurring-ical-events

Support for non-pytz timezones

Closed this issue · 6 comments

I'm currently trying to write a program involving calendars, and the mix of dependencies I've got seem to fall into one or both of two categories: support for pytz timezones, and support for PEP-495 timezones (e.g. zoneinfo and dateutil).

From reading the discussion in pytz_deprecation_shim's documentation and toying with my existing dependencies, the latter seems to be the modern way to go, and works with the exception of this library -- convert_to_datetime() makes the assumption that a datetime instance's tzinfo field is a pytz timezone, by use of its localize() method. As I understand it, this call is not needed for PEP-495 timezone objects as it's specifically for transition folds (which the datetime class in Python 3.6+ handles natively, and dateutil backports with a subclass).

To reproduce

from datetime import date, datetime, timedelta
from zoneinfo import ZoneInfo

from icalendar import Calendar, Event, vDDDTypes
import recurring_ical_events

tz = ZoneInfo("Europe/London")

cal = Calendar()
event = Event()
cal.add_component(event)

dt = datetime.now().astimezone().astimezone(tz)
# datetime.datetime(2021, 6, 24, 21, 15, tzinfo=zoneinfo.ZoneInfo(key='Europe/London'))
d = dt.date()

event["dtstart"] = vDDDTypes(dt)

recurring_ical_events.of(cal).between(d, d + timedelta(1))
Traceback (most recent call last):
  File "<stdin>", line 19, in <module>
    recurring_ical_events.of(cal).between(d, d + timedelta(1))
  File "/usr/lib/python3.9/site-packages/recurring_ical_events.py", line 370, in between
    for repetition in event_repetions.within_days(span_start_day, span_stop_day):
  File "/usr/lib/python3.9/site-packages/recurring_ical_events.py", line 241, in within_days
    start = start.tzinfo.localize(start.replace(tzinfo=None))
AttributeError: 'zoneinfo.ZoneInfo' object has no attribute 'localize'

Suggested implementation

Currently I'm making use of pytz_deprecation_shim and passing the shim wrappers to recurring-ical-events, which seems to work but produces deprecation warnings on use of the shim's localize(). This could be solved better if the library itself was aware of PEP-495 timezones by default, and only called localize() when encountering a pytz timezone that provides it.

I'm happy to PR this, though I'm not sure the best way of detecting the type of timezone -- there are a few options:

  • we could hasattr(tz, "localize"), but that will be incorrect for pytz_deprecation_shim if that's being used elsewhere
  • we could isinstance(tz, pytz.BaseTzInfo), but that would require an ongoing dependency on pytz which wouldn't be needed otherwise; you'll also need a relatively recent pytz otherwise pytz.UTC isn't an instance of BaseTzInfo
  • we could just use pytz_deprecation_shim, such that any input timezones are wrapped by the shims, and then can consistently be used without localize()

Looking at the issue, it might be nice to test that the library can deal with

  • pytz time zones
  • python native time zones
  • zoneinfo.ZoneInfo time zones

Or at least there should be documentation about how to use these with the library.
Where is that zoneinfo time zone from?

zoneinfo is a module shipped with Python 3.9 and above, but available on PyPI as backports.zoneinfo for 3.6 through 3.8.

Whilst looking at the tests in the PR, I had initially considered updating the tests to use zoneinfo timezones instead of pytz ones, but indeed we probably want to clone those tests instead to ensure they work with either type.

I am a bit confused about the zoneinfo module... It would need more reading for me. I will see. I think, it is good to support this and to drop pytz. So, if you have ideas about how to appoach it, it is welcome. Maybe I will try to create a PR for the code you showed in this issue.

Apologies for the radio silence -- I got another quick look in, but the scope of the test failures was pretty wide and I wasn't really sure where to go from there. The zoneinfo calendar fixtures in your PR look like a good place to start.

I switched to Gitlab-CI for tests. When #78 is merged, support for zoneinfo should be there from Python 3.7 on.

@Terrance In Version 1.0.1b the handling if zoneinfo time zones is tested and implemented.