[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

> Still, this feature is not appealing enough to try to squeeze into 3.7 
> release schedule.  I think this should go through a round of discussion 
> either on datetime-sig or python-ideas.

Agreed. I will put together some summary in the next week or so and send it to 
one of those (probably python-ideas, I guess).

> Why not start with that?  Remember: python standard library is where code 
> goes to die.  By implementing this feature in dateutil you will not be tied 
> to relatively slow python release schedule.  Of course, you cannot change 
> datetime.now from a 3rd party module, but you can provide your own 
> dateutil.now with the desired features.

This is sort of independent of whether it is implemented in `datetime`, but 
it's a bit more complicated than that. I have already, for example, implemented 
a `today()` utility that does what `datetime.today()` should do if it weren't 
leaking implementation details from how `date.today()` is implemented (i.e. 
gives you the current date at midnight), because I think that's a very common 
use.

The reasons I brought this here are:

1. I would *prefer* it if what I implement is more or less in line with what is 
implemented in the standard library if a standard library solution is going to 
be provided so that my `dateutil` function is more of a version-independent 
backport than a "second way of doing things", so the direction that Python is 
going can inform how I choose to implement my `dateutil` backport.

2. The solution in `dateutil`, which provides functions and classes that 
manipulate `datetime`, will likely be different from how it is handled 
upstream, so it's worth having a discussion about what to do in the standard 
library - the standard library, for example, can overload existing operators on 
`datetime`, provide alternate constructors, or modify the arguments to existing 
alternate constructors. The semantics of this are somewhat different from a 
`dateutil` function that constructs a datetime (for example, there was a lot of 
discussion in `dateutil.utils.today` about what the function should be called - 
it would be cleanly namespaced if it were `datetime.today()`, but `today()` 
seems like it might return a `date`, etc).

For something like this, where `dateutil` is acting more like `boltons` in that 
it is providing little convenience functions and extensions to the `datetime` 
library, I think it makes sense to see whether this might get an implementation 
upstream and what that implementation might look like upstream, even if what 
happens is that I go off and implement something similar in `dateutil` and we 
give it a year or two in `pip` to see what problems arise.

I'll also note that even though `dateutil` has a less constrained release 
schedule and gets to backport features from later Python versions (allowing for 
earlier adoption), it's widely-used enough that I'm not very comfortable making 
backwards-incompatible releases. As a result, once an interface is released, 
it's more or less set, so it's more or less just as important to get this right 
in dateutil as it is in datetime.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Alexander Belopolsky

Alexander Belopolsky  added the comment:

> (And, honestly, `dateutil` would provide a version-independent backport 
> anyway).

Why not start with that?  Remember: python standard library is where code goes 
to die.  By implementing this feature in dateutil you will not be tied to 
relatively slow python release schedule.  Of course, you cannot change 
datetime.now from a 3rd party module, but you can provide your own dateutil.now 
with the desired features.

The only downside I see is that you will need to copy much of datetime.now 
implementation which is rather convoluted.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Alexander Belopolsky

Alexander Belopolsky  added the comment:

I am about +0 on adding a keyword argument to datetime.now.  Note that as I 
wrote in issue 19475 (msg202242), "precision" may be a misleading name because 
python makes no guarantee about the precision of the computer clock.

Still, this feature is not appealing enough to try to squeeze into 3.7 release 
schedule.  I think this should go through a round of discussion either on 
datetime-sig or python-ideas.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

> This can be accomplished rather efficiently by truncating a time tuple:

This will not preserve tzinfo, and (though this is not a concern unless 
nanosecond precision is added), I don't believe it preserves microseconds 
either.

That said, it's also not very readable code without a wrapper - it's not 
obvious that you're trying to truncate, or what level you're truncating to, 
just reading it. I think it's worth considering "truncate" to be a first-class 
operation of datetimes, since it comes up very frequently - people truncating 
off unnecessary microseconds from `now`, people truncating the result of 
`datetime.now()` to get today, people getting the beginning of a given month, 
etc.

Of course, then the question is just "where does this wrapper live". It can 
live in user code, which is probably not ideal since a bunch of people are 
implementing their own versions of this common operation and carrying around 
`utils` submodules or whatever just for this, or it can live in third party 
libraries like `dateutil` or `boltons`, or it can live in the standard library 
- where it will likely get the most optimized treatment. (And, honestly, 
`dateutil` would provide a version-independent backport anyway).

That said, if the answer to the above is "not in the standard library", I think 
it makes sense to add a precision argument to `now`, since that is probably the 
most common use case for this sort of truncation function - and it also makes a 
lot of sense to allow users to specify the precision with which they get the 
current time.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Alexander Belopolsky

Alexander Belopolsky  added the comment:

> replacing all elements of a datetime below a certain level is a very common 
> idiom

This can be accomplished rather efficiently by truncating a time tuple:

>>> t = datetime.now()
>>> datetime(*t.timetuple()[:6])
datetime.datetime(2018, 1, 9, 14, 47, 12)
>>> datetime(*t.timetuple()[:5])
datetime.datetime(2018, 1, 9, 14, 47)
>>> datetime(*t.timetuple()[:4])
datetime.datetime(2018, 1, 9, 14, 0)
>>> datetime(*t.timetuple()[:3])
datetime.datetime(2018, 1, 9, 0, 0)

if you do this often, you can wrap this in a function


_PARTS = {'seconds': 6, 'minutes': 5, ...}
def truncate_to(t, timespec):
return datetime(*t.timetuple()[:_PARTS[timespec])

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Alexander Belopolsky

Alexander Belopolsky  added the comment:

> I think that a "truncate to rrule" function is *way* beyond the scope of the 
> standard library

I agree and I did not propose that.  What I said was that in the process of 
implementing truncate to rrule in dateutil you may encounter some common 
pattern that may benefit from a new stdlib datetime feature.

The operation that I often need is

def truncate_datetime(t:datetime, interval:timedelta, start=datetime.min) -> 
datetime
"""Return the largest datetime of the form start + interval * i not greater 
than t"""

but it is exactly the kind of one-liner that does not belong to stdlib.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

> Looking at the dateutil, I don't see a truncate to rrule function.  Maybe a 
> good starting point would be to implement that in dateutil and if some 
> simpler pattern emerges that can be proposed for stdlib, we can discuss it 
> then.

I think that a "truncate to rrule" function is *way* beyond the scope of the 
standard library, since it would require an actual rrule implementation - about 
which there are still many questions. That said, to accomplish what you want, 
you would just use `rrule.before` (floor) or `rrule.after` (ceil):

from dateutil.rrule import rrule, DAILY
from datetime import datetime

rr = rrule(freq=DAILY, byhour=15, byminute=30, dtstart=datetime(2000, 1, 1))

print(rr.before(datetime(2011, 4, 17, 12, 18)))
# 2011-04-16 15:30:00

print(rr.before(datetime(2011, 4, 17, 16, 17)))
# 2011-04-17 15:30:00


That said, I don't see why this is any different from the `timespec` option to 
isoformat (with the exception that this would support `day` and `month`). In 
fact, in Python 3.7, you can implement it in *terms* of timespec fairly easily:

def truncate(dt, timespec):
return dt.fromisoformat(dt.isoformat(timespec=timespec))

I think the fact that `timespec` is useful indicates why this is useful even 
before serialization - a lot of times you want a datetime *up to a specific 
precision*. I would also argue that the fact that `replace` allows you to 
manipulate each component individually - and the fact that replacing all 
elements of a datetime below a certain level is a very common idiom - 
demonstrates that these arbitrary truncation rulesets *are* special in that 
they are among the most common operations that people do.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Alexander Belopolsky

Alexander Belopolsky  added the comment:

The problem that I have with the round/truncate proposal is that it is not 
general enough.  Days, hours, minutes etc. are just arbitrary intervals that 
became popular for obscure historical and astronomical reasons.  In practice, 
you are as likely to encounter 1 second timeseries as say 10 second timeseries. 
 Hourly timeseries are as likely to use the "top of the hour" (0 minutes) as 
they are to use the "bottom of the hour" (30 minutes).  In general, you want to 
have a kind of "snap to grid" functionality on the time axis where "the grid" 
is an arbitrary RRULE.

Looking at the dateutil, I don't see a truncate to rrule function.  Maybe a 
good starting point would be to implement that in dateutil and if some simpler 
pattern emerges that can be proposed for stdlib, we can discuss it then.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Barry A. Warsaw

Barry A. Warsaw  added the comment:

On Jan 9, 2018, at 08:33, Paul Ganssle  wrote:

> @Barry I don't think it's a good idea to duplicate the `replace` 
> functionality in `datetime` like that. I think the main problem isn't the 
> `.replace`, it's the fact that you have to specify exactly which components 
> you want to set to zero - to get "the beginning of this month" or "today at 
> midnight" or "the beginning of the current hour" or "the beginning of the 
> current minute", you have to manually replace a whole list of these 
> components.

I personally haven’t ever had to do anything but get rid of the microseconds 
field, so maybe my use case is too limited.  It’s a minor inconvenience and I 
don’t like having to throw away an intermediate datetime, so that’s the main 
thing I’d like to improve.  I’d also caution trying to get too fancy and 
complicated for use cases that are already supported or are rare.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

> In my experience, when dealing with temporal data truncation (rounding 
> towards -infinity) is more useful than any other form of rounding. See also 
> issue 19475.

Ah, I agree - if you see that's how my __round__ implementation works. I guess 
that's another problem with the semantics of `round` (which are assumed to 
round to the nearest whole number). I suppose we could implement __floor__, but 
then you have the counter-intuitive property that in order to get access to 
this method, you have to import `math.floor`.

We could add a `datetime.truncate()` method, maybe, and not try to be clever 
about overloading existing operations. Or punt on the idea of truncation in 
general and do what I proposed in the original thread and have all the 
truncation happen in `now`.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Alexander Belopolsky

Alexander Belopolsky  added the comment:

In my experience, when dealing with temporal data truncation (rounding towards 
-infinity) is more useful than any other form of rounding. See also issue 19475.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

One thing to note, the "example implementation" of __round__ above is an actual 
working prototype*:

>>> round(Datetime.now(), 'second')
Datetime(2018, 1, 9, 11, 59, 35)

>>> round(Datetime.now(), 'day')
Datetime(2018, 1, 9, 0, 0)

>>> round(Datetime.now(), 'minute')
Datetime(2018, 1, 9, 11, 59)


So to be clear, `ndigits` can already accept any arbitrary type, it's just the 
fact that it's *called* `ndigits` that may be confusing to users.

*with the exception that it has a bug, the final line should actually be:

return self.replace(**{arg: dflt_args[arg] for arg in args[(idx+1):]})

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

I think if we're going to use `timedelta` then `__mod__` is the more 
appropriate option here, since it would be hard to interpret what `round(dt, 
timedelta(hours=2, microseconds=31))` would do.

Either __mod__ or __round__ with `timedelta` is a bit of a stretch in my 
opinion, and also is limited to well-defined units (and as such you can't round 
to the nearest month or year). I think a `round` taking either a string or an 
enum is the simplest, easiest to understand implementation (and/or adding a 
precision argument to `now` that is equivalent to `round(datetime.now(), 
precision)`).

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Alexander Belopolsky

Alexander Belopolsky  added the comment:

Maybe __round__ can be generalized to take a timedelta instead of ndigits?

For some related prior art, take a look at 
.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

@Barry I don't think it's a good idea to duplicate the `replace` functionality 
in `datetime` like that. I think the main problem isn't the `.replace`, it's 
the fact that you have to specify exactly which components you want to set to 
zero - to get "the beginning of this month" or "today at midnight" or "the 
beginning of the current hour" or "the beginning of the current minute", you 
have to manually replace a whole list of these components.

It doesn't help that `datetime.today()` leaks implementation details from 
`date.today()`, thus making it a slower, worse version of `datetime.now()`, 
since the overwhelmingly most common use cases for datetime rounding are 
probably "get today at midnight" and "get the current time with second 
precision". Still, I think a more general fix would be better now and in the 
future.

Even if we had "get today at midnight" and a `microseconds=0` argument to 
`datetime.now`, without a more general version of this, if (or possibly *when*) 
nanosecond precision is added to `datetime`, you'd now start having to add 
`microseconds=0, nanoseconds=0` or something to `datetime` (depending on the 
implementation of nanoseconds).

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Barry A. Warsaw

Barry A. Warsaw  added the comment:

The .replace(microseconds=0) hack annoys me too, but I'd be happier with a 
simpler solution: make datetime.now() accept a microseconds parameter, so 
datetime.now(microseconds=0) would be equivalent to 
datetime.now().replace(microseconds=0)

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Barry A. Warsaw

Change by Barry A. Warsaw :


--
nosy: +barry

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Change by Paul Ganssle :


--
type:  -> enhancement

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

@Victor: With regards to getting a "date as datetime", that is another way to 
do it that I have also done in the past (and in fact it's how the new 
dateutil.utils.today() function is implemented: 
https://github.com/dateutil/dateutil/blob/master/dateutil/utils.py#L7), but 
it's still not particularly elegant and doesn't obviously convey what you want 
there.

And yes, I'm aware of timespec, I linked it in my original report (it was 
actually added in Python 3.6).

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

Paul Ganssle  added the comment:

An alternate possibility here might be to implement either `__round__` or a 
`round` function in `datetime` (which would basically automatically add this 
precision functionality to *all* the constructors, not just now). An example 
implementation:

from datetime import datetime

class Datetime(datetime):
def __round__(self, ndigits=None):
if ndigits is None:
return self

dflt_args = {
'month': 1,
'day': 1,
'hour': 0,
'minute': 0,
'second': 0,
'microsecond': 0
}

args = list(dflt_args.keys())

if ndigits not in dflt_args:
raise ValueError('Unknown rounding component: %s' % ndigits)

idx = args.index(ndigits)

return self.replace(**{arg: dflt_args[arg] for arg in args[idx:]})


It's not great that `__round__`'s argument is `ndigits`, though. If we don't 
want to just add a `round` method to `datetime`, another option might be to 
implement `__mod__` somehow, so you could do `datetime.now() % 
timedelta(seconds=1)`, but that seems complicated (and also doesn't let you 
round to the nearest month).

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread STINNER Victor

STINNER Victor  added the comment:

> dt = datetime.now(precision='day')

Why not creating a date and then convert it to a datetime object?

> dt = datetime.now().replace(microseconds=0)

Yeah, that one annoys me as well, but I learnt the .replace(microseconds=0) 
hack, or how to format without microseconds.

By the way, are you aware of Python 3.7 enhancement of .isoformat(), the new 
timespec parameter?
https://docs.python.org/dev/library/datetime.html#datetime.datetime.isoformat

--
nosy: +vstinner

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue32522] Add precision argument to datetime.now

2018-01-09 Thread Paul Ganssle

New submission from Paul Ganssle :

One thing I think that is fairly common is the desire to get the current 
datetime only up to a current precision, so you see a lot of things in, say, 
`dateutil` like this:

dt = datetime.now().replace(hours=0, minutes=0, seconds=0, microseconds=0)

Or:

dt = datetime.now().replace(microseconds=0)

I think it would make sense to add a `precision` keyword argument, similar to 
the `timespec` argument to isoformat 
(https://docs.python.org/3/library/datetime.html#datetime.datetime.isoformat), 
then you could just do:

dt = datetime.now(precision='day')

And get the current date as a datetime.

--
components: Library (Lib)
messages: 309703
nosy: belopolsky, p-ganssle, tim.peters
priority: normal
severity: normal
status: open
title: Add precision argument to datetime.now
versions: Python 3.7, Python 3.8

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com