我有一个使用datetime.utcnow()创建并保存在数据库中的python datetime实例。
为了显示,我想使用默认的本地时区将从数据库检索到的日期时间实例转换为本地日期时间(即,就像使用datetime.now()创建日期时间一样。)
如何仅使用python标准库(例如,没有pytz依赖项)将UTC日期时间转换为本地日期时间?
似乎一种解决方案是使用datetime.astimezone(tz),但是你如何获得默认的本地时区?
答案 0 :(得分:187)
在Python 3.3 +中:
from datetime import datetime, timezone
def utc_to_local(utc_dt):
return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
在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)
使用pytz
(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
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()))
2010-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
注意:它考虑了DST和MSK时区最近的utc偏移变化。
我不知道非pytz解决方案是否适用于Windows。
答案 1 :(得分:38)
由于标准库没有任何时区,因此您无法仅使用标准库。您需要pytz或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())
或者,你可以通过实现自己的时区来实现没有pytz或dateutil。但这很愚蠢。
答案 2 :(得分:8)
Python 3.9添加了zoneinfo
模块,因此现在可以按以下方式完成操作(仅限stdlib):
from zoneinfo import ZoneInfo
from datetime import datetime
utc_unaware = datetime(2020, 10, 31, 12) # loaded from database
utc_aware = utc_unaware.replace(tzinfo=ZoneInfo('UTC')) # make aware
local_aware = utc_aware.astimezone(ZoneInfo('localtime')) # convert
中欧比世界标准时间早1或2个小时,因此local_aware
为:
datetime.datetime(2020, 10, 31, 13, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='localtime'))
为str
:
2020-10-31 13:00:00+01:00
Windows has no系统时区数据库,因此这里需要一个额外的程序包:
pip install tzdata
有一个允许在 Python 3.6至3.8 中使用的backport:
sudo pip install backports.zoneinfo
然后:
from backports.zoneinfo import ZoneInfo
答案 3 :(得分:7)
我想我想出来了:计算自纪元以来的秒数,然后使用time.localtime转换为本地timzeone,然后将时间结构转换回日期时间......
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 )
正确应用夏季/冬季DST:
>>> 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 :(得分:6)
您无法使用标准库执行此操作。使用 pytz 模块,您可以将任何天真/感知日期时间对象转换为任何其他时区。让我们看一些使用Python 3的例子。
通过类方法
创建的朴素对象utcnow()
要将天真对象转换为任何其他时区,首先必须将其转换为识别日期时间对象。您可以使用replace
方法将天真日期时间对象转换为识别日期时间对象。然后,要将感知日期时间对象转换为任何其他时区,您可以使用astimezone
方法。
变量pytz.all_timezones
为您提供pytz模块中所有可用时区的列表。
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)
通过类方法
创建的朴素对象now()
由于now
方法返回当前日期和时间,因此您必须首先了解datetime对象时区。 localize
函数将天真日期时间对象转换为时区感知日期时间对象。然后,您可以使用astimezone
方法将其转换为另一个时区。
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 :(得分:4)
标准Python库根本没有任何tzinfo
实现。我一直认为这是日期时间模块的一个令人惊讶的缺点。
documentation for the tzinfo class确实附带了一些有用的示例。在该部分的末尾查找大代码块。
答案 6 :(得分:4)
以阿列克谢的评论为基础。这也适用于夏令时。
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 :(得分:0)
一种简单(但可能有缺陷)的方法适用于Python 2和3:
import time
import datetime
def utc_to_local(dt):
return dt - datetime.timedelta(seconds = time.timezone)
它的优点是编写反函数
是微不足道的答案 8 :(得分:0)
我找到的最简单的方法是获取你的时间偏移量,然后从小时中减去。
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
这适用于我,在Python 3.5.2中。
答案 9 :(得分:0)
这是更改日期时间格式的时区的另一种方法(我知道我在此上浪费了精力,但是我没有看到此页面,所以我不知道如何)而没有分钟。和秒。因为我的项目不需要它:
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 :(得分:0)
这是一种糟糕的方法,但是避免了创建定义。它符合坚持使用基本Python3库的要求。
# 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 :(得分:0)
在Python3中替换时区时,转换和恢复与时间戳一起使用,我的datetime对象仅支持从标准日期时间导入的时区,可能为Python2重写了麻烦!
测试结果。
class Profile extends Component {
state = {
isCommentOpen: false
};
render(){
return(
<div>
//pass the state to child
<Preview isCommentOpen={this.state.isCommentOpen} />
{this.state.isCommentOpen ? <span>Cool</span> : null}
</div>
)
}
}
export default Profile
class Preview extends Component {
const handleComment = () => {
this.props.isCommentOpen = !this.props.isCommentOpen}
};
render() {
return(
<button type="button" onClick={this.handleComment}>Comment</button>
}
}
export default Preview;
答案 12 :(得分:0)
使用timedelta在时区之间切换。您所需要的只是时区之间的小时数偏移。不必摆弄日期时间对象的所有6个元素的边界。 timedelta也可以轻松处理leap年,leap历世纪等。您必须先
from datetime import datetime, timedelta
然后如果offset
是时区增量(以小时为单位):
timeout = timein + timedelta(hours = offset)
其中timein和timeout是日期时间对象。例如
timein + timedelta(hours = -8)
从格林尼治标准时间转换为PST。
那么,如何确定offset
?这是一个简单的函数,前提是您只有很少的转换可能性而无需使用时区“可感知”的datetime对象,而其他一些答案则很好。有点手册,但有时最好是清晰的。
def change_timezone(timein, timezone, timezone_out):
'''
changes timezone between predefined timezone offsets to GMT
timein - datetime object
timezone - 'PST', 'PDT', 'GMT' (can add more as needed)
timezone_out - 'PST', 'PDT', 'GMT' (can add more as needed)
'''
# simple table lookup
tz_offset = {'PST': {'GMT': 8, 'PDT': 1, 'PST': 0}, \
'GMT': {'PST': -8, 'PDT': -7, 'GMT': 0}, \
'PDT': {'GMT': 7, 'PST': -1, 'PDT': 0}}
try:
offset = tz_offset[timezone][timezone_out]
except:
msg = 'Input timezone=' + timezone + ' OR output time zone=' + \
timezone_out + ' not recognized'
raise DateTimeError(msg)
return timein + timedelta(hours = offset)
在查看了众多答案并尝试了我能想到的最严格的代码之后(目前),似乎最好的做法是,所有重要的应用程序(时间很重要且必须考虑混合时区)必须做出真正的努力所有日期时间对象都“知道”。那么看来,最简单的答案是:
timeout = timein.astimezone(pytz.timezone("GMT"))
例如,转换为GMT。当然,要与您希望的任何其他时区(本地或其他)进行相互转换,只需使用pytz可以理解的适当时区字符串(来自pytz.all_timezones)。然后还要考虑夏令时。
答案 13 :(得分:0)
使用 time.timezone
,它给出一个整数,单位为“UTC 以西的秒数”。
例如:
from datetime import datetime, timedelta, timezone
import time
# make datetime from timestamp, thus no timezone info is attached
now = datetime.fromtimestamp(time.time())
# make local timezone with time.timezone
local_tz = timezone(timedelta(seconds=-time.timezone))
# attach different timezones as you wish
utc_time = now.astimezone(timezone.utc)
local_time = now.astimezone(local_tz)
print(utc_time.isoformat(timespec='seconds'))
print(local_time.isoformat(timespec='seconds'))
在我的电脑(Python 3.7.3)上,它给出:
2021-05-07T12:50:46+00:00
2021-05-07T20:50:46+08:00
非常简单,只使用标准库~