classification
Title: Functions in time module should support year < 1900 when accept2dyear = 0
Type: behavior Stage: resolved
Components: Extension Modules Versions: Python 3.2
process
Status: closed Resolution: fixed
Dependencies: Superseder: datetime.strftime dislikes years before 1900
View: 1777412
Assigned To: belopolsky Nosy List: SilentGhost, Trundle, belopolsky, georg.brandl, haypo
Priority: normal Keywords: patch

Created on 2011-01-04 16:48 by belopolsky, last changed 2011-01-08 16:38 by haypo. This issue is now closed.

Files
File name Uploaded Description Edit
issue10827.diff belopolsky, 2011-01-04 18:30
issue10827a.diff belopolsky, 2011-01-04 19:02
issue10827b.diff belopolsky, 2011-01-05 16:50
issue10827c.diff belopolsky, 2011-01-07 01:04
Messages (12)
msg125339 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-04 16:48
"""
> http://docs.python.org/library/time.html#time-y2kissues
> "Values 100–1899 are always illegal."

Why are these values illegal? The GNU libc accepts year in [1900-2^31; 2^31-1] (tm_year in [-2147483648; 2147481747]). If time.accept2dyear=False, we should at least accept years in [1; 9999]. The system libc would raise an error (return NULL) if it doesn't know how to format years older than 1900.
""" -- Victor Stinner at msg12516
msg125343 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-04 17:30
> The system libc would raise an error (return NULL) if it doesn't know
> how to format years older than 1900.

As experience with asctime has shown, system libc can do whatever it pleases with out of range values including overrunning a fixed size buffer, returning non-sensical values etc.  However, now that we have control over asctime implemetation (see issue 8013), I don't see any problem in supporting at least year > 999 in time.asctime and time.ctime.  (Supporting full [1900-maxint, maxint] range would involve a decision on whether to fill < 4-digit values.)  Some extra care would be required for time.strftime() because some systems may not support year < 1900 as well as others.

It looks like POSIX does not make any strong mandates:

"tm_year is a signed value; therefore, years before 1900 may be represented." 

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/time.h.html

and regardless of what POSIX or C standards have to say, this is the area where systems a known to have spotty compliance records.
msg125345 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-04 18:30
I am attaching a patch.  While working on the patch, I noticed that although time.accept2dyear is documented as boolean, the current code expects int and treats any non-int including True as 0:

>>> time.accept2dyear = True; time.asctime((99,) + (0,)*8)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: year >= 1900 required

>>> time.accept2dyear = 1; time.asctime((99,) + (0,)*8)
'Mon Jan  1 00:00:00 1999'

This is clearly a bug.  (Although Y2K note contradicts time.accept2dyear documentation.)

Supporting year < 1900 would be a feature in my view, but I agree with SilentGhost that once we extended support to 5+ digit years, it is odd to keep year >= 1900 restriction.
msg125349 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-04 18:38
On Tue, Jan 4, 2011 at 1:30 PM, Alexander Belopolsky
<report@bugs.python.org> wrote:
..
> This is clearly a bug.  (Although Y2K note contradicts time.accept2dyear documentation.)
>

PyObject_IsTrue() may fail - this is probably the reason for the
current odd logic.  My patch should be fixed top respect that, but I
still maintain that time.accept2dyear = True should work as expected.
(Would also make True the default rather 1.)
msg125353 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2011-01-04 18:49
But if it fails, why not just let it fail?
msg125356 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-04 19:02
On Tue, Jan 4, 2011 at 1:49 PM, Georg Brandl <report@bugs.python.org> wrote:
..
> But if it fails, why not just let it fail?
>

