classification
Title: Seconds range in time unit
Type: behavior Stage: resolved
Components: Documentation Versions: Python 3.0, Python 3.1, Python 3.2, Python 3.3, Python 2.7, Python 2.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: belopolsky Nosy List: belopolsky, datacompboy, georg.brandl, haypo, mark.dickinson, r.david.murray
Priority: low Keywords: easy, patch

Created on 2008-04-07 06:53 by datacompboy, last changed 2011-01-10 22:57 by belopolsky. This issue is now closed.

Files
File name Uploaded Description Edit
issue2568-doc.patch r.david.murray, 2009-03-29 02:55 doc patch for datetime strftime footnotes
Messages (13)
msg65067 - (view) Author: Anton Fedorov (datacompboy) Date: 2008-04-07 06:53
"%S	Second as a decimal number [00,61].	(2)
(2) The range really is 0 to 61; this accounts for leap seconds and the 
(very rare) double leap seconds."

That is wrong. There NEVER can be two leap seconds in one moment, since UTC 
time keep the difference between UTC and UT1 from exceeding ±0.9 s.

Leap seconds occur only at the end of a UTC month, and have only ever been 
inserted at the end of June 30 or December 31.

So, 61 is wrong, real seconds range from 0 to 60 inclusive.
msg65099 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2008-04-07 19:27
OP does not have the reference, but the issue is apparently in the time
and datetime modules documentation:

http://docs.python.org/dev/library/time.html#time.strftime
http://docs.python.org/dev/library/datetime.html#strftime-behavior

Note that datetime's doc is twice incorrect because leap seconds are not
supported by the datetime module at all:

>>> from datetime import *
>>> time(23,59,60)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: second must be in 0..59
>>> datetime.strptime('20000101T235960', '%Y%m%dT%H%M%S')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: second must be in 0..59
msg65101 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2008-04-07 19:34
On the other hand, the time module allows full [00,61] range:

>>> [time.strftime('%S',time.strptime(x, '%S')) for x in ('00', '61')] 
['00', '61']

so this is implementation rather than documentation issue.
msg65104 - (view) Author: Anton Fedorov (datacompboy) Date: 2008-04-07 19:42
There no leap second in 2000th.
But correct time request fails:
>>> datetime.strptime('19951231T235960', '%Y%m%dT%H%M%S')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: second must be in 0..59
msg65112 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2008-04-07 20:04
On Mon, Apr 7, 2008 at 3:42 PM, Anton Fedorov <report@bugs.python.org> wrote:

>  There no leap second in 2000th.

I was just too lazy too look up the leap second year, but datetime
module knows nothing about leap seconds, so I did not expect different
behavior for years 1995 and 2000.

>  But correct time request fails:
>  >>> datetime.strptime('19951231T235960', '%Y%m%dT%H%M%S')

ditto
msg65256 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2008-04-09 18:49
Isn't the bug here rather that strptime doesn't allow leap seconds?
msg65262 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2008-04-09 19:54
On Wed, Apr 9, 2008 at 2:49 PM, Georg Brandl <report@bugs.python.org> wrote:

>  Isn't the bug here rather that strptime doesn't allow leap seconds?

This is not specific to strptime.  The datetime module does not allow
leap seconds:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: second must be in 0..59

This is intentional and documented. Or maybe I don't understand your question.
msg65272 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2008-04-09 22:11
Okay, sorry that I made uninformed guesses :)
msg84362 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2009-03-29 02:55
The 'double leap second' issue has been around a long time and is part
of the Posix standard (for some background see
http://www.ucolick.org/~sla/leapsecs/onlinebib.html, specifically the
section named 'Unix system time and the POSIX standard').  This document
notes that the double leap second was still in the standard in 1997, and
according to Wikipedia that is after the last revision of the standard,
so presumably it is still there.

Therefore the time documentation of strftime is correct, since my
understanding is that time is a wrapper around the Posix time functions.
 The datetime docs, on the other hand, are misleading in the strftime
documentation.  I've attached a doc patch to have the note clarify that
datetime does not consume or produce leap seconds.
msg85161 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2009-04-02 04:44
Doc patch applied in r71037.
msg106956 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2010-06-03 14:46
I am reopening this issue because the following note is still not entirely correct:

"""
The range really is 0 to 61; according to the Posix standard this accounts for leap seconds and the (very rare) double leap seconds. The time module may produce and does accept leap seconds since it is based on the Posix standard ...
""" - http://docs.python.org/library/datetime.html#strftime-and-strptime-behavior


First, the latest POSIX standard (IEEE Std 1003.1, 2004 Edition) defines seconds range as [0, 60]. 

http://www.opengroup.org/onlinepubs/009695399/basedefs/time.h.html

Second, AFAIK, the only way to produce tm structure with tm_sec = 60 using a POSIX function is to explicitly pass "60" to a "%S" field in strptime. Other functions that fill tm structure, such as localtime or gmtime will never produce tm_sec = 60.  This is important because in the current form the comment suggests that the common recipe of passing first six elements of time.struct_time() to datetime constructor is unsafe while it is only unsafe if the time information comes from a non-POSIX system.

