Issue23574
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.
Created on 2015-03-03 14:42 by vstinner, last changed 2022-04-11 14:58 by admin.
Files | ||||
---|---|---|---|---|
File name | Uploaded | Description | Edit | |
datetime_leapsecond.patch | vstinner, 2015-03-03 14:43 | review | ||
support_leap_seconds.patch | vstinner, 2015-03-03 15:45 | review |
Messages (15) | |||
---|---|---|---|
msg237142 - (view) | Author: STINNER Victor (vstinner) * | Date: 2015-03-03 14:42 | |
A leap second will be added in June 2015: http://www.usatoday.com/story/tech/2015/01/08/computer-chaos-feares/21433363/ The datetime module explicitly doesn't support leap seconds: https://docs.python.org/dev/library/datetime.html#datetime.date.fromtimestamp "Note that on non-POSIX systems that include leap seconds in their notion of a timestamp, leap seconds are ignored by fromtimestamp()." The following bug in oslo.utils was reported because datetime is indirectly used to unserialize a date, but it fails with ValueError("second must be in 0..59") if the second is 60: https://bugs.launchpad.net/oslo.utils/+bug/1427212 Would it be possible to silently drop ignore leap seconds in datetime.datetime constructor, as already done in datetime.datetime.fromtimestamp? Attached patch modified datetime constructor to drop leap seconds: replace second=60 with second=59. I also changed the error message for second (valid range is now 0..60). |
|||
msg237144 - (view) | Author: STINNER Victor (vstinner) * | Date: 2015-03-03 14:52 | |
Leap seconds are ignored, so a difference of <datetime before the leap second> and <datetime with the leap second> is zero: >>> import datetime >>> t1=datetime.datetime(2012, 6, 30, 23, 59, 59) >>> t2=datetime.datetime(2012, 6, 30, 23, 59, 59) >>> t2-t1 datetime.timedelta(0) Supporting leap seconds might be possible, but it requires much more work. |
|||
msg237146 - (view) | Author: STINNER Victor (vstinner) * | Date: 2015-03-03 15:37 | |
Ignoring leap seconds introduces unexpected result. datetime.timestamp -> datetime.fromtimestamp drops one second: $ ./python Python 3.5.0a1+ (default:760f222103c7+, Mar 3 2015, 15:36:36) >>> t=datetime.datetime(2012, 6, 30, 23, 59, 60).timestamp() >>> datetime.datetime.fromtimestamp(t) datetime.datetime(2012, 6, 30, 23, 59, 59) time and datetime modules behave differently: $ ./python Python 3.5.0a1+ (default:760f222103c7+, Mar 3 2015, 15:36:36) >>> import datetime, time >>> t1=datetime.datetime(2012, 6, 30, 23, 59, 59).timestamp() >>> t2=datetime.datetime(2012, 6, 30, 23, 59, 60).timestamp() >>> t2-t1 0.0 >>> t3=time.mktime((2012, 6, 30, 23, 59, 59, -1, -1, -1)) >>> t4=time.mktime((2012, 6, 30, 23, 59, 60, -1, -1, -1)) >>> t4-t3 1.0 >>> t1 == t2 == t3 True >>> t3, t4 (1341093599.0, 1341093600.0) |
|||
msg237147 - (view) | Author: STINNER Victor (vstinner) * | Date: 2015-03-03 15:45 | |
support_leap_seconds.patch: different approach, accept second=60. Problem: fromtimestamp() returns the wrong day. haypo@smithers$ ./python Python 3.5.0a1+ (default:760f222103c7+, Mar 3 2015, 15:36:36) >>> import datetime >>> datetime.datetime(2012, 6, 30, 23, 59, 60) datetime.datetime(2012, 6, 30, 23, 59, 60) >>> dt1=datetime.datetime(2012, 6, 30, 23, 59, 60) >>> t1=datetime.datetime(2012, 6, 30, 23, 59, 60).timestamp() >>> dt2=datetime.datetime.fromtimestamp(t1) >>> dt2 datetime.datetime(2012, 7, 1, 0, 0) >>> dt2 == dt1 False >>> dt1 datetime.datetime(2012, 6, 30, 23, 59, 60) >>> print(dt1) 2012-06-30 23:59:60 >>> print(dt2) 2012-07-01 00:00:00 >>> import time >>> time.mktime((2012, 6, 30, 23, 59, 60, -1, -1, -1)) 1341093600.0 >>> t1 1341093600.0 >>> t2=time.mktime((2012, 6, 30, 23, 59, 60, -1, -1, -1)) >>> t2 == t1 True >>> time.localtime(time.mktime((2012, 6, 30, 23, 59, 60, -1, -1, -1))) time.struct_time(tm_year=2012, tm_mon=7, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=183, tm_isdst=1) http://cr.yp.to/proto/utctai.html """ For many years, the UNIX localtime() time-display routine didn't support leap seconds. (...) Why not fix it? (...) The main obstacle is POSIX. POSIX is a ``standard'' designed by a vendor consortium several years ago to eliminate progress and protect the installed base. The behavior of the broken localtime() libraries was documented and turned into a POSIX requirement. """ |
|||
msg237148 - (view) | Author: STINNER Victor (vstinner) * | Date: 2015-03-03 15:48 | |
Oh, mktime() returns the same timestamp with and without the leap second: >>> time.mktime((2012, 6, 30, 23, 59, 59, -1, -1, -1)) 1341093599.0 >>> time.mktime((2012, 6, 30, 23, 59, 60, -1, -1, -1)) 1341093600.0 >>> time.mktime((2012, 7, 1, 0, 0, 0, -1, -1, -1)) 1341093600.0 |
|||
msg237413 - (view) | Author: Alexander Belopolsky (belopolsky) * | Date: 2015-03-07 03:39 | |
> POSIX is a ``standard'' designed by a vendor consortium several years ago to eliminate progress and protect the installed base. No, POSIX is an attempt to bring some sanity to the installed base of human calendars. The established standard tell's us that a year is 365 days. Wait, every 4-th year is 366 days, except some other rule every 400 years. POSIX says: fine as long as we can enumerate all YYYY-MM-DD's, we can live with it. But the line is drawn where each day is divided into 86,400 seconds. The problem is that unlike ancient astronomers who were finding better and better approximations to the ratio of two Earth's rotation periods (around the Sun and around itself) every few hundred years, modern astronomers will tell us how many seconds there will be in any given year with only a six month notice. |
|||
msg238004 - (view) | Author: Akira Li (akira) * | Date: 2015-03-13 03:13 | |
POSIX timestamp doesn't count (literally) past/future leap seconds. It allows to find out that the timestamp 2**31-1 corresponds to 2038-01-19T03:14:07Z (UTC) regardless of how many leap seconds will occur before 2038: >>> from datetime import datetime, timedelta >>> str(datetime(1970,1,1) + timedelta(seconds=2**31-1)) '2038-01-19 03:14:07' If you use "right" timezone then mktime() may count leap seconds: $ TZ=right/UTC ./python >>> import time >>> time.mktime((2012, 6, 30, 23, 59, 59, -1, -1, -1)) 1341100823.0 >>> time.mktime((2012, 6, 30, 23, 59, 60, -1, -1, -1)) 1341100824.0 >>> time.mktime((2012, 7, 1, 0, 0, 0, -1, -1, -1)) 1341100825.0 It is a different time scale. There are no leap seconds in TAI: >>> str(datetime(1970,1,1, 0,0, 10) + timedelta(seconds=1341100825)) '2012-07-01 00:00:35' i.e., 2012-07-01 00:00:35 TAI that corresponds to 2012-07-01 00:00:00 UTC. Each positive leap second increases the difference TAI-UTC (on 2015-07-01UTC it will be 36 [1]). TAI-UTC in the future (more than 6 months) is unknown but it is less than ~200 seconds until 2100 [2]. It might be convenient to think about datetime as a broken-down timestamp and therefore (datetime(2012,6,30,23,59,60) - epoch) == (datetime(2012,7, 1, 0, 0, 0) - epoch) The code [3] that silently truncates 60 to 59 when datetime constructor is called implicitly should retire. Use case: parse timestamps that might include a leap second [4] [1] https://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat [2] http://www.ucolick.org/~sla/leapsecs/year2100.html [3] https://bugs.python.org/msg155689 [4] http://stackoverflow.com/q/21027639 |
|||
msg244064 - (view) | Author: STINNER Victor (vstinner) * | Date: 2015-05-25 22:58 | |
Sorry, I give up on this issue. I don't know how to fix it, nor if it's possible to fix it. |
|||
msg244151 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2015-05-27 09:12 | |
Here's what mxDateTime uses: >>> import mx.DateTime >>> >>> t1 = mx.DateTime.DateTime(2012,6,30,23,59,60) >>> t2 = mx.DateTime.DateTime(2012,7,1,0,0,0) >>> >>> t1 <mx.DateTime.DateTime object for '2012-06-30 23:59:60.00' at 7fbb36008d68> >>> t2 <mx.DateTime.DateTime object for '2012-07-01 00:00:00.00' at 7fbb36008d20> >>> >>> t2-t1 <mx.DateTime.DateTimeDelta object for '00:00:00.00' at 7fbb35ff0540> >>> (t2-t1).seconds 0.0 >>> >>> t1 + mx.DateTime.oneSecond <mx.DateTime.DateTime object for '2012-07-01 00:00:01.00' at 7fbb360083d8> It preserves the broken down values, but uses POSIX days of 86400 seconds per day to calculate time deltas. It's a compromise, not a perfect solution, but it prevents applications from failing for that one second every now and then. I don't believe there is a perfect solution, since what your application or users expect may well be different. All I can say is that raising exceptions in these rare cases is not what your users typically want :-) |
|||
msg247059 - (view) | Author: (dlroo) | Date: 2015-07-21 20:15 | |
If you are using mx.DateTime make certain you do not use the .strftime method. If you use .strftime method and have a 60th second in your DateTime object it will crash python with no error message. This occurs because the .strftime method is fully inherited from Python's datetime.datetime. |
|||
msg247743 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2015-07-31 11:33 | |
On 21.07.2015 22:15, dlroo wrote: > > dlroo added the comment: > > If you are using mx.DateTime make certain you do not use the .strftime method. If you use .strftime method and have a 60th second in your DateTime object it will crash python with no error message. This occurs because the .strftime method is fully inherited from Python's datetime.datetime. Thanks for the report. We will fix this in the next mxDateTime release. |
|||
msg247760 - (view) | Author: (dlroo) | Date: 2015-07-31 18:11 | |
Is it possible to modify datetime so that the check_time_args function in the datetimemodule.c does not error when given a seconds value of greater than 59? I was thinking that if the seconds were greater than 59, the seconds are set to 59 and any extra seconds are kept in a book keeping "attribute" (not a real attribute because its C) that is accessible from the Python side? You would have to make the seconds argue passed by reference (thus returning a modified second). Also would want the book keeping value to be zero in nominal conditions. This would do nothing for any of the datetime arithmetic, but that can be handled externally. |
|||
msg247762 - (view) | Author: Alexander Belopolsky (belopolsky) * | Date: 2015-07-31 18:32 | |
Please redirect this discussion to the recently opened datetime-sig mailing list. https://mail.python.org/pipermail/datetime-sig/ |
|||
msg365224 - (view) | Author: Maximilian Nöthe (maxnoe) * | Date: 2020-03-28 18:42 | |
Could this be revisited? Especially now that datetime supports `fromisoformat`, as there are valid ISO8601 timestamps in UTC out there, that contain the leap seconds, e.g. files describing when those occured or will occur. E.g. the NTP Leap second file: https://kb.meinbergglobal.com/kb/time_sync/ntp/configuration/ntp_leap_second_file This get's synced on linux to `/usr/share/zoneinfo/leapseconds` and could even be used by python to lookup when leap seconds occured. The datetime also gained a fold argument, which if it is not wanted to support second values of 60 to at least be able to parse those. The 60th second of a minute is a reality with our current civil time keeping, so python should be able to handle it. |
|||
msg365285 - (view) | Author: STINNER Victor (vstinner) * | Date: 2020-03-29 23:25 | |
One option to explore is to add a "leap seconds" field to datetime.datetime which can be negative (just in case someone decides to add negative leap seconds in the future). It can use in operations which involve time zones, it can be serialized/deserialized, but datetime.datetime.timestamp() would ignore this field ("drop" leap seconds on purpose). |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:58:13 | admin | set | github: 67762 |
2020-03-29 23:25:48 | vstinner | set | status: closed -> open nosy: + p-ganssle messages: + msg365285 resolution: wont fix -> |
2020-03-28 18:42:57 | maxnoe | set | nosy:
+ maxnoe messages: + msg365224 |
2015-07-31 18:32:55 | belopolsky | set | messages: + msg247762 |
2015-07-31 18:11:50 | dlroo | set | messages: + msg247760 |
2015-07-31 11:33:12 | lemburg | set | messages: + msg247743 |
2015-07-21 20:15:09 | dlroo | set | nosy:
+ dlroo messages: + msg247059 versions: + Python 2.7, - Python 3.5 |
2015-05-27 09:12:20 | lemburg | set | nosy:
+ lemburg messages: + msg244151 |
2015-05-25 22:58:18 | vstinner | set | status: open -> closed resolution: wont fix messages: + msg244064 |
2015-03-13 03:13:40 | akira | set | nosy:
+ akira messages: + msg238004 |
2015-03-07 03:39:56 | belopolsky | set | messages: + msg237413 |
2015-03-03 15:48:15 | vstinner | set | messages: + msg237148 |
2015-03-03 15:45:19 | vstinner | set | files:
+ support_leap_seconds.patch messages: + msg237147 |
2015-03-03 15:37:21 | vstinner | set | messages: + msg237146 |
2015-03-03 15:02:18 | doughellmann | set | nosy:
+ doughellmann |
2015-03-03 14:52:00 | vstinner | set | messages: + msg237144 |
2015-03-03 14:43:29 | vstinner | set | files:
+ datetime_leapsecond.patch keywords: + patch |
2015-03-03 14:42:23 | vstinner | create |