我有一个使用datetime.utcnow()创建的python datetime实例,并持久化在数据库中。

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

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

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


当前回答

我发现的最简单的方法是得到你所处位置的时间偏移量,然后从小时中减去它。

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中,这对我来说是可行的。

其他回答

从Python 3.9开始,你可以使用zoneinfo模块。

首先让我们用utcnow()获取时间:

>>> from datetime import datetime
>>> database_time = datetime.utcnow()
>>> database_time
datetime.datetime(2021, 9, 24, 4, 18, 27, 706532)

然后创建时区:

>>> from zoneinfo import ZoneInfo
>>> utc = ZoneInfo('UTC')
>>> localtz = ZoneInfo('localtime')

然后转换。为了在时区之间进行转换,datetime必须知道它所在的时区,然后我们只需使用astimezone():

>>> utctime = database_time.replace(tzinfo=utc)
>>> localtime = utctime.astimezone(localtz)
>>> localtime
datetime.datetime(2021, 9, 24, 6, 18, 27, 706532, tzinfo=zoneinfo.ZoneInfo(key='localtime'))

对于Python 3.6到3.8,你需要后台端口。zoneinfo模块:

>>> try:
>>>     from zoneinfo import ZoneInfo
>>> except ImportError:
>>>     from backports.zoneinfo import ZoneInfo

其余的都是一样的。

对于更早的版本,需要pytz或dateutil。Datutil的工作原理类似于zoneinfo:

>>> from dateutil import tz
>>> utc = tz.gettz('UTC')
>>> localtz = tz.tzlocal()

The Conversion:
>>> utctime = now.replace(tzinfo=UTC)
>>> localtime = utctime.astimezone(localtz)
>>> localtime
datetime.datetime(2010, 12, 30, 15, 51, 22, 114668, tzinfo=tzlocal())

pytz有一个不同的接口,这是Python的时区处理不处理歧义时间的结果:

>>> import pytz
>>> utc = pytz.timezone('UTC')
# There is no local timezone support, you need to know your timezone
>>> localtz = pytz.timezone('Europe/Paris')

>>> utctime = utc.localize(database_time)
>>> localtime = localtz.normalize(utctime.astimezone(localtz))
>>> localtime

我想我弄清楚了:计算自epoch以来的秒数,然后使用时间转换为本地timzeone。然后将时间结构转换回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 )

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

>>> 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)

针对特定情况: 输入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'

根据阿列克谢的评论。这也适用于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)

你不能用标准库来做。使用pytz模块,您可以将任何naive/aware datetime对象转换为任何其他时区。让我们看一些使用Python 3的例子。

通过类方法utcnow()创建的朴素对象

要将naive对象转换为任何其他时区,首先必须将其转换为感知datetime对象。可以使用replace方法将天真的datetime对象转换为可感知的datetime对象。然后可以使用astimezone方法将一个感知的datetime对象转换为任何其他时区。

变量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对象感知时区。localalize函数的作用是:将原始datetime对象转换为可感知时区的datetime对象。然后可以使用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)