Re: Why does datetime.timedelta only have the attributes 'days' and 'seconds'?

2022-04-17 Thread Peter J. Holzer
On 2022-04-16 20:35:22 -, Jon Ribbens via Python-list wrote:
> On 2022-04-16, Peter J. Holzer  wrote:
> > On 2022-04-16 14:22:04 -, Jon Ribbens via Python-list wrote:
> >> ... although now having looked into the new 'zoneinfo' module slightly,
> >> it really should have a giant red flashing notice at the top of it
> >> saying "BEWARE, TIMEZONES IN PYTHON ARE UTTERLY BROKEN, NEVER USE THEM".
> >>
> >> Suppose we do this:
> >>
> >> >>> import datetime, zoneinfo
> >> >>> LOS_ANGELES = zoneinfo.ZoneInfo('America/Los_Angeles')
> >> >>> UTC = zoneinfo.ZoneInfo('UTC')
> >> >>> d = datetime.datetime(2020, 10, 31, 12, tzinfo=LOS_ANGELES)
> >> >>> print(d)
> >> 2020-10-31 12:00:00-07:00
> >> >>> d1 = d + datetime.timedelta(days=1)
> >> >>> print(d1)
> >> 2020-11-01 12:00:00-08:00
> >>
> >> d1 is *wrong*.
> >
> > No, this is correct. That's the result you want.
> 
> I can categorically guarantee you it is not. But let's put it a
> different way, if you like, if I want to add 24 hours, i.e. 86,400
> seconds (or indeed any other fixed time period), to a timezone-aware
> datetime in Python, how do I do it?

What you *should* be able to do is use datetime.timedelta(hours=24).

Unfortunately, you can't, because somebody decided to add a
normalization rule to timedelta which turns this into timedelta(days=1,
hours=0).

> It would appear that, without converting to UTC before doing the
> calculation, you can't.

When doing calculations of this kind I frankly prefer converting to
"seconds since the epoch" and doing simple arithmetic. (Yes, leap
seconds, I know .. I just ignore those)


> > So why didn't this work for me (I also used Python 3.9)? My guess is
> > that astimezone() doesn't pick the correct time zone.
> 
> astimezone() doesn't pick a time zone at all. It works out the current
> local offset from UTC.

The timezone object it returns also includes a timezone string ("CET" in
my example). So it's not *just* the offset. The result is misleading,
though. You get something which looks like it's a timezone object for
Central European Time, but isn't.

> It doesn't know anything about when or if that
> offset ever changes.

astimezone() doesn't have to. It just has to pick the correct timezone
object. That object then knows about offset changes.


> >> timedelta(days=1) is 24 hours (as you can check by
> >> calling timedelta(days=1).total_seconds() ),
> >
> > It shouldn't be. 1 Day is not 24 hours in the real world.
> 
> Nevertheless, timedelta is a fixed time period so that is the only
> definition possible.

Yeah, you keep repeating that. I think we are talking at cross-purposes
here. You are talking about how timedelta is implemented while I'm
talking what semantics it *should* have.


> >> It appears that with Python it's not so much a guideline as an
> >> absolute concrete rule, and not because programmers will introduce
> >> bugs, but because you need to avoid bugs in the standard library!
> >
> > As a programmer you must always adapt to the problem. Saying "I must do
> > it the wrong way because my library is buggy" is just lazy.
> 
> I didn't say any of that. I said you must do it the conservative way,
> and it's not "my library" that's buggy, it's the language's built-in
> *standard library* that's buggy.

With "your library" I meant "the library you have" not "the library you
wrote". And while having a buggy (or just badly designed) standard
library is especially annoying, you still aren't forced to use it if if
doesn't fit your needs. 

hp

-- 
   _  | Peter J. Holzer| Story must make more sense than reality.
|_|_) ||
| |   | h...@hjp.at |-- Charles Stross, "Creative writing
__/   | http://www.hjp.at/ |   challenge!"


signature.asc
Description: PGP signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pre-Pre-PEP: The datetime.timedeltacal class

