-
Notifications
You must be signed in to change notification settings - Fork 15
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
Semantics and need for local system datetime #30
Comments
One solution i'm gravitating towards: making
ExamplesProblem with the current situationos.environ['TZ'] = 'America/New_York'
# this time exists just fine in NYC
backup_scheduled = LocalDateTime(2024, 3, 31, hour=2, minute=30)
os.environ['TZ'] = 'Europe/Paris'
# but it gets skipped in Paris DST transition!
should_run_backup = UTCDateTime.now() > backup_scheduled The last line is unreliable, as the
What would the solved problem look likeos.environ['TZ'] = 'America/New_York'
backup_scheduled = LocalDateTime(2024, 3, 31, hour=2, minute=30)
os.environ['TZ'] = 'Europe/Paris'
# We are forced to call the `with_local_offset` method which offers various methods to deal with ambiguity/non-existence
should_run_backup = UTCDateTime.now() > backup_scheduled.with_local_offset(disambiguate=..., nonexistent=...) |
Side comment: Have you considered a different name for Possible alternatives:
|
I'm coming around to this as well. Ideally I'd like consistent
I'm less of a fan of |
LocalDateTime
LocalDateTime
Following up the discussion in #59, and after reading the notes in this ticket, I'm still confused about the intended behavior of My expectation is that the timezone of the OS gets bound to the instance of the So I don't follow how the problems mentioned above can happen: "a LocalDateTime can become non-existent after initialization", and "a LocalDateTime can suddenly require disambiguation after initialization". My expectation is that the instance of |
I've come around to this as well, and in the 0.4 release candidate this has been changed. See the 0.4-dev docs here. Have a look at the API documentation of the |
Sorry for being dense, but I'm still not understanding the intended behavior of On the one hand, the document says: "Instances have the fixed offset of the system timezone at the time of initialization." That says to me that it's the same as an On the other hand, the doc also says: "Unlike OffsetDateTime, it knows about the system timezone and its DST transitions." That says to me that it's a I'm confused why the |
Not at all.
Assuming we keep
Note there there is no system
The underlying data that Now, we could replace Another niggle is that you'd have to add a special constructor to |
LocalDateTime
I've been pondering why we have a disconnect on this topic, and I think it comes to this: I get the impression that you are restricting the From my understanding of Linux/Unix/POSIX machines, the system could be configured with an IANA timezone, but the system could also be configured with an arbitrary timezone rule such as the POSIX-style DST transition rules using the This is where I would say that the It seems to me that primary reason that |
Yes. My approach is: "Different classes for different semantics":
Mixing semantics in one class is one of the problems with the standard library I'm aiming to avoid. The problem with a Back on the topic of The use casesFor Python programs, it is totally reasonable to want to interact with local system time. Here are the use cases I imagine:
The alternativesA: An immutable Comparison by use case
# A
dt.as_local()
# B
dt.as_local_offset()
# A
LocalDateTime(2023, 10, 29, 2, 15, disambiguate="earlier")
# B
NaiveDateTime(2023, 10, 29, 2, 15).assume_local_offset(disambiguate="earlier")
# A
local_dt + hours(5)
# B
# note this isn't exactly the same for periods containing days, months, etc.
(local_dt.as_utc() + hours(5)).as_local_offset()
# A
LocalDateTime.from_timestamp(ts)
# B
UTCDateTime.from_timestamp(ts).as_local_offset() Other considerationswhy drop the class:
why keep the class:
|
My argument is that those 4 things are exactly the same, in the sense that they are all |
Let me present my thoughts from a different angle. I think you have correctly identified the usability problem in the Maybe I'm suggesting that the pendulum may have swung too far with the creation of numerous subclasses to model the different use cases of |
Absolutely agree. They are the same in that they represent a moment in POSIX timeline, and their "only" difference is how their UTC offset is defined. I'd argue though, that it's exactly this difference that profoundly affects the behavior of the class! Addition/subtraction, parsing, formatting, and disambiguation are all but defined by their handling of the offset. 😉 Taking a cheeky straw-man metaphor here: imagine if a geometry library had one class |
I think we're getting to the core here. Would you consider that different
Important not to confuse the two, I'll reflect on this. |
Hi, my bits: I think this is extremely simple. I think
Just my thoughts Keep it simple |
Also I don't see a use case for
I understand why you create a For example: utcdate = UTCDateTime(2022, 1, 1, 12) # it's utc
offsetdate = OffsetDateTime(2022, 1, 1, 12, offset=hours(0)) # it's utc as well
zoneddate = ZonedDateTime(2022, 1, 1, 12, tz='UTC') # it's utc as well
print(f'UTCDateTime is {utcdate}')
print(f'OffsetDateTime is {offsetdate}')
print(f'ZonedDateTime is {zoneddate}')
print(utcdate == offsetdate == zoneddate) This outputs:
So the difference here is:
Either way, let the user decide if he wants UTC in Zoned, Offset or Naive datetimes... Hope this adds up to the issue. |
Is this the case? Doesn't every OS have a system time? The python standard library (both
Depends what exactly you mean with dynamic. I've already amended it to no longer 'automatically' adjust to changes in the system timezone. The user is in charge when to update this. See the API docs for upcoming release, what do you think?
The reason I chose for a fixed-offset is that this is what Python's
True that explicitly providing the timezone can be useful. In calendar applications, for example, you can have the user explicitly set it. print(f"Your download is expected to be ready at {LocalDateTime.now() + hours(1)")
I can definitely relate to this feeling. What would you think of removing |
Well then LocalDateTime should disappear and just have something like the following. There’s no need for a class for this. from whenever import get_local_time
local_time = get_local_time()
print(type(local_time))
print(local_time) Output: OffsetDateTime
2022-01-01T12:00:00+00:00 |
Correct, but: the value of a class isn't just in what it supports, but also in what it doesn't. Example: imagine encountering this in a codebase: class ChatMessage:
sent: UTCDateTime
text: str This tells us that there is no local time stored with the message. This knowledge is invaluable for all sorts of features you might want to build: how to display such a time, how to store it in a database, etc. |
This is definitely something I've considered. Have a look at the second part of this comment: #30 (comment). The reason (legend: A = using
# A
dt.as_local()
# B
dt.as_local_offset()
# A
LocalDateTime(2023, 10, 29, 2, 15, disambiguate="earlier")
# B
NaiveDateTime(2023, 10, 29, 2, 15).assume_local_offset(disambiguate="earlier")
# A
local_dt + hours(5)
# B
# note this isn't exactly the same for periods containing days, months, etc.
(local_dt.as_utc() + hours(5)).as_local_offset()
# A
LocalDateTime.from_timestamp(ts)
# B
UTCDateTime.from_timestamp(ts).as_local_offset() |
Well my system can definitely be set at utc but anyway, if you look it this way we should have 1000 classes to describe exactly what contents you should expect at a glance. I think there’s a balance that for me goes into “practicality beats purity” 99% of the time. A docstring will suffice for this usecase. The other 3 classes (zoned, offset and naive) are really useful and I think everything fits inside. Anyway, just my thoughts. Keep the great work! |
I work with local times all the time in my project O365 (using tzlocal extensively) and I really don’t see what a specific LocalDateTime class will help me with. I kind of don’t needed. In O365 I just store the local timezone and work with it. It’s a regular datetime… I still think you don’t need a specific class for this. Again this is maybe a practicality vs purity kind of thing. |
I don’t see how a datetime with IANA timezone could be ambiguous. Can you provide an example? I think is the least ambiguous one thanks |
I could have been more clear. What I meant: constructing a IANA zoned datetime sometimes requires disambiguation. For example, the below datetime is ambiguous: # at 3am the clock is moved 1 hour back (DST). 2:15am occurs twice
ZonedDateTime(2023, 10, 29, 2, 15, tz="Europe/Amsterdam") Necessary to do: ZonedDateTime(2023, 10, 29, 2, 15, tz="Europe/Amsterdam", disambiguate="later") |
Ok, True. I get it. Does any timezone format (iso, or whatever) encodes if it’s the earlier 2:15 or the later one? |
AFAIK there are two approaches to encode this:
You can see this in the canonical |
Ok, I certainly prefer the offset. The offset already encodes the fold + is something you can do math on (not with a fold flag). In fact now that you mention it: ZonedDateTime should always expose the offset along with the timezone name. |
Indeed, it does 😄 This reminds me of why
|
An improved |
The oddest of the
DateTime
classes is undoubteblyLocalDateTime
. This is because its value is tied to the system timezone, which may change at any time.This leads to tricky situations:
LocalDateTime
can become non-existent after initializationLocalDateTime
can suddenly require disambiguation after initializationlater
is chosen, but the system timezone changes so thatlater
is now a more/less further in time)The current mititagations in place:
LocalDateTime
methods check for existence first and raise exceptions.Potential solutions:
LocalDateTime
in a separate category from the other aware types. Instead ofas_local
, make the method better reflect that you're entering a new reality.FloatingLocalDateTime
to reflect how it 'floats' on the system timezoneas_offset_local
method to avoid race conditions when converting to local datetime at a particular moment.to_offset(dismabiguate=...)
before comparison operationsThe text was updated successfully, but these errors were encountered: