仅使用python标准库将python UTC日期时间转换为本地日期时间?

Nit*_*ark 150 python timezone datetime python-datetime

我有一个使用datetime.utcnow()创建并保存在数据库中的python datetime实例.

为了显示,我想使用默认的本地时区将从数据库检索的日期时间实例转换为本地日期时间(即,就像使用datetime.now()创建日期时间一样).

如何仅使用python标准库将UTC日期时间转换为本地日期时间(例如,没有pytz依赖项)?

似乎一个解决方案是使用datetime.astimezone(tz),但是如何获得默认的本地时区?

jfs*_*jfs 222

在Python 3.3+中:

from datetime import datetime, timezone

def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
Run Code Online (Sandbox Code Playgroud)

在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)
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

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()))
Run Code Online (Sandbox Code Playgroud)

产量

Python 3.3
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
Run Code Online (Sandbox Code Playgroud) Python 2
2010-06-06 21:29:07.730000 
2010-12-06 20:29:07.730000 
2012-11-08 14:19:50.093911 
Run Code Online (Sandbox Code Playgroud) 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
Run Code Online (Sandbox Code Playgroud)

注意:它考虑了DST和最近MSK时区的utc偏移的变化.

我不知道非pytz解决方案是否适用于Windows.

  • @avi:一般来说:[pytz:为什么在时区之间进行转换需要规范化?](http://stackoverflow.com/q/1422880/4279).注意:对于`.normalize()`的当前实现没有必要,因为`.astimezone()`的源时区是UTC. (4认同)
  • “规范化”有什么作用? (2认同)
  • 尝试 pytz 解决方案时,我收到“TypeError:replace() 没有关键字参数”。 (2认同)
  • Python 3.3+ 解决方案的快速提示。要走向相反的方向(UTC 本机),可以使用: `local_dt.replace(tzinfo=None).astimezone(tz=timezone.utc)` (2认同)
  • @Tes3awy 是的,答案中的 DST 代表“夏令时” (2认同)

Len*_*bro 40

您不能只使用标准库,因为标准库没有任何时区.你需要pytzdateutil.

>>> 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())
Run Code Online (Sandbox Code Playgroud)

或者,你可以通过实现自己的时区来实现没有pytz或dateutil.但这很愚蠢.

  • 为什么不使用简单的“datetime.now(timezone.utc)”并避免令人困惑的 utcnow *加上*额外的替换步骤?此外,“ZoneInfo('localtime')”是特定于平台的。 (3认同)
  • 更新: pytz 和 dateutil 现在都支持 Python 3。 (2认同)

xjc*_*jcl 16

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
Run Code Online (Sandbox Code Playgroud)

中欧比 UTC 早 1 或 2 小时,因此local_aware

datetime.datetime(2020, 10, 31, 13, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='localtime'))
Run Code Online (Sandbox Code Playgroud)

str

2020-10-31 13:00:00+01:00
Run Code Online (Sandbox Code Playgroud)

Windows 没有系统时区数据库,所以这里需要一个额外的包:

pip install tzdata  
Run Code Online (Sandbox Code Playgroud)

有一个 backport 允许在Python 3.6 到 3.8 中使用

sudo pip install backports.zoneinfo
Run Code Online (Sandbox Code Playgroud)

然后:

from backports.zoneinfo import ZoneInfo
Run Code Online (Sandbox Code Playgroud)

  • 您关于 Win10 的注释可能是正确的,因为 Python 3.9 不使用它,但 Win10 自 Windows 10 1703(2017 年发布)以来就包含了 IANA ICU 数据,但它在 1903 年有所改进。根据 Raymond Chen 的说法,他们建议应用程序使用现在它超过了 Windows 注册表 TZ 数据。https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255 (2认同)

小智 8

以阿列克谢的评论为基础。这也适用于 DST。

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)
Run Code Online (Sandbox Code Playgroud)


Nit*_*ark 6

我想我想出来了:计算自纪元以来的秒数,然后使用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 )
Run Code Online (Sandbox Code Playgroud)

它正确应用夏季/冬季夏令时:

>>> 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)
Run Code Online (Sandbox Code Playgroud)


N R*_*awa 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)
Run Code Online (Sandbox Code Playgroud)

通过类方法创建的朴素对象 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)
Run Code Online (Sandbox Code Playgroud)