opening-hours-rs icon indicating copy to clipboard operation
opening-hours-rs copied to clipboard

Timezone ignored in `intervals()`

Open LTMullineux opened this issue 3 years ago • 2 comments

When passing timezone-aware start and/or end to the .intervals() function the returned datetime objects are not timezone aware:

>> from opening_hours import OpeningHours
>> from datetime import datetime, timedelta
>> import pytz
>> oh = OpeningHours('Mo-Fr 09:00-17:00')
>> start = datetime(2022, 9, 5, 0, 0, 0, 0, pytz.UTC)
>> end = start + timedelta(days=2)
>> next(oh.intervals(start=start, end=end))
(datetime.datetime(2022, 9, 5, 0, 0), datetime.datetime(2022, 9, 5, 9, 0), 'closed', [])

So in the above if you were to do something like the below you would get a Python TypeError when doing inequalities, or a False when comparing equality:

>> rust_start, rust_end, state, _ = next(oh.intervals(start=start, end=end))
>> rust_start == start
False
>> rust_start >= start
TypeError: can't compare offset-naive and offset-aware datetimes

My Rust isn't good enough to help with the debugging in the code, but it looks like there is not a way to add the timezone cleanly to NaiveDateTime?

If not it will be possible to do the timezone adjustments on the returned dates in Python, but it would be cool if this was handled straight out of the box.

LTMullineux avatar Sep 09 '22 16:09 LTMullineux

Hi! Thanks for your feedback.

I don't think it would be a good idea to add a timezone by default: it will be error prone because most of the time these opening hours fields come from OSM or equivalent and depend on the locality of the point of interest, not of the locality of the user.

However I agree that something opt-in may be relevant, mostly because timezone management is always a pain and anything that can make it easier will probably make it welcome (maybe just allow to pass a timezone to the parser so that it outputs times in given timezone).

remi-dupre avatar Oct 18 '22 14:10 remi-dupre

Hi, thanks for getting back.

I agree with your idea of having the option to pass in a timezone and have whatever datetime objects that are yielded be in that timezone

LTMullineux avatar Oct 19 '22 10:10 LTMullineux

Hello!

I've just added this while relying on PyO3's conversion layer for timezones, which currently has a few limitations :

  • Dates returned from the library will have a fixed offset instead of the input timezone. This will be fixed soon with new PyO3 release : https://github.com/PyO3/pyo3/issues/3266.
  • It seems to only support zoneinfo, and not pytz. I think it might remain this way as pytz is completely supersetted by the standard library now?

Anyway, your example, very slightly modified now works :

>> from opening_hours import OpeningHours
>> from datetime import datetime, timedelta
>> from zoneinfo import ZoneInfo
>> oh = OpeningHours('Mo-Fr 09:00-17:00')
>> start = datetime(2022, 9, 5, 0, 0, 0, 0, ZoneInfo("UTC"))
>> end = start + timedelta(days=2)
>> next(oh.intervals(start, end))
(datetime.datetime(2022, 9, 5, 0, 0, tzinfo=datetime.timezone.utc), datetime.datetime(2022, 9, 5, 9, 0, tzinfo=datetime.timezone.utc), State.CLOSED, [])

I've played a bit around it and this comes with a few more features :

  • You can now specify a local timezone with the expression, any input time will be converted to that timezone (eg. for keeping the user's time consistent with the actual place's location)
  • If you specify a local timezone, sun events will be precisely computed (sunset, ...)
  • Timezone can be inferred from coordinates automatically, together with a public holidays database.

remi-dupre avatar Dec 29 '24 15:12 remi-dupre