Sure.  That's what I meant by fixing the patch.  See new patch attached.
msg125432 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-05 16:50
Attached patch, issue10827b.diff, fixes the accept2dyear = True issue and removes unnecessary struct_time to tuple conversion, but otherwise does not change the Y2K behavior.  The code handling accept2dyear is refactored so that it is now easy to accept y < 1900 in accept2dyear = False mode.  The patch also includes unit tests.
msg125609 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-07 00:57
Attached patch, issue10827c.diff, implements the following logic in gettmarg:


    /* If year is specified with less that 4 digits, its interpretation                                                                                                                                       
     * depends on the accept2dyear value.                                                                                                                                                                     
     *                                                                                                                                                                                                        
     * If accept2dyear is true (default), a backward compatibility behavior is                                                                                                                                
     * invoked as follows:                                                                                                                                                                                    
     *                                                                                                                                                                                                        
     *   - for 2-digit year, century is guessed according to POSIX rules for                                                                                                                                  
     *      %y strptime format: 21st century for y < 69, 20th century                                                                                                                                         
     *      otherwise.  A deprecation warning is issued when century                                                                                                                                          
     *      information is guessed in this way.                                                                                                                                                               
     *                                                                                                                                                                                                        
     *   - for 3-digit or negative year, a ValueError exception is raised.                                                                                                                                    
     *                                                                                                                                                                                                        
     * If accept2dyear is false (set by the program or as a result of a                                                                                                                                       
     * non-empty value assigned to PYTHONY2K environment variable) all year                                                                                                                                   
     * values are interpreted as given.                                                                                                                                                                       
     */

It is easy to restore year >= 1900 limit for strftime, but I would rather add tests that time.strftime either produces correct values or raises ValueError and see if buildbots discover any platform bugs.
msg125707 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-07 20:39
Committed in 87829.

I added a year >= 1900 check in time.strftime for now because removing or relaxing this limit should be done in coordination with similar datetime module issue.  See #1777412.

See also python-dev discussion starting at

http://mail.python.org/pipermail/python-dev/2011-January/107186.html
msg125708 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-01-07 20:41
Commit link: r87829
msg125714 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2011-01-07 21:41
I tried time.asctime() on Windows 32 bits (compiled with Visual Studio) with accept2dyear=False: it accepts years in [-2^31; 2^31-1], cool.
msg125791 - (view) Author: STINNER Victor (haypo) * (Python committer) Date: 2011-01-08 16:38
time.asctime(), time.ctime() and time.strftime() are no more not limited for the year field if accept2dyear=0. Except with Visual Studio or on Solaris: the year is limited to the range [1; 9999].
History
Date User Action Args
2011-01-08 16:38:02hayposetnosy: georg.brandl, belopolsky, haypo, Trundle, SilentGhost
messages: + msg125791
2011-01-07 22:59:33belopolskylinkissue1777412 dependencies
2011-01-07 21:41:52hayposetnosy: georg.brandl, belopolsky, haypo, Trundle, SilentGhost
messages: + msg125714
2011-01-07 20:41:52belopolskysetstatus: open -> closed
nosy: georg.brandl, belopolsky, haypo, Trundle, SilentGhost
messages: + msg125708
2011-01-07 20:39:46belopolskysetversions: + Python 3.2
nosy: georg.brandl, belopolsky, haypo, Trundle, SilentGhost
messages: + msg125707
resolution: fixed

superseder: datetime.strftime dislikes years before 1900
stage: commit review -> resolved
2011-01-07 01:04:57belopolskysetfiles: + issue10827c.diff
nosy: georg.brandl, belopolsky, haypo, Trundle, SilentGhost
2011-01-07 00:57:15belopolskysetnosy: georg.brandl, belopolsky, haypo, Trundle, SilentGhost
messages: + msg125609
2011-01-05 16:50:42belopolskysetfiles: + issue10827b.diff

messages: + msg125432
nosy: georg.brandl, belopolsky, haypo, Trundle, SilentGhost
stage: commit review
2011-01-04 19:02:30belopolskysetfiles: + issue10827a.diff

messages: + msg125356
nosy: georg.brandl, belopolsky, haypo, Trundle, SilentGhost
2011-01-04 18:49:51georg.brandlsetnosy: + georg.brandl
messages: + msg125353
2011-01-04 18:38:55belopolskysetnosy: belopolsky, haypo, Trundle, SilentGhost
messages: + msg125349
2011-01-04 18:30:45belopolskysetfiles: + issue10827.diff

messages: + msg125345
keywords: + patch
nosy: belopolsky, haypo, Trundle, SilentGhost
2011-01-04 17:32:00Trundlesetnosy: + Trundle
2011-01-04 17:30:03belopolskysetnosy: belopolsky, haypo, SilentGhost
messages: + msg125343
2011-01-04 16:48:19belopolskycreate