2022-04-17 Thread Peter J. Holzer
On 2022-04-17 06:08:54 +1000, Chris Angelico wrote:
> On Sun, 17 Apr 2022 at 03:37, Peter J. Holzer  wrote:
> > Datetime arithmetic in the real world is typically not done in seconds,
> > but in calendaric units: Hours, days, weeks, months, years, ...
> > The problem is that several of these have varying lengths:
> >
> > * 1 minute may be 60 or 61 seconds (theoretically also 59, but that
> >   hasn't happened yet).
> > * 1 day can be 23, 24 or 25 hours (unless you are in Troll, Antarctica,
> >   where it's even weirder).
> 
> I think Troll still only has days that consist of 23-25 hours; the
> weird part is that they move their clocks forward for Oslo's summer,
> which is their winter.

According to Wikipedia they switch between UTC+2 and UTC+0, so the
switchover days would be 22 and 26 hours, respectively.


> > Therefore a new class (provisionally called timedeltacal, because it is
> > calendaric, not absolute) should be added to datetime:
> >
> > Internally it stores months, days, seconds and microseconds as ints.
> >
> > The seconds and microseconds split is mostly for compatibility with
> > datetime and timedelta. We could store seconds as a float instead.
> >
> > We don't store minutes since leap seconds aren't usually represented in
> > "computer time", so they are unlikely to be useful in a timedeltacal
> > object.
> >
> > Days are stored since they aren't a fixed multiple of any smaller unit.
> > Months are stored since they aren't a fixed multiple of any smaller unit.
> >
> > Hours, weeks and years aren't stored since they are always 60 minutes, 7
> > days and 12 months respectively.
> 
> It sounds like you're planning for annual DST changes, but what about
> other shifts? What about when a location adopts standard time, which
> could change their UTC offset (yes, I'm aware that most places adopted
> standard time before UTC was a thing, but we still usually call it a
> UTC offset rather than messing with GMT-UTC changeover) by an
> arbitrary amount, even minutes?

Yes, I think you are right. I first thought it wouldn't matter because
you'd have to look it up in the database anyway, but that's a
non-sequitur. Clearly, when you cities switched from local times to
timezones, one hour had to be longer or shorter than 3600 seconds.
Similarily if India decided to switch to a whole-hour offset, then they
would have one hour of 30 or 90 minutes.

> It might be cleaner to simply have all of the arguments that datetime
> has: year, month, day, hour, minute, second, microsecond (with the
> possibility of merging second/usec into a single float).

Yup.


> > When adding a timedeltacal object to a datetime, the fields are added
> > from most to least significant: First a new date is computed by
> > advancing the number of months specified [TODO: Research how other
> > systems handle overflow (e.g. 2022-01-31 + 1 month: 2022-02-31 doesn't
> > exist)]
> 
> Quick test in Pike:
[...]
> Subtracting seventeen days from today gets us to the 31st of March,
> and adding one month to that gives us the 30th of April. Subtracting
> eighteen days gets us to the 30th of March, and adding a month to that
> _also_ gives us the 30th of April.

Same for PostgreSQL.


> > then advance the number of days. Finally add the number of
> > seconds and microseconds, taking into accout daylight savings time
> > switches if the datetime is time zone aware.
> 
> Here's the local DST switchover:
[results I expected]


> > Subtracting a timedeltacal object from a datetime is the same, just in
> > the opposite direction.
> >
> > Note that t + d - d is in general not equal to t.
> 
> By "in general", do you mean "there will be some odd exceptions", or
> "this is usually going to be unequal"?

The former.

> For instance, going back to the month boundary case, since it's not
> possible for "add one month" from two different dates to give the same
> result, obviously subtracting a month from that result can't give both
> the originals. But for the most part, I would expect t + d - d to be
> equal to t, modulo rounding error and possible DST corrections.
> Crossing a DST boundary shouldn't break this pattern; only landing in
> the actual gap/fold should cause issues.
> Is that the intention?

Yes, exactly.


> > We can't cnange the semantics of datetime - datetime, so there must be a
> > function to compute the difference between to datetimes as a
> > timedeltacal. It could be a method on datetime (maybe t.sub(u) for t-u
> > like in Go) or a constructor which takes two datetime objects.
> >
> > In any case I think that u + (t - u) == t should hold. [TODO: Check that
> > this is possible]
> >
> 
> Isn't that the exact same thing as saying that t + d - d == t?

No, because the overflow handling isn't reversible. Using the semantics
from Pike or Postgres:

2022-03-31 + 1 month == 2022-04-30.
But 2022-04-30 - 1 month == 2022-03-30.

If "spill over" extra days (IIRC, PHP does that but I'd have to check)
2022-03-31 + 1 month == 2022-05-01
2022-05-01

Re: Pre-Pre-PEP: The datetime.timedeltacal class

2022-04-17 Thread Chris Angelico
On Sun, 17 Apr 2022 at 18:17, Peter J. Holzer  wrote:
>
> On 2022-04-17 06:08:54 +1000, Chris Angelico wrote:
> > On Sun, 17 Apr 2022 at 03:37, Peter J. Holzer  wrote:
> > > Datetime arithmetic in the real world is typically not done in seconds,
> > > but in calendaric units: Hours, days, weeks, months, years, ...
> > > The problem is that several of these have varying lengths:
> > >
> > > * 1 minute may be 60 or 61 seconds (theoretically also 59, but that
> > >   hasn't happened yet).
> > > * 1 day can be 23, 24 or 25 hours (unless you are in Troll, Antarctica,
> > >   where it's even weirder).
> >
> > I think Troll still only has days that consist of 23-25 hours; the
> > weird part is that they move their clocks forward for Oslo's summer,
> > which is their winter.
>
> According to Wikipedia they switch between UTC+2 and UTC+0, so the
> switchover days would be 22 and 26 hours, respectively.

Ah, hmm. I'll have to play around with the Antarctica/Troll timezone a
bit, but it could be that they track Oslo time when only Norwegians
are there, and UTC when there are others?

In any case, there are *plenty* of bizarre cases. Whether Troll itself
is one of those or not, there certainly is no shortage of weird
changeover to be found.

> > > Subtracting a timedeltacal object from a datetime is the same, just in
> > > the opposite direction.
> > >
> > > Note that t + d - d is in general not equal to t.
> >
> > By "in general", do you mean "there will be some odd exceptions", or
> > "this is usually going to be unequal"?
>
> The former.

Okay, cool. My lengthy explanation of the question was unnecessary then :)

> > Isn't that the exact same thing as saying that t + d - d == t?
>
> No, because the overflow handling isn't reversible.
>
> > Or are you saying that, when you subtract two timestamps, you can
> > never get one of the odd exceptions that would cause problems?
>
> Yes.
>

Perfect then.

So if I've understood correctly, what you're looking for is a thing
that can be added onto a timezone-aware datetime which will have the
semantics of adding onto each component individually, and then
normalizing. That seems internally consistent, and useful. It's also a
natural continuation of Python's migration from "datetime kinda sorta
supports timezones, but only if you get a third-party package" to
"datetime actually does support timezones" (in the same way that Pike
and PostgreSQL do).

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pre-Pre-PEP: The datetime.timedeltacal class

2022-04-17 Thread Karsten Hilbert
Am Sun, Apr 17, 2022 at 11:10:01AM +1200 schrieb Greg Ewing:

> On 17/04/22 9:17 am, Karsten Hilbert wrote:
> > Take this medication for 1 month !
> >
> >is quite likely to mean "take it for 28 days".
>
> Except when your doctor prescribes 90 days worth of tablets,

It *still* means "take for 28 days" :-)

Karsten
--
GPG  40BE 5B0E C98E 1713 AFA6  5BC0 3BEA AC80 7D4F C89B
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pre-Pre-PEP: The datetime.timedeltacal class

2022-04-17 Thread Peter J. Holzer
On 2022-04-17 10:15:54 +0200, Peter J. Holzer wrote:
> On 2022-04-17 06:08:54 +1000, Chris Angelico wrote:
> > On Sun, 17 Apr 2022 at 03:37, Peter J. Holzer  wrote:
> > > Therefore a new class (provisionally called timedeltacal, because it is
> > > calendaric, not absolute) should be added to datetime:
> > >
> > > Internally it stores months, days, seconds and microseconds as ints.
> > >
> > > The seconds and microseconds split is mostly for compatibility with
> > > datetime and timedelta. We could store seconds as a float instead.
> > >
> > > We don't store minutes since leap seconds aren't usually represented in
> > > "computer time", so they are unlikely to be useful in a timedeltacal
> > > object.
> > >
> > > Days are stored since they aren't a fixed multiple of any smaller unit.
> > > Months are stored since they aren't a fixed multiple of any smaller unit.
> > >
> > > Hours, weeks and years aren't stored since they are always 60 minutes, 7
> > > days and 12 months respectively.
> > 
> > It sounds like you're planning for annual DST changes, but what about
> > other shifts? What about when a location adopts standard time, which
> > could change their UTC offset (yes, I'm aware that most places adopted
> > standard time before UTC was a thing, but we still usually call it a
> > UTC offset rather than messing with GMT-UTC changeover) by an
> > arbitrary amount, even minutes?
> 
> Yes, I think you are right. I first thought it wouldn't matter because
> you'd have to look it up in the database anyway, but that's a
> non-sequitur. Clearly, when you cities switched from local times to
> timezones, one hour had to be longer or shorter than 3600 seconds.
> Similarily if India decided to switch to a whole-hour offset, then they
> would have one hour of 30 or 90 minutes.

Thinking about it some more (while writing test cases) I've changed my
mind again. I think "1 hour" should always be 3600 seconds (disregarding
leap seconds), not "the time until the hour hand has advanced by 30° and
the minute hand is in the same position". If I sit at a bar at 00:30 on
the last Sunday in March, and I continue to sit there for 2 hours, then
it's 03:30 when I get out of the bar, not 02:30. 02:30 doesn't even
exist.

That might be considered an inconsistency, but I think it "does the
right thing" in most cases.

hp

-- 
   _  | Peter J. Holzer| Story must make more sense than reality.
|_|_) ||
| |   | h...@hjp.at |-- Charles Stross, "Creative writing
__/   | http://www.hjp.at/ |   challenge!"


signature.asc
Description: PGP signature
-- 
https://mail.python.org/mailman/listinfo/python-list