pythonpython-datetimepytzmktimepython-docker

mktime resulting in OverflowError: mktime argument out of range


When attempting to get a time using mktime, I get a overflow exception. This occurs on a Debian docker image, but not on my host machine:

docker pull python:3.9.7

https://hub.docker.com/layers/library/python/3.9.7/images/sha256-9d7d2a6ce11fe5a12c74f81b860f5142fd91192427ece0b566c4fb962353325e?context=explore

pytz==2021.3

Here's a toy example:

import datetime
import time
import pytz

ts = 1655043357
tz = 'Canada/Eastern'

utc_time = datetime.datetime.now(tz=pytz.utc)

tz = pytz.timezone(tz)
tz_aware = datetime.datetime.now(tz=tz)

utc_ts = time.mktime(utc_time.timetuple()) # No exception

ts = time.mktime(tz_aware.timetuple()) # <----Results in exception

Exception:

Traceback (most recent call last):
  File "/home/toy_example.py", line 15, in <module>
    time.mktime(tz_aware.timetuple())
OverflowError: mktime argument out of range

There is no exception when running on my host machine and works as expected. Any idea why that's happening?

edit: Interestingly, if I change my current system time to last week March 7, 2024 it works without an exception being raised


Solution

  • Found out the reason why changing the date fixed the exception. Those of us in North America have a daylight savings time that went into effect on March 10th 2024. This is why when setting a system time before March 10th, everything works. After daylight savings time went into effect, the exception starting occurring.

    As part of timetuple(), there is a is_dst field that gets added ONLY when daylight savings time is in effect. When you pass this into mktime in older linux platforms (such as the docker image in the question), it will trigger an (very misleading) overflow exception. On newer platforms, it accepts is_dst (works on newer debian and osx).

    The correct way is to avoid mktime altogether:

    # current date and time
    tz = 'Canada/Eastern'
    tz = pytz.timezone(tz)
    
    tz_aware = datetime.now(tz=tz)
    ts = datetime.timestamp(tz_aware)
    
    utc_time = datetime.datetime.now(tz=pytz.utc)
    utc_ts = datetime.timestamp(utc_time)