问题
I have a python datetime instance that was created using datetime.utcnow() and persisted in database.
For display, I would like to convert the datetime instance retrieved from the database to local datetime using the default local timezone (i.e., as if the datetime was created using datetime.now()).
How can I convert the UTC datetime to a local datetime using only python standard library (e.g., no pytz dependency)?
It seems one solution would be to use datetime.astimezone( tz ), but how would you get the default local timezone?
回答1:
In Python 3.3+:
from datetime import datetime, timezone
def utc_to_local(utc_dt):
return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
In Python 2/3:
import calendar
from datetime import datetime, timedelta
def utc_to_local(utc_dt):
# get integer timestamp to avoid precision lost
timestamp = calendar.timegm(utc_dt.timetuple())
local_dt = datetime.fromtimestamp(timestamp)
assert utc_dt.resolution >= timedelta(microseconds=1)
return local_dt.replace(microsecond=utc_dt.microsecond)
Using pytz
(both Python 2/3):
import pytz
local_tz = pytz.timezone('Europe/Moscow') # use your local timezone name here
# NOTE: pytz.reference.LocalTimezone() would produce wrong result here
## You could use `tzlocal` module to get local timezone on Unix and Win32
# from tzlocal import get_localzone # $ pip install tzlocal
# # get local timezone
# local_tz = get_localzone()
def utc_to_local(utc_dt):
local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
return local_tz.normalize(local_dt) # .normalize might be unnecessary
Example
def aslocaltimestr(utc_dt):
return utc_to_local(utc_dt).strftime('%Y-%m-%d %H:%M:%S.%f %Z%z')
print(aslocaltimestr(datetime(2010, 6, 6, 17, 29, 7, 730000)))
print(aslocaltimestr(datetime(2010, 12, 6, 17, 29, 7, 730000)))
print(aslocaltimestr(datetime.utcnow()))
Output
Python 3.32010-06-06 21:29:07.730000 MSD+0400
2010-12-06 20:29:07.730000 MSK+0300
2012-11-08 14:19:50.093745 MSK+0400
Python 2
2010-06-06 21:29:07.730000
2010-12-06 20:29:07.730000
2012-11-08 14:19:50.093911
pytz
2010-06-06 21:29:07.730000 MSD+0400
2010-12-06 20:29:07.730000 MSK+0300
2012-11-08 14:19:50.146917 MSK+0400
Note: it takes into account DST and the recent change of utc offset for MSK timezone.
I don't know whether non-pytz solutions work on Windows.
回答2:
You can't do it with only the standard library as the standard library doesn't have any timezones. You need pytz or dateutil.
>>> from datetime import datetime
>>> now = datetime.utcnow()
>>> from dateutil import tz
>>> HERE = tz.tzlocal()
>>> UTC = tz.gettz('UTC')
The Conversion:
>>> gmt = now.replace(tzinfo=UTC)
>>> gmt.astimezone(HERE)
datetime.datetime(2010, 12, 30, 15, 51, 22, 114668, tzinfo=tzlocal())
Or well, you can do it without pytz or dateutil by implementing your own timezones. But that would be silly.
回答3:
I think I figured it out: computes number of seconds since epoch, then converts to a local timzeone using time.localtime, and then converts the time struct back into a datetime...
EPOCH_DATETIME = datetime.datetime(1970,1,1)
SECONDS_PER_DAY = 24*60*60
def utc_to_local_datetime( utc_datetime ):
delta = utc_datetime - EPOCH_DATETIME
utc_epoch = SECONDS_PER_DAY * delta.days + delta.seconds
time_struct = time.localtime( utc_epoch )
dt_args = time_struct[:6] + (delta.microseconds,)
return datetime.datetime( *dt_args )
It applies the summer/winter DST correctly:
>>> utc_to_local_datetime( datetime.datetime(2010, 6, 6, 17, 29, 7, 730000) )
datetime.datetime(2010, 6, 6, 19, 29, 7, 730000)
>>> utc_to_local_datetime( datetime.datetime(2010, 12, 6, 17, 29, 7, 730000) )
datetime.datetime(2010, 12, 6, 18, 29, 7, 730000)
回答4:
You can't do it with standard library. Using pytz module you can convert any naive/aware datetime object to any other time zone. Lets see some examples using Python 3.
Naive objects created through class method
utcnow()
To convert a naive object to any other time zone, first you have to convert it into aware datetime object. You can use the replace
method for converting a naive datetime object to an aware datetime object. Then to convert an aware datetime object to any other timezone you can use astimezone
method.
The variable pytz.all_timezones
gives you the list of all available time zones in pytz module.
import datetime,pytz
dtobj1=datetime.datetime.utcnow() #utcnow class method
print(dtobj1)
dtobj3=dtobj1.replace(tzinfo=pytz.UTC) #replace method
dtobj_hongkong=dtobj3.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
print(dtobj_hongkong)
Naive objects created through class method
now()
Because now
method returns current date and time, so you have to make the datetime object timezone aware first. The localize
function converts a naive datetime object into a timezone-aware datetime object. Then you can use the astimezone
method to convert it into another timezone.
dtobj2=datetime.datetime.now()
mytimezone=pytz.timezone("Europe/Vienna") #my current timezone
dtobj4=mytimezone.localize(dtobj2) #localize function
dtobj_hongkong=dtobj4.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
print(dtobj_hongkong)
回答5:
The standard Python library does not come with any tzinfo
implementations at all. I've always considered this a surprising shortcoming of the datetime module.
The documentation for the tzinfo class does come with some useful examples. Look for the large code block at the end of the section.
回答6:
Building on Alexei's comment. This should work for DST too.
import time
import datetime
def utc_to_local(dt):
if time.localtime().tm_isdst:
return dt - datetime.timedelta(seconds = time.altzone)
else:
return dt - datetime.timedelta(seconds = time.timezone)
回答7:
A simple (but maybe flawed) way that works in Python 2 and 3:
import time
import datetime
def utc_to_local(dt):
return dt - datetime.timedelta(seconds = time.timezone)
Its advantage is that it's trivial to write an inverse function
回答8:
The easiest way I have found is to get the time offset of where you are, then subtract that from the hour.
def format_time(ts,offset):
if not ts.hour >= offset:
ts = ts.replace(day=ts.day-1)
ts = ts.replace(hour=ts.hour-offset)
else:
ts = ts.replace(hour=ts.hour-offset)
return ts
This works for me, in Python 3.5.2.
回答9:
Here is another way to change timezone in datetime format (I know I wasted my energy on this but I didn't see this page so I don't know how) without min. and sec. cause I don't need it for my project:
def change_time_zone(year, month, day, hour):
hour = hour + 7 #<-- difference
if hour >= 24:
difference = hour - 24
hour = difference
day += 1
long_months = [1, 3, 5, 7, 8, 10, 12]
short_months = [4, 6, 9, 11]
if month in short_months:
if day >= 30:
day = 1
month += 1
if month > 12:
year += 1
elif month in long_months:
if day >= 31:
day = 1
month += 1
if month > 12:
year += 1
elif month == 2:
if not year%4==0:
if day >= 29:
day = 1
month += 1
if month > 12:
year += 1
else:
if day >= 28:
day = 1
month += 1
if month > 12:
year += 1
return datetime(int(year), int(month), int(day), int(hour), 00)
回答10:
This is a terrible way to do it but it avoids creating a definition. It fulfills the requirement to stick with the basic Python3 library.
# Adjust from UST to Eastern Standard Time (dynamic)
# df.my_localtime should already be in datetime format, so just in case
df['my_localtime'] = pd.to_datetime.df['my_localtime']
df['my_localtime'] = df['my_localtime'].dt.tz_localize('UTC').dt.tz_convert('America/New_York').astype(str)
df['my_localtime'] = pd.to_datetime(df.my_localtime.str[:-6])
回答11:
Convert and revert works with timestamp in Python3 , my datetime objects is timezone aware only import datetime, may be hazzle free rewritten for Python2
Test result

import datetime
def mz(dt,tzsecs):
return dt.replace(tzinfo=datetime.timezone(datetime.timedelta(seconds=tzsecs)))
def local2utc(local):
return mz(datetime.datetime.utcfromtimestamp(local.timestamp()),0)
def utc2local(utc):
utc_ts=utc.timestamp()
local_ts=mz(datetime.datetime.fromtimestamp(utc_ts),0).timestamp()
return mz(datetime.datetime.fromtimestamp(utc_ts),local_ts-utc_ts)
I made an example clearly demonstrating this and how to use my functions!
fmt="%c %Z"
u=mz(datetime.datetime.utcnow(),0)
dm=u.month-1
dy=u.year
for m in range(12):
u=u.replace(year=dy+int((dm+m)/12),month=1+(dm+m)%12)
dt=u
l=utc2local(u)
print("Convert",u.strftime(fmt)," == ",l.strftime(fmt))
u=local2utc(l)
print("Reverse",l.strftime(fmt)," == ",u.strftime(fmt),dt==u)
print("Noted DST is active in summer (Rome, Stockholm UTC+2)!")
来源:https://stackoverflow.com/questions/4563272/convert-a-python-utc-datetime-to-a-local-datetime-using-only-python-standard-lib