handle Y2038 in timestamp to datetime conversions

According to datetime.utcfromtimestamp() method documentation[1],
this and similar methods fail for dates past 2038 and can be replaced by
the following expression on the POSIX compliant systems:

  datetime(1970, 1, 1, tzinfo=timezone.utc) + timedelta(seconds=timestamp)

Make sure to use a method that at least allows to import the timestamps
properly to datetime objects on 32-bit platforms.

[1] https://docs.python.org/3/library/datetime.html#datetime.datetime.utcfromtimestamp

Fixes: https://pagure.io/freeipa/issue/8378

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
Alexander Bokovoy committed 2020-06-25 09:18:02 +03:00
1 parent a3c648bd92
commit 1f6ca418ee
6 files changed
+61 -21

No files matched your search

+6 -3
View File
@@ -22,6 +22,7 @@ import datetime
import email.utils
from calendar import timegm
from urllib.parse import urlparse
from ipapython.ipautil import datetime_from_utctimestamp
'''
@@ -184,7 +185,7 @@ class Cookie:
# use the RFC 1123 parsing function which uses only English
try:
dt = datetime.datetime(*email.utils.parsedate(s)[0:6])
dt = email.utils.parsedate_to_datetime(s)
except Exception as e:
raise ValueError("unable to parse expires datetime '%s': %s" % (s, e))
@@ -390,7 +391,7 @@ class Cookie:
elif isinstance(value, datetime.datetime):
self._timestamp = value
elif isinstance(value, (int, float)):
self._timestamp = datetime.datetime.utcfromtimestamp(value)
self._timestamp = datetime_from_utctimestamp(value, units=1)
elif isinstance(value, str):
self._timestamp = Cookie.parse_datetime(value)
else:
@@ -415,8 +416,10 @@ class Cookie:
self._expires = None
elif isinstance(value, datetime.datetime):
self._expires = value
if self._expires.tzinfo is None:
self._expires.replace(tzinfo=datetime.timezone.utc)
elif isinstance(value, (int, float)):
self._expires = datetime.datetime.utcfromtimestamp(value)
self._expires = datetime_from_utctimestamp(value, units=1)
elif isinstance(value, str):
self._expires = Cookie.parse_datetime(value)
else:
+24
View File
@@ -1647,3 +1647,27 @@ def rmtree(path):
shutil.rmtree(path)
except Exception as e:
logger.error('Error removing %s: %s', path, str(e))
def datetime_from_utctimestamp(t, units=1):
"""
Convert a timestamp or a time.struct_time to a datetime.datetime
object down to seconds, with UTC timezone
The conversion is safe for year 2038 problem
:param t: int or float timestamp in (milli)seconds since UNIX epoch
or time.struct_time
:param units: normalizing factor for the timestamp
(1 for seconds, 1000 for milliseconds)
defaults to 1
:return: datetime.datetime object in UTC timezone
"""
if isinstance(t, time.struct_time):
v = int(time.mktime(t))
elif isinstance(t, (float, int)):
v = int(t)
else:
raise TypeError(t)
epoch = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
return epoch + datetime.timedelta(seconds=v // units)