[Python-ideas] use gmtime(0) epoch in functions that use mktime()

Akira Li 4kir4.1i at gmail.com
Sat Sep 6 15:53:55 CEST 2014


Python uses "seconds since the epoch" term to describe time.time()
return value. POSIX term is "seconds since the Epoch" (notice the
capitalization) where Epoch is 1970-01-01 00:00:00+00:00. C99 term is
"calendar time" -- the encoding of the calendar time returned by the
time() function is unspecified.

Python documentation defines `epoch` as:

  The :dfn:`epoch` is the point where the time starts.  On January 1st
  of that year, at 0 hours, the "time since the epoch" is zero.  For
  Unix, the epoch is 1970.  To find out what the epoch is, look at
  ``gmtime(0)``.

time module documentation specifies calendar.timegm() as the inverse of
time.gmtime() while timegm() uses the fixed 1970 Epoch instead of
gmtime(0) epoch.

datetime.astimezone() (local_timezone()) passes Unix timestamp [1970] to
time.localtime() that may expect timestamp with different epoch
[gmtime(0)].

email.util.mktime_tz() uses both mktime() [gmtime(0)] and timegm() [1970].

mailbox.py uses both time.time() [gmtime(0)] and timegm() [1970].

http.cookiejar uses both EPOCH_YEAR=1970 and datetime.utcfromtimestamp()
[gmtime(0) epoch] for "seconds since epoch".

It seems 1970 Epoch is used for file times on Windows (os.stat()) but
os.path.getatime() refers to "seconds since epoch" [gmtime(0) epoch].

date{,time}.{,utc}fromtimestamp(), datetime.timestamp() docs equates
"POSIX timestamp" [1970 Epoch] and time.time()'s returned value
[gmtime(0) epoch].

datetime.timestamp() is inconsistent if gmtime(0) is not 1970. It uses
mktime() for naive datetime objects [gmtime(0) epoch]. But it
uses POSIX Epoch for aware datetime objects.

Correct me if I'm wrong here.

---
Possible fixes:

Say in the `epoch` definition that stdlib doesn't support
gmtime(0).tm_year != 1970.

OR don't use mktime() if 1970 Epoch is used e.g., create an aware
datetime object in the local timezone instead and use it to compute the
result with 1970 Epoch.

OR add *analog* of TZ=UTC time.mktime() and use it in stdlib where
necessary. Looking at previous attempts (e.g., [1], [2]) to implement
timegm(), the problem seems over-constrained. A different name could be
used, to avoid wrong expectations e.g., datetime could use
`(aware_datetime_object - gmtime0_epoch) // sec`

[1] http://bugs.python.org/issue6280,
[2] http://bugs.python.org/issue1667546

OR set EPOCH_YEAR=gmtime(0).tm_year instead of 1970 in
calendar.timegm(). It may break backward compatibility if there is a
system with non-1970 epoch. Deal on a case-by-case basis with other
places where 1970 Epoch is used.  And drop "POSIX timestamp" [1970
Epoch] and use "seconds since the epoch" [gmtime(0) epoch] in the
datetime documentation. Change internal EPOCH year accordingly.

What is Python-ideas opinion about it?


--
Akira


More information about the Python-ideas mailing list