Third, POSIX is not a relevant standard for Python.  I have not seen any statement of python compliance to any version of Posix.  The relevant standard, I believe is C89:

"""
 Python now must be compiled with C89 compilers (after 19 years!).
""" - http://docs.python.org/whatsnew/2.6.html

While strictly speaking this is not a compliance statement, at least it has Python and C89 in the same sentence. :-)

AFAIK, the C89 standard does allow double leap seconds.  It may also allow compliant systems to produce leap second times from localtime() or gmtime(), but I don't have access to the text at the moment.  If this is true, the only fix required is s/POSIX/C89/g.
msg125913 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-10 19:04
The C89 draft as available through a Wikipedia link, http://flash-gordon.me.uk/ansi.c.txt, specifies tm_sec range as [0, 60].  Although it looks like the range has been extended in the published version.  A discussion on comp.std.c describes the situation as follows:

"""

The "double leap second" is complete nonsense. It never existed outside the 
ANSI C standard and never will. It was introduced by the ANSI C 89 committee 
to document its problems understanding UTC issues. Someone heard that there 
are two prefered dates for leap seconds per year and this got munched up to 
the false rumour that UTC days can be up to 86402 seconds long. The 
definition of UTC, which requires that |UTC-UT1| < 0.9 s all the time, 
obviously makes double leap seconds mathematically impossible. 
"""

The latest publicly available standard that I was able to find that specifies [0, 61] range was "The Single UNIX ® Specification, Version 2" from 1997: http://pubs.opengroup.org/onlinepubs/007908799/xsh/time.h.html

Note that this range has been recognized as a mistake by Open Group:

"""
Problem:

 The valid range of seconds is no longer 0-61, a range chosen
 to accomodate the mythical double leap second.

 The correct range is 0-60 seconds.

 This has been fixed elsewhere in 1003.1-200x already.  See for
 instance <time.h>.

 Action:

 Change range to 00-60 seconds.

 Search for any other places where the range is wrongly given as 0-61
 and fix them too.
 [Ed recommendation: Accept as marked
 make the change , and add a note to the CH
 that
 The description of %S is updated so the valid range is 00-60 rather
 than 00-61.

 A search was done of the draft for other occurrences of 61 and none
 found.  ]
"""  http://www.opengroup.org/austin/docs/austin_77r1.txt

Compliance with the mistakes in old versions of various standards, does not seem like a valid goal for Python, but until a system is reported that misbehaves when tm_sec = 61 is passed to strftime, I don't see a compelling reason to change Python behavior.  On the other hand, the documentation should not refer to bogus standards, so I suggest to change 

"""
The range really is 0 to 61; according to the Posix standard this accounts for leap seconds and the (very rare) double leap seconds. The time module may produce and does accept leap seconds since it is based on the Posix standard ...
"""

to 

"""
The range really is 0 to 61; tm_sec = 60 may be necessary to represent an occasional leap second and tm_sec = 61 is supported for historical reasons.
"""
msg125949 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-10 22:57
Committed in revision 87910.
History
Date User Action Args
2011-01-10 22:57:19belopolskysetstatus: open -> closed
nosy: georg.brandl, mark.dickinson, belopolsky, haypo, datacompboy, r.david.murray
messages: + msg125949

resolution: fixed
stage: needs patch -> resolved
2011-01-10 19:04:40belopolskysetnosy: + haypo
messages: + msg125913
2010-06-03 14:46:49belopolskysetstatus: closed -> open

assignee: belopolsky
versions: + Python 3.2, Python 3.3
nosy: + mark.dickinson

messages: + msg106956
resolution: fixed -> (no value)
stage: patch review -> needs patch
2009-04-02 04:44:57r.david.murraysetstatus: open -> closed
resolution: fixed
messages: + msg85161
2009-03-29 02:55:54r.david.murraysetfiles: + issue2568-doc.patch
priority: low

versions: + Python 2.6, Python 3.0, Python 3.1, Python 2.7
keywords: + patch, easy
nosy: + r.david.murray

messages: + msg84362
stage: patch review
2008-04-09 22:11:35georg.brandlsetassignee: georg.brandl -> (no value)
2008-04-09 22:11:30georg.brandlsetmessages: + msg65272
2008-04-09 19:54:33belopolskysetmessages: + msg65262
2008-04-09 18:49:22georg.brandlsetmessages: + msg65256
2008-04-07 20:04:35belopolskysetmessages: + msg65112
2008-04-07 19:42:26datacompboysetmessages: + msg65104
2008-04-07 19:34:50belopolskysetmessages: + msg65101
2008-04-07 19:27:20belopolskysetassignee: georg.brandl
messages: + msg65099
components: + Documentation, - Library (Lib)
nosy: + georg.brandl, belopolsky
2008-04-07 06:53:12datacompboycreate