Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update FiniteDatetimeRange.days to require passing a TZ #195

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

Peter554
Copy link
Contributor

It's possible to create mixed TZ FiniteDatetimeRange's. It probably shouldn't be, but it currently is and this is unlikely to change too soon. See #192

This makes the FiniteDatetimeRange.days property ambiguous. For example, should the below range be 31 or 30 days?

RANGE = ranges.FiniteDatetimeRange(
    # This is also 2024-03-01T00:00:00 in TZ_UTC
    datetime.datetime(2024, 3, 1, tzinfo=TZ_LONDON),

    # This is 2024-04-01T00:00:00 in TZ_LONDON
    datetime.datetime(2024, 3, 31, hour=23, tzinfo=TZ_UTC),
)

Getting this wrong can lead to bugs in e.g. computation of per-day standing costs.

This PR updates FiniteDatetimeRange.days to require passing a TZ, to remove this ambiguity. This is a bit more verbose, but it's much more explicit and less error prone.

It's possible to create mixed TZ FiniteDatetimeRange's.
It probably shouldn't be, but it currently is and this is
unlikely to change too soon.
See #192

This makes the FiniteDatetimeRange.days poorly defined.
For example, should the below range be 31 or 30 days?

RANGE = ranges.FiniteDatetimeRange(
    # This is also 2024-03-01T00:00:00 in TZ_UTC
    datetime.datetime(2024, 3, 1, tzinfo=TZ_LONDON),
    # This is 2024-04-01T00:00:00 in TZ_LONDON
    datetime.datetime(2024, 3, 31, hour=23, tzinfo=TZ_UTC),
)

This PR updates FiniteDatetimeRange.days to require passing
a TZ, to remove this ambiguity.
In #187 the implementation of
FiniteDatetimeRange.intersection was optimised. This introduced a very
subtle change in behaviour. Previously when left.end was equal to
right.end then the right.end was favoured. In
#187 the implementation was
(unintentionally) changed to favour left.end.

This change reverses that, to again favour right.end.

This really shouldn't matter, but it can be signidicant when
the TZ of left and right are different. Eventually we'd like
to prevent this kind of mixed TZ behaviour (see
#192), but in the short
term it seems reasonable to make this tiny change to make things
consistent again with how they were before.
I was surprised to find that we don't already have this function.
@Peter554 Peter554 force-pushed the dt-range-days-require-tz branch from ad83af4 to 0d88cbd Compare January 30, 2025 11:31
@Peter554 Peter554 self-assigned this Jan 30, 2025
"""
Return the number of days between the start and end of the range.
"""
return (self.end - self.start).days
range_ = self.localize(tz)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ How about disallowing differing zones, just for this property?

Requiring tz means we're going to need to update all call sites anyway, so it's easy enough to do period.localize(tz).days instead of period.days(tz). That keeps this property simple and makes the caller a little more explicit with its intent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants