homepage

This issue tracker has been migrated to GitHub , and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: datetime.utcfromtimestamp rounds results incorrectly
Type: behavior Stage: commit review
Components: Library (Lib) Versions: Python 3.6, Python 3.4, Python 3.5
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: belopolsky Nosy List: BreamoreBoy, aconrad, belopolsky, larry, mark.dickinson, python-dev, r.david.murray, serhiy.storchaka, tbarbugli, tim.peters, trcarden, vivanov, vstinner
Priority: normal Keywords: 3.3regression, patch

Created on 2015年02月24日 22:38 by tbarbugli, last changed 2022年04月11日 14:58 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
round_half_even_py34.patch vstinner, 2015年09月09日 00:02 review
Messages (100)
msg236552 - (view) Author: Tommaso Barbugli (tbarbugli) Date: 2015年02月24日 22:38
Hi,
I am porting a library from python 2.7 to 3.4 and I noticed that the behaviour of datetime.utcfromtimestamp is not consistent between the two versions.
For example on python 2.7.5
datetime.utcfromtimestamp(1424817268.274)
returns a datetime with 274000 microseconds
the same code in python 3.4 returns a datetime with 273999 microseconds.
msg236553 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2015年02月24日 22:43
This seems to have changed in 3.3 (versions up to 3.2 return 274000).
msg236577 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015年02月25日 14:11
Most likely this was a rounding fix (ie: not a bug), but hopefully Alexander will know for sure.
msg236580 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年02月25日 15:00
Let me dig up the history, but this does not look like correct rounding to me:
>>> datetime.utcfromtimestamp(1424817268.274)
datetime.datetime(2015, 2, 24, 22, 34, 28, 273999)
>>> decimal.Decimal(1424817268.274)
Decimal('1424817268.2739999294281005859375')
msg236581 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年02月25日 15:13
It looks like it was an intentional change. See #14180 (changeset 75590:1e9cc1a03365).
I am not sure what the motivation was. Note that this change made utcfromtimestamp(t) different from datetime(1970,1,1) + timedelta(seconds=t).
msg236585 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年02月25日 15:33
Victor's motivation for the change was (msg154811):
"""
I chose this rounding method because it is the method used by int(float) and int(time.time()) is a common in programs (more than round(time.time()). Rounding towards zero avoids also producing timestamps in the future.
"""
I recall the earlier discussions of rounding in the datetime module and Mark's explanation that rounding up is fine as long as ordering is preserved. i.e. for x < y round(x) <= round(y).
There are cases when producing times in the future are problematic, for example UNIX make really dislikes when file timestamps are in the future, but if this was the main motivation - rounding towards -infinity would be more appropriate.
In any case, as long as we have the following in the datetime module documentation, I think this behavior is a bug:
"""
On the POSIX compliant platforms, utcfromtimestamp(timestamp) is equivalent to the following expression:
datetime(1970, 1, 1) + timedelta(seconds=timestamp)
"""
>>> timestamp = 1424817268.274
>>> datetime.utcfromtimestamp(timestamp) == datetime(1970, 1, 1) + timedelta(seconds=timestamp)
False
msg236587 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年02月25日 16:06
I started a large change set to support nanoseconds in the C "pytime" API: see the issue #22117. While working on this change, I noticed that the rounding mode of datetime is currently wrong. Extract of a private patch:
typedef enum {
 /* Round towards zero. */
 _PyTime_ROUND_DOWN=0,
 /* Round away from zero.
 For example, used for timeout to wait "at least" N seconds. */
 _PyTime_ROUND_UP=1,
 /* Round towards minus infinity (-inf).
 For example, used for the system clock with UNIX epoch (time_t). */
 _PyTime_ROUND_FLOOR=2
} _PyTime_round_t;
I changed Modules/_datetimemodule.c to use _PyTime_ROUND_FLOOR, instead of _PyTime_ROUND_DOWN.
msg236589 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年02月25日 16:16
> I noticed that the rounding mode of datetime is currently wrong.
What do you mean by "currently"? What versions of python have it wrong?
msg236597 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年02月25日 16:48
Victor,
Would you consider going back to round to nearest? Mark and I put in a lot of effort to get the rounding in the datetime module right. (See for example, #8860.)
Sub-microsecond timesources are still rare and users who work with such should avoid FP timestamps in any case. On the other hand, double precision timestamps are adequate for microsecond resolution now and in the next few decades.
Timestamps like OP's (sec=1424817268, us=274000) should not change when converted to double and back. IMO, the following behavior is a bug.
>>> dt = datetime(2015, 2, 24, 22, 34, 28, 274000)
>>> datetime.utcfromtimestamp(dt.timestamp())
datetime.datetime(2015, 2, 25, 3, 34, 28, 273999)
msg236607 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年02月25日 17:31
> Would you consider going back to round to nearest?
I don't understand "nearest". I prefer to use names of decimal rounding modes:
https://docs.python.org/dev/library/decimal.html#rounding-modes
In my local patch, I'm using ROUND_FLOOR in _decimal: "Round towards -Infinity."
> Mark and I put in a lot of effort to get the rounding in the datetime module right. (See for example, #8860.)
I'm unable right now to say which rounding mode should be used in the decimal module. But it's important to use the same rounding mode for all similar operations. For example, time.time() and datetime.datetime.now() should have the same rounding method (bad example, time.time() returns a float, which doesn't round the result).
For example, in my local patch, I'm using ROUND_FLOOR for:
- datetime.date.fromtimestamp()
- datetime.datetime.fromtimestamp()
- datetime.datetime.now()
- datetime.datetime.utcnow()
- os.utime()
- time.clock_settime()
- time.gmtime()
- time.localtime()
- time.ctime()
Note: the Python implementation of datetime uses time.localtime() and time.gmtime() for fromtimestamp(), so these functions should also have the same rounding method.
msg236608 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年02月25日 17:33
> What do you mean by "currently"? What versions of python have it wrong?
I search for "ROUND" in Modules/_datetimemodule.c: in the Python development branch (default), I found _PyTime_ROUND_DOWN (Round towards zero). Since a bug was reported, I understand that it's not the good rounding method?
msg236609 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年02月25日 17:38
> I don't understand "nearest".
Sorry for using loose terms. I was hoping the in the context of "going back", it would be clear.
I believe the correct mode is "ROUND_HALF_EVEN". This is the mode used by the builtin round() function: 
>>> round(0.5)
0
>>> round(1.5)
2
msg236610 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年02月25日 17:57
> For example, in my local patch, I'm using ROUND_FLOOR for:
> - datetime.date.fromtimestamp()
> - datetime.datetime.fromtimestamp()
These should use ROUND_HALF_EVEN
> - datetime.datetime.now()
> - datetime.datetime.utcnow()
These should not involve floating point arithmetics, but when converting from nanoseconds to microseconds, you should round to nearest 1000 ns with 500 ns ties resolved to even number of microseconds.
> - os.utime()
This takes nanoseconds as an optional argument. Passing floats in times should probably be deprecated. In any case, here you would be rounding floats to nanoseconds and what you do with 0.5 nanoseconds is less important because in most cases they are not even representable as floats.
> - time.clock_settime()
Is this a new method? I don't see it in 3.5.0a1.
> - time.gmtime()
This should be fixed
>>> time.gmtime(1.999999999).tm_sec
1
is really bad and
>>> time.gmtime(-1.999999999)[:6]
(1969, 12, 31, 23, 59, 59)
is probably even worse. 
> - time.localtime()
> - time.ctime()
Same story as in time.gmtime.
msg246049 - (view) Author: Timothy Cardenas (trcarden) Date: 2015年07月01日 23:31
We are seeing this behavior influencing other libraries in python 3.4.
This should never fail if timestamp and fromtimestamp are implemented correctly:
from datetime import datetime
t = datetime.utcnow().timestamp()
t2 = datetime.utcfromtimestamp(t)
assert t == t2, 'Moving from timestamp and back should always work'
msg246073 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015年07月02日 12:23
Because this seems to be a regression, I'm marking this as a release blocker. The RM can decide is isn't, of course.
msg246097 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年07月02日 21:04
Yes, by all means, fix for 3.4, 3.5, and 3.6. If possible I'd appreciate you getting the fix checked in to 3.5 within the next 48 hours, as I'm tagging the next beta release of 3.5 around then, and it'd be nice if this fix went out in that release.
msg246104 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年07月02日 22:55
I'm concerned by this example:
>>> dt = datetime(2015, 2, 24, 22, 34, 28, 274000)
>>> dt - datetime.fromtimestamp(dt.timestamp())
datetime.timedelta(0, 0, 1)
I don't know yet if it should be fixed or not.
If we modify .fromtimestamp(), should we use the same rounding method in datetime constructor? And in datetime.now()/.utcnow()?
I would prefer to keep ROUND_DOWN for .now() and .utcnow() to avoid timestamps in the future. I care less for other methods.
What do you think of this plan?
---
Hum, I don't remember the whole story line of rounding timestamps in Python. Some raw data.
Include/pytime.h of Python 3.5+ has:
typedef enum {
 /* Round towards minus infinity (-inf).
 For example, used to read a clock. */
 _PyTime_ROUND_FLOOR=0,
 /* Round towards infinity (+inf).
 For example, used for timeout to wait "at least" N seconds. */
 _PyTime_ROUND_CEILING
} _PyTime_round_t;
Include/pytime.h of Python 3.4 had:
typedef enum {
 /* Round towards zero. */
 _PyTime_ROUND_DOWN=0,
 /* Round away from zero. */
 _PyTime_ROUND_UP
} _PyTime_round_t;
Include/pytime.h of Python 3.3 and older didn't have rounding.
C files using pytime.h rounding in Python 3.4 (grep -l _PyTime_ROUND */*.c):
Modules/_datetimemodule.c
Modules/posixmodule.c
Modules/selectmodule.c
Modules/signalmodule.c
Modules/_testcapimodule.c
Modules/timemodule.c
Python/pytime.c
It is used by 3 mores C files in Python 3.5:
Modules/socketmodule.c
Modules/_ssl.c
Modules/_threadmodule.c
NEAREST was never implemented in pytime.h.
If I recall correctly, there were inconsitencies between the Python and the C implementation of the datetime module. At least in Python 3.5, both implementations should be consistent (even if some people would prefer a different rounding method).
The private pytime API was rewritten in Python 3.5 to get nanosecond resolution. This API is only used by the datetime module to get the current time.
My rationale for ROUND_DOWN was to follow how UNIX rounds timestmaps. As Alexander wrote, UNIX doesn't like timestamps in the future, so rounding towards minus infinity avoids such issue. Rounding issues become more common on file timestamps with filesystems supporting microsecond resolution or event nanosecond resolution.
msg246121 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年07月03日 02:07
Victor> I don't know yet if it should be fixed or not.
It is my understanding that datetime -> timestamp -> datetime round-tripping was exact in 3.3 for datetimes not too far in the future (as of 2015), but now it breaks for datetime(2015, 2, 24, 22, 34, 28, 274000).
This is clearly a regression and should be fixed.
> UNIX doesn't like timestamps in the future
I don't think this is a serious consideration. The problematic scenario would be obtaining high-resolution timestamp (from say time.time()), converting it to datetime and passing it back to OS as a possibly 0.5μs
higher value. Given that timestamp -> datetime -> timestamp roundtrip by
itself takes over 1μs, it is very unlikely that by the time rounded value hits the OS it is still in the future.
msg246284 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年07月05日 01:28
I'm not going to hold up beta 3 while you guys argue about how to round up or down the number of angels that can dance on the head of a pin.
msg246306 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年07月05日 10:42
Le vendredi 3 juillet 2015, Alexander Belopolsky <report@bugs.python.org> a
écrit :
>
> > UNIX doesn't like timestamps in the future
>
> I don't think this is a serious consideration. The problematic scenario
> would be obtaining high-resolution timestamp (from say time.time()),
> converting it to datetime and passing it back to OS as a possibly 0.5μs
> higher value. Given that timestamp -> datetime -> timestamp roundtrip by
> itself takes over 1μs, it is very unlikely that by the time rounded value
> hits the OS it is still in the future.
>
In many cases the resolution is 1 second. For example, a filesystem with a
resolution of 1second. Or an API only supporting a resolution of 1 second.
With a resoltuion of 1 second, timestamps in the future are likely (50%).
Sorry I don't remember all detail of timestamp rounding and all issues that
I saw.
msg246307 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年07月05日 10:44
My rationale is more general than datetime. But problems araise when
different API use different rounding methods.
msg246334 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年07月05日 19:04
I'll let others fight this battle. In my view, introducing floating point timestamp method for datetime objects was a mistake. See issue #2736. 
Specifically, I would like to invite Velko Ivanov to rethink his rant at msg124197.
If anyone followed his advise and started using timestamp method to JSON-serialize datetimes around 3.3, have undoubtedly being bitten by the present bug (but may not know it yet.)
For those who need robust code, I will continue recommending (dt - EPOCH)/timedelta(seconds=1) expression over the timestamp method and for JSON serialization (dt - EPOCH) // datetime.resolution to convert to integers and EPOCH + n * datetime.resolution to convert back.
msg248942 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年08月21日 02:14
It is really bad that roundtripping current microsecond datetimes doesn't work. About half of all microsecond-resolution datetimes fail to roundtrip correctly now. While the limited precision of a C double guarantees roundtripping of microsecond datetimes "far enough" in the future will necessarily fail, that point is about 200 years from now.
Rather than argue endlessly about rounding, it's possible instead to make the tiniest possible change to the timestamp _produced_ at the start. Here's code explaining it:
 ts = d.timestamp()
 # Will microseconds roundtrip correctly? For times far
 # enough in the future, there aren't enough bits in a C
 # double for that to always work. But for years through
 # about 2241, there are enough bits. How does it fail
 # before then? Very few microsecond datetimes are exactly
 # representable as a binary float. About half the time, the
 # closest representable binary float is a tiny bit less than
 # the decimal value, and that causes truncating 1e6 times
 # the fraction to be 1 less than the original microsecond
 # value.
 if int((ts - int(ts)) * 1e6) != d.microsecond:
 # Roundtripping fails. Add 1 ulp to the timestamp (the
 # tiniest possible change) and see whether that repairs
 # it. It's enough of a change until doubles just plain
 # run out of enough bits.
 mant, exp = math.frexp(ts)
 ulp = math.ldexp(0.5, exp - 52)
 ts2 = ts + ulp
 if int((ts2 - int(ts2)) * 1e6) == d.microsecond:
 ts = ts2
 else:
 # The date is so late in time that a C double's 53
 # bits of precision aren't sufficient to represent
 # microseconds faithfully. Leave the original
 # timestamp alone.
 pass
 # Now ts exactly reproduces the original datetime,
 # if that's at all possible.
This assumes timestamps are >= 0, and that C doubles have 53 bits of precision. Note that because a change of 1 ulp is the smallest possible change for a C double, this cannot make closest-possible unequal datetimes produce out-of-order after-adjustment timestamps.
And, yes, this sucks ;-) But it's far better than having half of timestamps fail to convert back for the next two centuries. Alas, it does nothing to get the intended datetime from a microsecond-resolution timestamp produced _outside_ of Python. That requires rounding timestamps on input - which would be a better approach.
Whatever theoretical problems may exist with rounding, the change to use truncation here is causing real problems now. Practicality beats purity.
msg249282 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年08月28日 17:17
I wish we could use the same algorithm in datetime.utcfromtimestamp as we use in float to string conversion. This may allow the following chain of conversions to round trip in most cases:
float literal -> float -> datetime -> seconds.microseconds string
msg249300 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年08月28日 22:02
> I wish we could use the same algorithm in
> datetime.utcfromtimestamp as we use in float
> to string conversion. This may allow the
> following chain of conversions to round trip in most cases:
>
> float literal -> float -> datetime -> seconds.microseconds string
I don't follow. float->string produces the shortest string that reproduces the float exactly. Any flavor of changing a timestamp to a microsecond-precision datetime is essentially converting a float * 1e6 to an integer - there doesn't seem to be a coherent concept of "shortest integer" that could apply. We have to fill every bit a datetime has.
A variant of the code I posted could be "good enough": take the result we get now (truncate float*1e6). Also add 1 ulp to the float and do that again. If the results are the same, we're done. If the results are different, and the difference is 1, take the second result. Else keep the first result. What this "means" is that we're rounding up if and only if the original is so close to the boundary that the tiniest possible amount of floating-point noise is all that's keeping it from giving a different result - but also that the float "has enough bits" to represent a 1-microsecond difference (which is true of current times, but in a couple centuries will become false).
But that's all nuts compared to just rounding float*1e6 to the nearest int, period. There's nothing wrong with doing that. Truncating is propagating the tiniest possible binary fp representation error all the way into the microseconds. It would be defensible _if_ we were using base-10 floats (in which "representation error" doesn't occur for values expressed _in_ base 10). But we're not. Truncating a base-2 float _as if_ it were a base-10 float is certain to cause problems. Like the one this report is about ;-)
msg249301 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年08月28日 22:19
I probably misremembered a different issue. See msg194311.
>>> timedelta(seconds=0.6112295) == timedelta(seconds=1)*0.6112295
False
I thought the problem there was that the same float was converted to one decimal by str() and to a different decimal by timedelta. But now it looks like it was something else.
Does your algorithm guarantee that any float that is displayed with 6 decimal places or less will convert to a datetime or timedelta with microseconds matching the fractional part?
msg249303 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年08月28日 22:28
OK, I looked at the wrong place. Here is the correct example:
>>> x = float.fromhex('0x1.38f312b1b36bdp-1')
>>> x
0.6112295
>>> round(x, 6)
0.611229
>>> timedelta(0, x).microseconds
611230
but I no longer remember whether we concluded that timedelta got it wrong or round or both or neither. :-)
msg249304 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年08月28日 22:31
> Does your algorithm guarantee that any float that
> is displayed with 6 decimal places or less will
> convert to a datetime or timedelta with microseconds
> matching the fractional part?
No algorithm can, for datetimes far enough in the future (C doubles just plain run out of enough bits).
Apart from negative timestamps (which I didn't consider - they just blow up on my platform :-) ), the intent is to do the best that _can_ be done.
But _proving_ things in this area isn't simple, and there's no need for it: check in a change to round the thing, and be done with it. If Victor wants to rework rounding again, that's fine, but only under a _requirement_ that this particular bug remain fixed. His change created the problem, and it's still languishing half a year after being reported - there's little sense in continuing to wait for him to do something about it.
msg249307 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年08月29日 00:35
Hi, I'm trying to write the rationale of the changes that I wrote in pytime.h in Python 3.3-3.5. The rounding of microseconds or nanoseconds is tricky. The code changed a lot and we used and we are still using various rounding methods depending on the case...
Alexander Belopolsky wrote:
> I believe the correct mode is "ROUND_HALF_EVEN". This is the mode used by the builtin round() function: (...)
Right, round(float) and round(decimal.Decimal) uses the ROUND_HALF_EVEN rounding method.
On Python < 3.3, datetime.datetime.fromtimestamp(float) doesn't use exactly ROUND_HALF_EVEN, but it looks more to "round half away from zero" (the decimal module doesn't seem to support this exact rounding method).
The difference between ROUND_HALF_EVEN and "round half away from zero" is subtle. The two rounding methods only return a different result on the following case:
 divmod(t + us * 1e-6, 1.0)[1] * 1e6 == 0.5
where t and us are integers (t is a number of seconds created by mktime() and us is a number of microseconds in [0; 999999]).
I don't think that the case can occur. I failed to find such case for various values of t between 0 and 2**40, and us=0 or us=1. 1e-6 (10^-6 = 0.000001) cannot be represented exactly in base 2 (IEEE 754).
--
To move forward, we should agree on which rounding method datetime.datetime.fromtimestamp() should use, implement it in pytime.c (add a constant in pytime.h, implement it in pytime.c, and then write unit tests in test_time.py), and then use it in datetime.datetime.fromtimestamp().
IMHO we should only modify the rounding method used by datetime.datetime.fromtimestamp() and datetime.datetime.utcfromtimestamp(), other functions use the "right" rounding method.
msg249308 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年08月29日 01:40
> >>> x = float.fromhex('0x1.38f312b1b36bdp-1')
> >>> x
> 0.6112295
> >>> round(x, 6)
> 0.611229
> >>> timedelta(0, x).microseconds
> 611230
>
> but I no longer remember whether we concluded that
> timedelta got it wrong or round or both or neither. :-)
Here you go:
>>> import decimal
>>> decimal.Decimal(x)
Decimal('0.61122949999999998116351207499974407255649566650390625')
That's the exact value you're actually using. What's "correct" depends on what's intended.
round(x, 6) actually rounds to
>>> decimal.Decimal(round(x, 6))
0.6112290000000000222968310481519438326358795166015625
and that's fine. timedelta's result does not match what using infinite precision would deliver, but I couldn't care much less ;-)
The real lesson to take from all this, when you design your own killer language, is that using a binary floating point type for timestamps comes with many costs and surprises.
msg249309 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年08月29日 02:08
> IMHO we should only modify the rounding method used by
> datetime.datetime.fromtimestamp() and
> datetime.datetime.utcfromtimestamp(), other functions
> use the "right" rounding method.
Fine by me. How about today? ;-)
The regression reported here must get repaired. nearest/even is generally favored when there's a choice.
I personally _prefer_ add-a-half-and-chop in time contexts that need rounding, because it's more uniform. That is, picturing a context that rounds to 1 digit for clarity, using a decimal system, with a uniformly spaced sequence of inputs on the first line, then a line with add-a-half-and-chop results, and then a line with nearest/even results:
0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 input
0.0 1.0 1.0 2.0 2.0 3.0 3.0 4.0 add-a-half-and-chop
0.0 0.0 1.0 2.0 2.0 2.0 3.0 4.0 nearest/even
From the last (nearest/even) line, you'd never guess that the inputs were uniformly spaced; in the second line, you would. nearest/even's "in a tie, sometimes go up, sometimes go down" is, IMO, unnatural in this context.
But it doesn't make a lick of real difference to timestamp functions. We're not working in decimal, and people aren't going to be staring at hex patterns in output. So I'd pick whichever is easier to implement.
msg249519 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月01日 23:52
New changeset abeb625b20c2 by Victor Stinner in branch 'default':
Issue #23517: Add "half up" rounding mode to the _PyTime API
https://hg.python.org/cpython/rev/abeb625b20c2 
msg249520 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月01日 23:57
New changeset b690bf218702 by Victor Stinner in branch 'default':
Issue #23517: datetime.datetime.fromtimestamp() and
https://hg.python.org/cpython/rev/b690bf218702 
msg249521 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月02日 00:03
Ok, I fixed the issue in Python 3.6. Example with the initial message:
$ python2.7 -c 'import datetime; print(datetime.datetime.utcfromtimestamp(1424817268.274).microsecond); print(datetime.datetime.utcfromtimestamp(-1424817268.274).microsecond)'
274000
726000
$ python3.6 -c 'import datetime; print(datetime.datetime.utcfromtimestamp(1424817268.274).microsecond); print(datetime.datetime.utcfromtimestamp(-1424817268.274).microsecond)'
274000
726000
I wrote:
"On Python < 3.3, datetime.datetime.fromtimestamp(float) doesn't use exactly ROUND_HALF_EVEN, but it looks more to "round half away from zero" (the decimal module doesn't seem to support this exact rounding method)."
I was wrong: it's decimal.ROUND_HALF_UP in fact.
I will backport the change to Python 3.4 and 3.5. Since this issue was defined as a bugfix, it should be fixed in Python 3.5.1 (too late for 3.5.0).
msg249525 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月02日 01:41
> too late for 3.5.0
How's that?
msg249528 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月02日 08:41
New changeset 30454ef98e81 by Victor Stinner in branch 'default':
Backed out changeset b690bf218702
https://hg.python.org/cpython/rev/30454ef98e81
New changeset 700303850cd7 by Victor Stinner in branch 'default':
Issue #23517: Fix _PyTime_ObjectToDenominator()
https://hg.python.org/cpython/rev/700303850cd7
New changeset 03c97bb04cd2 by Victor Stinner in branch 'default':
Issue #23517: Reintroduce unit tests for the old PyTime API since it's still
https://hg.python.org/cpython/rev/03c97bb04cd2 
msg249530 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月02日 08:56
Larry Hasting wrote:
>> too late for 3.5.0
> How's that?
Well, for example... my change broke all buildbots.
I don't think that it's good idea to rush to fix Python 3.5 :-) This part of Python (handling timestamps, especially the rounding mode) is complex, I prefer to check for all buildbots and wait for some feedback from users (wait at least 2 weeks).
I reverted my change, another function must be changed:
$ python2 -c 'import datetime; print(datetime.timedelta(microseconds=0.5))'
0:00:00.000001
$ python3 -c 'import datetime; print(datetime.timedelta(microseconds=0.5))'
0:00:00
datetime.timedelta must also use the ROUND_HALF_UP method, as Python 2, instead of ROUND_HALF_EVEN (datetime.py uses the round() function).
$ python2 -c 'import datetime; print(datetime.timedelta(microseconds=1.5))'
0:00:00.000002
$ python3 -c 'import datetime; print(datetime.timedelta(microseconds=1.5))'
0:00:00.000002
I have to rework my patch to use ROUND_HALF_UP in datetime.timedelta(), datetime.datetime.fromtimestamp() and datetime.datetime.utcfromtimestamp(), and update test_datetime.
msg249532 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月02日 10:01
New changeset df074eb2a5be by Victor Stinner in branch 'default':
Issue #23517: Try to fix test_time on "x86 Ubuntu Shared 3.x" buildbot
https://hg.python.org/cpython/rev/df074eb2a5be 
msg249539 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月02日 11:54
New changeset 59185ef69166 by Victor Stinner in branch 'default':
Issue #23517: test_time, skip a test checking a corner case on floating point
https://hg.python.org/cpython/rev/59185ef69166 
msg249569 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月02日 20:37
New changeset 0eb8c182131e by Victor Stinner in branch 'default':
Issue #23517: datetime.timedelta constructor now rounds microseconds to nearest
https://hg.python.org/cpython/rev/0eb8c182131e 
msg249611 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月03日 07:14
New changeset bf634dfe076f by Victor Stinner in branch 'default':
Issue #23517: fromtimestamp() and utcfromtimestamp() methods of
https://hg.python.org/cpython/rev/bf634dfe076f 
msg249728 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月04日 07:11
I will happy delegate to Tim Peters whether or not this should be fixed in 3.5.0, or whether it should wait until 3.5.1 or even 3.6.
Tim, ball's in your court!
msg249744 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 08:53
Backport to Python 3.4 splitted in 3 patches:
* (1) timedelta_round_half_up_py34.patch, backport changeset 0eb8c182131e: "datetime.timedelta constructor now rounds microseconds to nearest with ties going away from zero (ROUND_HALF_UP)".
* (2) round_half_up.patch: add _PyTime_ROUND_HALF_UP rounding mode to the _PyTime API
* (3) fromtimestamp_round_half_up.patch, backport changeset bf634dfe076f: "fromtimestamp() and utcfromtimestamp() methods of datetime.datetime now round microseconds to nearest with ties going away from zero (ROUND_HALF_UP)"
msg249771 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月04日 15:01
Larry, I appreciate the vote of confidence, but I'm ill-equipped to help at the patch level: I'm solely on Windows, and (long story) don't even have a C compiler at the moment. The patch(es) are too broad and delicate to be sure of without kicking the tires (running contrived examples).
So I would sub-delegate to Alexander and/or Mark. They understand the issues too. I was just the most annoying about insisting it get fixed ;-)
msg249777 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 15:40
> Larry, I appreciate the vote of confidence, but I'm ill-equipped to help at the patch level: (...) The patch(es) are too broad and delicate to be sure of without kicking the tires (running contrived examples).
Well, the patches change how timedelta, .fromtimestamp() and .utcfromtimestamp() round the number of microseconds. It's a deliberate choice since it was decided that the current rounding mode is a bug, and not a feature :-)
The code is well tested. There are unit tests on how numbers are rounded for: timedelta, .(utc)fromtimestamp(), and even the C private API _PyTime. The code is (almost) the same in default and was validated on various platforms. So I'm confident on the change.
msg249778 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月04日 15:52
That's great, Victor! Another person trying the code with their own critical eyes would still be prudent. Two days ago you wrote:
> This part of Python (handling timestamps, especially
> the rounding mode) is complex, I prefer to check for
> all buildbots and wait for some feedback from users
> (wait at least 2 weeks).
It's not entirely clear why that switched to "So I'm confident on the change." in 12 days short of 2 weeks ;-)
I have no reason to doubt your confidence. Just saying some independent checking is prudent (but I can't do it at this time).
msg249779 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 16:07
I'll try to find the time to kick the tires on this patch this weekend.
msg249786 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 16:52
2015年09月04日 17:52 GMT+02:00 Tim Peters <report@bugs.python.org>:
> That's great, Victor! Another person trying the code with their own critical eyes would still be prudent.
Sure!
> It's not entirely clear why that switched to "So I'm confident on the change." in 12 days short of 2 weeks ;-)
He he. 2 days ago, the buildbots were broken for various reasons. I
fixed a lot of issues (unrelated to this rounding mode issue), so I
now got the confirmation that the test pass on all platforms.
> I have no reason to doubt your confidence. Just saying some independent checking is prudent (but I can't do it at this time).
Sorry if I wasn't clear. I'm confident, but not enough to not wait for
a review :-)
--
Usually, I don't wait for a review simply because there are too few
reviewers :-( I spent the last 3 years to work alone on the funnny
_PyTime C API project. I started to write an article to tell this
journey ;-)
msg249787 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 16:52
Alexander Belopolsky added the comment:
> I'll try to find the time to kick the tires on this patch this weekend.
Cool! Keep me in touch ;-)
msg249788 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 16:58
Victor,
Do I understand correctly that this is already committed in 3.4 - 3.6 development branches and we just need to decide whether to cherry-pick this fix to 3.5rc?
Is the "review" link up-to date?
msg249791 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 17:23
It looks like this patch violates fromtimestamp(s) == EPOCH + timedelta(seconds=s) invariant:
Python 3.6.0a0 (default:73911e6c97c8, Sep 4 2015, 13:14:12)
>>> E = datetime(1970,1,1,tzinfo=timezone.utc)
>>> s = -1/2**7
>>> datetime.fromtimestamp(s, timezone.utc) == E + timedelta(seconds=s)
False
msg249796 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月04日 17:35
FYI, that invariant failed for me just now under the released 3.4.3 too:
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40) [MSC v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import *
>>> E = datetime(1970,1,1,tzinfo=timezone.utc)
>>> s = -1/2**7
>>> datetime.fromtimestamp(s, timezone.utc)
datetime.datetime(1969, 12, 31, 23, 59, 59, 992187, tzinfo=datetime.timezone.utc)
>>> E + timedelta(seconds=s)
datetime.datetime(1969, 12, 31, 23, 59, 59, 992188, tzinfo=datetime.timezone.utc)
It's an exactly-tied rounding case for the exactly-representable-in-binary s = -0.0078125.
msg249798 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 17:54
Can someone check 3.3? I believe that was the release where we tried to get various rounding issues right.
msg249825 - (view) Author: Mark Lawrence (BreamoreBoy) * Date: 2015年09月04日 20:30
Python 3.3.5 (v3.3.5:62cf4e77f785, Mar 9 2014, 10:35:05) [MSC v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import *
>>> E = datetime(1970,1,1,tzinfo=timezone.utc)
>>> s = -1/2**7
>>> datetime.fromtimestamp(s, timezone.utc)
datetime.datetime(1969, 12, 31, 23, 59, 59, 992187, tzinfo=datetime.timezone.utc)
>>> E + timedelta(seconds=s)
datetime.datetime(1969, 12, 31, 23, 59, 59, 992187, tzinfo=datetime.timezone.utc)
FWIW Windows 10.
msg249827 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 20:38
Oh, it looks like my implementation of ROUND_HALF_UP is wrong.
Negative numbers are not correctly rounded :-/ I'm working on a fix.
msg249834 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 21:05
Victor,
I did not quite understand why you've chosen ROUND_HALF_UP over ROUND_HALF_EVEN, but as long as fromtimestamp() uses the same rounding as timedelta() - I am happy.
msg249835 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月04日 21:09
Alex, if you like, I'll take the blame for the rounding method - I did express a preference for it here:
http://bugs.python.org/issue23517#msg249309
When I looked at the code earlier, the round-half-up implementation looked good to me (floor(x+0.5) if x >= 0 else ceil(x-0.5)).
msg249836 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 21:27
> I did not quite understand why you've chosen ROUND_HALF_UP over ROUND_HALF_EVEN, but as long as fromtimestamp() uses the same rounding as timedelta() - I am happy.
Not only Tim prefers this rounding mode, Python 2.7 also uses the same mode, and the original poster basically said that Python 3 doesn't behave like Python 2. So... the most obvious rounding mode is the same than Python 2, ROUND_HALF_UP.
msg249841 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月04日 22:09
New changeset 3c29d05c0710 by Victor Stinner in branch 'default':
Issue #23517: Fix implementation of the ROUND_HALF_UP rounding mode in
https://hg.python.org/cpython/rev/3c29d05c0710 
msg249842 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 22:11
It would be nice to hear from Mark Dickinson on this. In Python 3, we took a much more systematic approach to rounding than a rather haphazard Python 2. For example, the rounding mode for timedelta(0, float_seconds) is not specified in Python 2, but it is in Python 3:
https://docs.python.org/2/library/datetime.html#datetime.timedelta
https://docs.python.org/3/library/datetime.html#datetime.timedelta 
msg249844 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 22:25
> Is the "review" link up-to date?
Oh, I wrote a patch serie, but Rietveld doesn't know the dependencies between my patches...
So I attached combined_py34.patch which combines the 3 patches into a single one, hard to reviewer, but it should work with Rietveld.
msg249845 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月04日 22:31
Yes, it would be good to hear from Mark. When I first saw this report, I checked to see whether he was on the nosy list. He is, but is apparently busy elsewhere.
My opinions haven't changed: nearest/even is unnatural for rounding times ("sometimes up, sometimes down" grates against the nature of monotonically non-decreasing timestamps), but it doesn't make a lick of real difference. If the inconsequential choice was documented, then sure, that it _was_ documented makes it consequential, so that's the one that must be used.
msg249847 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 22:34
> My opinions haven't changed: nearest/even is unnatural for rounding times ("sometimes up, sometimes down" grates against the nature of monotonically non-decreasing timestamps),
By the way, why does Python use ROUND_HALF_EVEN for round()? It also feels unnatural to me!
>>> round(0.5)
0
>>> round(1.5)
2
msg249848 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月04日 22:38
Victor, there are good "theoretical" reasons for using half/even rounding in _general_ use. But this bug report isn't the place to get into them. Here it just should be enough to note that the IEEE 754 floating point standard _requires_ half/even to be the default rounding mode. Slowly but surely, all the world's non-business computer applications are moving toward that. Python is just one of 'em.
msg249849 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 22:43
> By the way, why does Python use ROUND_HALF_EVEN for round()?
ROUND_HALF_EVEN does not introduce statistical bias in your floating point data. With this choice a randomly chosen decimal has an equal chance of being rounded up or down. I think one can prove the same for a binary float being converted to a decimal with a fixed number of places after dot. The builtin round() is a unique beast and I would have to ask Mark to know what its statistical properties are, but in general round-half-to-even is justified by the desire of not introducing a statistical bias and I don't think the natural asymmetry of the time line matters in this argument.
msg249850 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 22:46
Here is what Wikipedia has to say on the subject:
"""
Round half to even .. This method treats positive and negative values symmetrically, and is therefore free of sign bias. More importantly, for reasonable distributions of y values, the expected (average) value of the rounded numbers is the same as that of the original numbers. However, this rule will introduce a towards-zero bias when y − 0.5 is even, and a towards-infinity bias for when it is odd.
"""
https://en.wikipedia.org/wiki/Rounding#Round_half_to_even 
msg249851 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 22:47
.. and here is an unbeatable argument: "this variant of the round-to-nearest method is also called unbiased rounding, convergent rounding, statistician's rounding, **Dutch rounding**, Gaussian rounding, odd–even rounding, or bankers' rounding."
msg249853 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月04日 22:49
.. and "[Round half to even] is the default rounding mode used in IEEE 754 computing functions and operators."
msg249856 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月04日 23:03
Ok, thanks for the explanation :-)
msg249875 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月05日 00:54
Goodness. It's the properties of "randomly chosen decimals" that have nothing to do with timestamps ;-) timestamps aren't random, so "statistical bias" is roughly meaningless in this context. I gave a specific example before of how nearest/even destroys obvious regularities in a _sequence_ of timestamps, where half-up preserves as much of the input regularity as possible. That's worth more than a million abstract "head arguments" on Wikipedia.
But it doesn't make a lick of real difference either way. We're rounding to microseconds, and there are only 64 "fractional parts" where the methods could _possibly_ deliver different results: those of the form i/128 for i in range(1, 128, 2). All and only those are exactly representable in base 2, and require exactly 7 decimal digits "after the decimal point" to express in decimal, _and_ end with "5" in decimal. Half end with "25" while the other half with "75". So Alex's 1/128 is one of the only 32 possible fractional parts where it makes a difference. We systematically force all these cases to even, and dare think that's _not_ "biased"? Half-up would leave half the results even and half odd, exactly the same as the _input_ odd/even distribution of the 6th digit. And preserve the input strict alternation between even and odd in the 6th digit. nearest/even destroys all of that.
Except that, I agree, there's no arguing with "Dutch rounding" ;-)
msg249879 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月05日 01:25
> timestamps aren't random, so "statistical bias" is roughly meaningless in this context.
I agree. I don't think I made any arguments about timestamps specifically other than a consistency with timedeltas. In the later case, I think all the usual arguments for half-to-even tiebreaker apply.
Let's just leave it at "When in doubt - use Dutch rounding."
msg249885 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月05日 02:28
Bah. It doesn't matter who's consuming the rounding of a binary float to decimal microseconds: there are only 32 possible fractional parts where nearest/even and half-up deliver different results. half-up preserves properties of these specific inputs that nearest/even destroys. These inputs themselves have no bias - they're utterly uniformly spaced.
Not only does nearest/even _introduce_ bias on these inputs by destroying these properties, it doesn't even preserve the spacing between them. Half-up leaves them all 5 microseconds apart, while nearest/even creates a bizarre "sometimes 4 microseconds apart, sometimes 6" output spacing out of thin air.
So it's not a question of "when in doubt" to me, it's a question of "live up to what the docs already say". Although, again, it doesn't make a lick of real difference. That's why we'll never stop arguing about it ;-)
msg249886 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月05日 02:34
> Half-up leaves them all 5 microseconds apart,
When only looking at the decimal digit in the 6th place after rounding. Which is all I did look at ;-)
msg249898 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月05日 08:50
New changeset d755f75019c8 by Victor Stinner in branch 'default':
Issue #23517: Skip a datetime test on Windows
https://hg.python.org/cpython/rev/d755f75019c8 
msg249909 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月05日 14:08
> It doesn't matter who's consuming the rounding of a binary
> float to decimal microseconds
That is true, but what does matter is who is producing the incoming floats. Consider an extreme case of a timer that ticks twice a microsecond and gives you results in a timespec struct style sec, nsec pair. If you convert those to timedeltas with timedelta(0, sec, nsec/1000), you will see half of nsec/1000 floats ending in .5 and half ending with .0. If you have a large sample of (sec, nsec) measurements, the mean of corresponding timedeltas will be 0.25μs larger with the half-up rounding than the actual mean of your samples.
Is round-half-to-even a panacea? Of course not. In an extreme case if all your measurements are 1.5μs - it will not help at all, but in a more realistic sample you are likely to have an approximately equal number of even and odd μs and the Dutch rounding will affect the mean much less than round-half-up.
As you said, for most uses none of this matters, but we are not discussing a change to timedelta(0, s) rounding here. All I want from this issue is to keep the promise that we make in the docs:
On the POSIX compliant platforms, utcfromtimestamp(timestamp) is equivalent to the following expression:
datetime(1970, 1, 1) + timedelta(seconds=timestamp)
https://docs.python.org/3/library/datetime.html#datetime.datetime.utcfromtimestamp
Tim, while we have this entertaining theoretical dispute, I am afraid we are leaving Victor confused about what he has to do in his patch. I think we agree that fromtimestamp() should use Dutch rounding. We only disagree why.
If this is the case, please close this thread with a clear advise to use round-half-to-even and we can continue discussing the rationale privately or in a different forum.
msg249910 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月05日 16:46
Victor, sorry if I muddied the waters here: Alex & I do agree nearest/even must be used. It's documented for timedelta already, and the seconds-from-the-epoch invariant Alex showed is at least as important to preserve as round-tripping.
Alex, agreed about the overall mean in the example, but expect that continuing to exchange contrived examples isn't really a major life goal for either of us ;-) Thanks for playing along.
msg249925 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月05日 20:49
> Alex & I do agree nearest/even must be used.
Ok, so Python 3.6 should be updated to use ROUND_HALF_EVEN, and then
updated patches should be backported to Python 3.4 and 3.5. Right?
Right now, Python 3.6 uses ROUND_HALF_UP.
msg249926 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月05日 21:05
You may find it easier to start with a patch for 3.5 which is the only time sensitive task. I'll be happy to review your code in whatever form you find it easier to submit, but I believe in hg it is easier to forward port than to backport.
msg250043 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月07日 03:55
So is this considered broken enough that I need to accept a fix for 3.5.0? And has a consensus been reached about exactly what that fix would be?
msg250047 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2015年09月07日 04:06
Universal consensus on ROUND_HALF_EVEN, yes.
I would like to see it repaired for 3.5.0, but that's just me saying so without offering to do a single damned thing to make it happen ;-)
msg250048 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月07日 04:07
Well, I'm already holding up rc3 on one other issue, might as well fix this too. Can somebody make me a pull request?
msg250049 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月07日 04:08
I suspect we're not fixing this in 3.4, so I'm removing 3.4 from the version list.
msg250066 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月07日 05:33
Okay, this is literally the only thing rc3 is waiting on now.
msg250075 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月07日 05:57
I understand that I have to implement a new rounding mode. The code will be
new, I'm not condifent enough to push it immedialty into python 3.5. IMHO a
buggy rounding mode is worse than keeping the current rounding mode. The
rounding mode changed in python 3.3. There is no urgency to fix it.
I will change python 3.6, then 3.4 and 3.5.1.
Le 7 sept. 2015 07:33, "Larry Hastings" <report@bugs.python.org> a écrit :
>
> Larry Hastings added the comment:
>
> Okay, this is literally the only thing rc3 is waiting on now.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue23517>
> _______________________________________
>
msg250083 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月07日 06:49
Is it appropriate to make this change as a "bug fix", in already-released versions of Python? Would you be happy or sad if you updated your Python from 3.x.y to 3.x.y+1 and the rounding method used when converting floats to datetime stamps changed?
msg250101 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月07日 12:04
Well, for now I assume it really truly genuinely isn't going in 3.5.0. I suppose we can debate about 3.4.x and 3.5.1 later, once we have a fix everybody is happy with.
msg250103 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月07日 12:26
Larry> Well, for now I assume it really truly genuinely isn't going in 3.5.0.
This is an unfortunate outcome.
Larry> I suppose we can debate about 3.4.x and 3.5.1 later
It is even more unfortunate that the question of whether this regression is a bug or not is up for debate again.
Victor> The code will be new, I'm not condifent enough to push it immedialty into python 3.5.
I don't understand why any new code is needed to fix a regression. All you need to do is to revert a few chunks of 1e9cc1a03365 where the regression was introduced.
msg250104 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月07日 12:38
If the guy doing the work says "don't merge it in 3.5.0", and we're at the final release candidate before 3.5.0 final ships, and we don't even have a patch that everyone likes yet... it does seem like it's not going to happen for 3.5.0. Unfortunate perhaps but that's the situation we're in.
msg250107 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月07日 12:56
> All you need to do is to revert a few chunks of 1e9cc1a03365 where the regression was introduced.
Hum, please don't revert this change. I spent a lot of days to write pytime.c/.h.
My patches add more unit tests to datetime, to test the exact rounding mode.
> Well, for now I assume it really truly genuinely isn't going in 3.5.0. I suppose we can debate about 3.4.x and 3.5.1 later, once we have a fix everybody is happy with.
The rounding mode of microseconds only impact a very few people. The rounding mode changed in 3.3, again in 3.4 and again in 3.5. This issue is the first bug report. So I think it's fine to modify 3.4.x and 3.5.x.
msg250108 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2015年09月07日 13:38
Victor> please don't revert this change.
I did not suggest reverting the entire commit. The change that affects fromdatetime() is just
- us = round(frac * 1e6)
+ us = int(frac * 1e6)
in datetime.py. It is probably more involved in _datetimemodule.c, but cannot be that bad. You can still keep pytime.c/.h.
msg250113 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015年09月07日 15:09
FWIW, count me as +1 on roundTiesToEven, mostly just for consistency. It's easier to remember that pretty much everything in Python 3 does round-ties-to-even (round function, string formatting, float literal evaluations, int-to-float conversion, Fraction-to-float conversion, ...) than to have to remember that there's a handful of corner cases where we do roundTiesToAway instead.
msg250259 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月08日 21:59
New changeset d0bb896f9b14 by Victor Stinner in branch 'default':
Revert change 0eb8c182131e:
https://hg.python.org/cpython/rev/d0bb896f9b14 
msg250265 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月08日 23:04
New changeset 171d5590ebc3 by Victor Stinner in branch 'default':
Issue #23517: fromtimestamp() and utcfromtimestamp() methods of
https://hg.python.org/cpython/rev/171d5590ebc3 
msg250268 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月09日 00:00
I wish people wouldn't remove old patches. There's no harm in leaving them, and it may be a useful historical record.
msg250269 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月09日 00:02
I modified Python 3.6 to use ROUND_HALF_EVEN rounding mode in datetime.timedelta, datetime.datetime.fromtimestamp(), datetime.datetime.utcfromtimestamp().
round_half_even_py34.patch: Backport this change to Python 3.4. I tried to write a minimal patch only changing datetime, not pytime.
msg250270 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月09日 00:04
> I wish people wouldn't remove old patches. There's no harm in leaving them, and it may be a useful historical record.
It became hard to read this issue, it has a long history. My old patches for Python 3.4 were for ROUND_HALF_UP, but it was then decided to use ROUND_HALF_EVEN. Technically, patches are not "removed" but "unlinked" from the issue: you can get from the history at the bottom of this page.
msg250304 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2015年09月09日 12:58
Given Victor's reluctance to get this in to 3.5.0, this can't even be marked as a "deferred blocker" anymore. Demoting to normal priority.
msg250405 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月10日 19:03
Alexander: can you please review attached round_half_even_py34.patch? It's the minimum patch to fix datetime/timedelta rounding for Python 3.4 (and 3.5).
msg250973 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2015年09月18日 12:58
New changeset ee1cf1b188d2 by Victor Stinner in branch '3.4':
Issue #23517: Fix rounding in fromtimestamp() and utcfromtimestamp() methods
https://hg.python.org/cpython/rev/ee1cf1b188d2 
msg250974 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015年09月18日 13:01
Ok, I fixed Python 3.4 and 3.5 too: fromtimestamp() and utcfromtimestamp() now uses also ROUND_HALF_EVEN rounding mode, as timedelta constructor. I explained in Misc/NEWS that the change was made to respect the property:
(datetime(1970,1,1) + timedelta(seconds=t)) == datetime.utcfromtimestamp(t)
Thanks Alexander Belopolsky & Tim Peters for your explanation on rounding. It's not a simple problem :-)
Thanks Tommaso Barbugli for the bug report: it's now fixed on all (maintained) Python 3 versions: 3.4, 3.5 and 3.6. The fix will be part of Python 3.5.1.
History
Date User Action Args
2022年04月11日 14:58:13adminsetgithub: 67705
2015年09月18日 13:11:31vstinnersetstatus: open -> closed
resolution: fixed
2015年09月18日 13:01:57vstinnersetmessages: + msg250974
versions: + Python 3.4
2015年09月18日 12:58:34python-devsetmessages: + msg250973
2015年09月10日 19:04:58serhiy.storchakasetnosy: + serhiy.storchaka
2015年09月10日 19:03:23vstinnersetmessages: + msg250405
2015年09月09日 12:58:01larrysetpriority: deferred blocker -> normal

messages: + msg250304
2015年09月09日 00:04:17vstinnersetmessages: + msg250270
2015年09月09日 00:02:08vstinnersetfiles: + round_half_even_py34.patch

messages: + msg250269
2015年09月09日 00:00:37larrysetmessages: + msg250268
2015年09月08日 23:59:56vstinnersetfiles: - combined_py34.patch
2015年09月08日 23:59:55vstinnersetfiles: - fromtimestamp_round_half_up_py34-2.patch
2015年09月08日 23:59:54vstinnersetfiles: - round_half_up_py34-2.patch
2015年09月08日 23:59:53vstinnersetfiles: - timedelta_round_half_up_py34.patch
2015年09月08日 23:04:28python-devsetmessages: + msg250265
2015年09月08日 21:59:50python-devsetmessages: + msg250259
2015年09月07日 15:09:28mark.dickinsonsetmessages: + msg250113
2015年09月07日 13:38:47belopolskysetmessages: + msg250108
2015年09月07日 12:56:39vstinnersetmessages: + msg250107
2015年09月07日 12:38:39larrysetmessages: + msg250104
2015年09月07日 12:26:40belopolskysetmessages: + msg250103
2015年09月07日 12:04:12larrysetmessages: + msg250101
2015年09月07日 06:49:25larrysetmessages: + msg250083
2015年09月07日 05:57:17vstinnersetmessages: + msg250075
2015年09月07日 05:33:26larrysetmessages: + msg250066
2015年09月07日 04:08:05larrysetmessages: + msg250049
versions: - Python 3.4
2015年09月07日 04:07:38larrysetmessages: + msg250048
2015年09月07日 04:06:10tim.peterssetmessages: + msg250047
2015年09月07日 03:55:27larrysetmessages: + msg250043
2015年09月05日 21:05:28belopolskysetmessages: + msg249926
2015年09月05日 20:49:31vstinnersetmessages: + msg249925
2015年09月05日 16:46:37tim.peterssetmessages: + msg249910
2015年09月05日 14:08:38belopolskysetmessages: + msg249909
2015年09月05日 08:50:41python-devsetmessages: + msg249898
2015年09月05日 02:34:54tim.peterssetmessages: + msg249886
2015年09月05日 02:28:11tim.peterssetmessages: + msg249885
2015年09月05日 01:25:22belopolskysetmessages: + msg249879
2015年09月05日 00:54:27tim.peterssetmessages: + msg249875
2015年09月04日 23:03:49vstinnersetmessages: + msg249856
2015年09月04日 22:49:21belopolskysetmessages: + msg249853
2015年09月04日 22:47:59belopolskysetmessages: + msg249851
2015年09月04日 22:46:03belopolskysetmessages: + msg249850
2015年09月04日 22:43:50belopolskysetmessages: + msg249849
2015年09月04日 22:38:26tim.peterssetmessages: + msg249848
2015年09月04日 22:34:49vstinnersetmessages: + msg249847
2015年09月04日 22:31:29tim.peterssetmessages: + msg249845
2015年09月04日 22:25:06vstinnersetfiles: + combined_py34.patch

messages: + msg249844
2015年09月04日 22:21:22vstinnersetfiles: + fromtimestamp_round_half_up_py34-2.patch
2015年09月04日 22:21:04vstinnersetfiles: - fromtimestamp_round_half_up_py34-2.patch
2015年09月04日 22:19:30vstinnersetfiles: - fromtimestamp_round_half_up_py34.patch
2015年09月04日 22:19:29vstinnersetfiles: - round_half_up_py34.patch
2015年09月04日 22:19:22vstinnersetfiles: + fromtimestamp_round_half_up_py34-2.patch
2015年09月04日 22:18:06vstinnersetfiles: + round_half_up_py34-2.patch
2015年09月04日 22:11:28belopolskysetmessages: + msg249842
2015年09月04日 22:09:31python-devsetmessages: + msg249841
2015年09月04日 21:27:45vstinnersetmessages: + msg249836
2015年09月04日 21:09:05tim.peterssetmessages: + msg249835
2015年09月04日 21:05:01belopolskysetmessages: + msg249834
2015年09月04日 20:38:19vstinnersetmessages: + msg249827
2015年09月04日 20:30:24BreamoreBoysetnosy: + BreamoreBoy
messages: + msg249825
2015年09月04日 17:54:00belopolskysetmessages: + msg249798
2015年09月04日 17:35:09tim.peterssetmessages: + msg249796
2015年09月04日 17:23:19belopolskysetmessages: + msg249791
2015年09月04日 16:58:28belopolskysetmessages: + msg249788
2015年09月04日 16:54:47belopolskysetstage: commit review
2015年09月04日 16:52:57vstinnersetmessages: + msg249787
2015年09月04日 16:52:37vstinnersetmessages: + msg249786
2015年09月04日 16:07:42belopolskysetassignee: belopolsky
messages: + msg249779
2015年09月04日 15:52:32tim.peterssetmessages: + msg249778
2015年09月04日 15:40:27vstinnersetmessages: + msg249777
2015年09月04日 15:01:56tim.peterssetmessages: + msg249771
2015年09月04日 08:53:31vstinnersetfiles: + fromtimestamp_round_half_up_py34.patch
2015年09月04日 08:53:19vstinnersetfiles: + round_half_up_py34.patch
2015年09月04日 08:53:09vstinnersetfiles: + timedelta_round_half_up_py34.patch
keywords: + patch
messages: + msg249744
2015年09月04日 07:11:32larrysetmessages: + msg249728
2015年09月03日 07:14:37python-devsetmessages: + msg249611
2015年09月02日 20:37:33python-devsetmessages: + msg249569
2015年09月02日 11:54:44python-devsetmessages: + msg249539
2015年09月02日 10:01:46python-devsetmessages: + msg249532
2015年09月02日 08:56:47vstinnersetmessages: + msg249530
2015年09月02日 08:41:02python-devsetmessages: + msg249528
2015年09月02日 01:41:13larrysetmessages: + msg249525
2015年09月02日 00:03:56vstinnersetmessages: + msg249521
2015年09月01日 23:57:35python-devsetmessages: + msg249520
2015年09月01日 23:52:53python-devsetnosy: + python-dev
messages: + msg249519
2015年08月29日 02:08:18tim.peterssetmessages: + msg249309
2015年08月29日 01:40:11tim.peterssetmessages: + msg249308
2015年08月29日 00:35:02vstinnersetmessages: + msg249307
2015年08月28日 22:31:58tim.peterssetmessages: + msg249304
2015年08月28日 22:28:48belopolskysetmessages: + msg249303
2015年08月28日 22:19:07belopolskysetmessages: + msg249301
2015年08月28日 22:02:47tim.peterssetmessages: + msg249300
2015年08月28日 17:17:30belopolskysetnosy: + belopolsky
messages: + msg249282
2015年08月28日 17:08:48belopolskysettitle: datetime.utcfromtimestamp parses timestamps incorrectly -> datetime.utcfromtimestamp rounds results incorrectly
2015年08月27日 20:28:19aconradsetnosy: + aconrad
2015年08月21日 02:14:48tim.peterssetnosy: + tim.peters
messages: + msg248942
2015年07月21日 07:04:57ethan.furmansetnosy: - ethan.furman
2015年07月05日 19:04:33belopolskysetnosy: - belopolsky
2015年07月05日 19:04:08belopolskysetnosy: + vivanov
messages: + msg246334
2015年07月05日 10:44:38vstinnersetmessages: + msg246307
2015年07月05日 10:42:37vstinnersetmessages: + msg246306
2015年07月05日 01:28:29larrysetpriority: release blocker -> deferred blocker

messages: + msg246284
2015年07月03日 02:07:08belopolskysetmessages: + msg246121
2015年07月02日 22:55:45vstinnersetmessages: + msg246104
2015年07月02日 21:04:44larrysetmessages: + msg246097
2015年07月02日 12:23:26r.david.murraysetpriority: normal -> release blocker
versions: + Python 3.5, Python 3.6
nosy: + larry

messages: + msg246073
2015年07月01日 23:31:55trcardensetnosy: + trcarden
messages: + msg246049
2015年02月25日 17:57:09belopolskysetmessages: + msg236610
2015年02月25日 17:38:00belopolskysetmessages: + msg236609
2015年02月25日 17:33:48vstinnersetmessages: + msg236608
2015年02月25日 17:31:56vstinnersetmessages: + msg236607
2015年02月25日 17:30:57belopolskysetkeywords: + 3.3regression, - 3.2regression
2015年02月25日 16:48:46belopolskysetmessages: + msg236597
2015年02月25日 16:16:17belopolskysetmessages: + msg236589
2015年02月25日 16:06:34vstinnersetmessages: + msg236587
2015年02月25日 15:33:56belopolskysetnosy: + vstinner
2015年02月25日 15:33:09belopolskysetmessages: + msg236585
2015年02月25日 15:13:28belopolskysetkeywords: + 3.2regression

messages: + msg236581
2015年02月25日 15:00:55belopolskysetnosy: + mark.dickinson
messages: + msg236580
2015年02月25日 14:11:57r.david.murraysetnosy: + r.david.murray
messages: + msg236577
2015年02月25日 09:30:54ned.deilysetnosy: + belopolsky
2015年02月24日 22:43:21ethan.furmansetnosy: + ethan.furman
messages: + msg236553
2015年02月24日 22:38:42tbarbuglicreate

AltStyle によって変換されたページ (->オリジナル) /