我有一个使用datetime.utcnow()创建的python datetime实例,并持久化在数据库中。
为了显示,我想使用默认的本地时区将从数据库检索到的datetime实例转换为本地datetime(即,就像使用datetime.now()创建的datetime一样)。
如何将UTC日期时间转换为本地日期时间仅使用python标准库(例如,没有pytz依赖)?
一种解决方案似乎是使用datetime.astimezone(tz),但是如何获得默认的本地时区呢?
我有一个使用datetime.utcnow()创建的python datetime实例,并持久化在数据库中。
为了显示,我想使用默认的本地时区将从数据库检索到的datetime实例转换为本地datetime(即,就像使用datetime.now()创建的datetime一样)。
如何将UTC日期时间转换为本地日期时间仅使用python标准库(例如,没有pytz依赖)?
一种解决方案似乎是使用datetime.astimezone(tz),但是如何获得默认的本地时区呢?
当前回答
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
中欧比UTC早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没有系统时区数据库,所以这里需要一个额外的包:
pip install tzdata
在Python 3.6到3.8中允许使用一个backport:
sudo pip install backports.zoneinfo
然后:
from backports.zoneinfo import ZoneInfo
其他回答
标准的Python库根本没有任何tzinfo实现。我一直认为这是datetime模块的一个惊人缺点。
tzinfo类的文档中确实提供了一些有用的示例。在小节的末尾寻找大代码块。
针对特定情况: 输入utc datetime字符串。//通常来自日志 输出区域日期时间字符串。
def utc_to_locale(utc_str):
# from utc to locale
d1=datetime.fromisoformat(utc_str+'-00:00')
return d1.astimezone().strftime('%F %T.%f')[:-3]
测试:
>>> utc_to_locale('2022-02-14 00:49:06')
'2022-02-14 08:49:06.000'
>>> utc_to_locale('2022-02-14 00:49:06.123')
'2022-02-14 08:49:06.123'
>>> utc_to_locale('2022-02-14T00:49:06.123')
'2022-02-14 08:49:06.123'
在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()))
输出
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
注意:它考虑了DST和MSK时区utc偏移量的最近变化。
我不知道非pytz解决方案是否适用于Windows。
使用timedelta在时区之间切换。您所需要的只是时区之间的小时偏移量。不必为datetime对象的所有6个元素设置边界。Timedelta也可以轻松处理闰年、闰世纪等。你必须首先
from datetime import datetime, timedelta
如果offset是时区的delta(以小时为单位):
超时 = 时间 + 时间增量(小时 = 偏移量)
其中timein和timeout是datetime对象。如。
时间 + 时间增量(小时 = -8)
从格林尼治标准时间转换为太平洋标准时间。
那么,如何确定偏移量呢?这里是一个简单的函数,前提是你只有一些转换的可能性,而不使用时区“感知”的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)
在看了大量的答案和我能想到的最严格的代码之后(目前),似乎最好的是所有应用程序,其中时间是重要的,混合时区必须考虑在内,应该真正努力使所有datetime对象“感知”。那么,最简单的答案似乎是:
timeout = timein.astimezone(pytz.timezone("GMT"))
例如,转换为格林尼治时间。当然,要转换到或从您希望的任何其他时区(本地或其他时区),只需使用pytz理解的适当的时区字符串(来自pytz.all_timezones)。日光节约时间也被考虑在内。
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
中欧比UTC早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没有系统时区数据库,所以这里需要一个额外的包:
pip install tzdata
在Python 3.6到3.8中允许使用一个backport:
sudo pip install backports.zoneinfo
然后:
from backports.zoneinfo import ZoneInfo