-
Notifications
You must be signed in to change notification settings - Fork 7
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
Care around datetime ranges with mixed tzifo / fix FiniteDatetimeRange.days
#192
Comments
🐮 Thoughts about
|
@benthorner thanks for taking a look
# This range has inconsistent tzinfo.
# It's unclear how many days this represents.
range_utc = ranges.FiniteDatetimeRange(
datetime.datetime(2024, 3, 1, tzinfo=TZ_LONDON),
datetime.datetime(2024, 3, 31, hour=23, tzinfo=TZ_UTC),
) It's only ambiguous in that the concept of "days" in general is ambiguous. According to its usage in Kraken this You can see an example of how this property is (misused?) in a billing calculation from this tiny snippet: measurement_period = certificates.period & period
...
scale = Decimal(measurement_period.days).quantize(three_dp) / certificates.period.days
# scale is then used to scale a cost.
# The problem occurs because one of `certificates.period` and `period` are in UTC
# and the other is in localtime. The resulting `measurement_period` has mixed tzinfo
# (following xocto v7.1.1), and the `.days` property doesn't behave as expected (we end up
# with the example range I gave above, and the code expects 31 days, but the result is actually 30 days). I agree that
|
I'm not sure that "days" is ambiguous. The python standard library uses "days" as an integer, and I don't think that has been a point of confusion. The confusion is.. date math in python does not consider TZ or fold attributes. It does a straight comparison between the date and time components only. IMO we should probably enforce ranges ONLY being in UTC - though that'd take a lot more thought to confirm than I've actually given, and our range database types try to localise into the local timezone by default (a decision I made that I massively regret, and we should never use in Kraken!). Enforcing a consistent timezone (even if not UTC) is the next best and probably safest compatibility wise. If enforcing a consistent timezone for our intersection/union/other operators means that the order of operations is no longer buggy, great. If not, then we should definitely fix that oddity as well. |
@jarshwah to me that's a secondary issue here. To me the problem is we are not clear about what we want:
Are we talking about the 👉 Sounds like we may be in agreement:
Does that sound OK to everyone? |
@benthorner It does in theory sound ideal to me, however I created a PR for this change (#194) and tried installing it one Kraken. The number of errors is rather daunting so I think we might need to find a smaller change to fix this issue in the short term. Context: Upgrading xocto to v7.1.1 in Kraken is currently blocked since it surfaced this issue. While ranges with mixed tzinfo are generally going to be confusing/error-prone, we're mainly talking about the
@jarshwah Sidetracking but this has me concerned since I've been encouraging people to use our custom range database types! If we regret that decision is it too late to change it? Can't we fix that? Is it worth opening a separate issue for this? |
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.
A possible quicker fix to unblock things -> #195 Update: Still quite a few errors trying to install this. Less, but still quite some. |
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.
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.
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.
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.
Seems to me that the root cause of the problem is that datetime ranges are being used rather than date ranges. Both have a clear, unambiguous definition of "days", it's muddying the two where the confusion creeps in.
Presumably what billing wants is actually the latter - in which case the conversion ought to be explicit. So instead of
That forces rounding etc to be dealt with explicitly (the existing method fails if either of the datetimes aren't midnight-aligned, so it would also need an option to allow this) Fractional number of days is probably not what people usually want, so Thoughts? |
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.
@djrobstep Thanks for the feedback - yes this is likely what we want. There was also some internal discussion in slack that reached a similar conclusion. I'll updated this issue. |
FiniteDatetimeRange.days
Problem summary
FiniteDatetimeRange
s with mixed tzinfo (where the start and end have different tzinfo).FiniteDatetimeRange.days
property confusing and error prone (Kraken: important for computation of daily standing charges). For example, is the below range 30 or 31 days?Problem in detail
See https://github.com/octoenergy/xocto/pull/191/files, which demonstrates:
FiniteDatetimeRange
with mixed tzinfo the.days
property is ambiguous.(r1 & r2).days
can be different from(r2 & r1).days
.(r1 | r2).days
can be different fromr1.days
even whenr2 == r1
.Proposed solution
FiniteDatetimeRange.days
property.dt_range.localize(tz=...).as_date_range().days
.The text was updated successfully, but these errors were encountered: