I have a python 2.7 codebase that I'm trying to containerize. Much as I'd like to, our devs cannot move to Python 3.
When running natively in their dev environments, datetimes respect timezones. I can confirm that the output is as expected on a Mac running Python 3.9.6
. But when we containerize this on Ubuntu base images, this no longer works correctly.
Using python:2.7.18-buster
works correctly, but that is a 3 year old image that doesn't get updates. Both ubuntu:18.04
and ubuntu:16.04
fail.
The incorrect output when run at this time is
UTC Hour: 22
NY Hour: 22
1609459200.0
London Hour: 22
1609459200.0
The code to repro the issue is
import os
import datetime
import time
from dateutil.parser import parse
date_string="2021-01-01"
os.environ.get("TZ") # should be none, use system TZ, which is UTC
print ("UTC Hour: ", datetime.datetime.now().hour) # should be whatever hour it is in UTC
os.environ["TZ"] = "America/New_York"
print ("NY Hour:", datetime.datetime.now().hour) # should be whatever hour it is in EST
print (time.mktime(parse(date_string).timetuple())) # should be 1609477200.0
os.environ["TZ"] = "Europe/London"
print ("London Hour: ", datetime.datetime.now().hour) # should be whatever hour it is in GMT
print (time.mktime(parse(date_string).timetuple())) # should be 1609459200.0
CodePudding user response:
It turns out that python:2.7.18-buster
includes tzdata under /usr/share/zoneinfo
. Neither ubuntu:16.04
nor ubuntu:18.04
does this, so the function posted fails.
Simply installing tzdata
with apt resolves the issue.
CodePudding user response:
You can avoid changing environment variables by using aware datetime consistently. To calculate Unix time, derive it from a timedelta.
from datetime import datetime
from dateutil import tz
def to_unix(dt, _epoch=datetime(1970, 1, 1, tzinfo=tz.UTC)):
"""convert aware datetime object to seconds since the unix epoch"""
return (dt-_epoch).total_seconds()
# reference date for demonstration
refdate = datetime(2021, 1, 1)
zone = tz.gettz("America/New_York")
dt_zone = refdate.replace(tzinfo=zone) # 2021-01-01 00:00:00-05:00
print(to_unix(dt_zone)) # 1609477200.0
print("UTC hour:", datetime.now(tz.UTC).hour, "NY hour:", datetime.now(zone).hour)
# -->> -5 hour offset on 11 Nov 2022
zone = tz.gettz("Europe/London")
dt_zone = refdate.replace(tzinfo=zone) # 2021-01-01 00:00:00 00:00
print(to_unix(dt_zone)) # 1609459200.0
print("UTC hour:", datetime.now(tz.UTC).hour, "London hour:", datetime.now(zone).hour)
# -->> 0 hour offset on 11 Nov 2022
Note: tested with Python 2.7.18 on Linux, but not in a Docker environment.