我有一个使用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

其他回答

下面是另一种在datetime格式中更改时区的方法(我知道我在这上面浪费了精力,但我没有看到这个页面,所以我不知道怎么做),没有min.和sec.因为我的项目不需要它:

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)

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

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模块,所以现在可以这样做(仅限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

你不能用标准库来做。使用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)

在Python 2和3中工作的简单(但可能有缺陷)方法:

import time
import datetime

def utc_to_local(dt):
    return dt - datetime.timedelta(seconds = time.timezone)

它的优点是写一个逆函数很简单