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

2025-01-08 08:50:00
admin
原创
18
摘要:问题描述:我有一个datetime使用数据库创建datetime.utcnow()并保存在数据库中的 python 实例。为了显示,我想使用默认本地时区datetime将从数据库检索到的实例转换为本地(即,就好像是使用创建的一样)。datetime`datetime`datetime.now()如何仅使用 p...

问题描述:

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

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

如何仅使用 python 标准库(例如无依赖项)将 UTC 转换datetime为本地时间?datetime`pytz`

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


解决方案 1:

在 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.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

Python 2

2010-06-06 21:29:07.730000 
2010-12-06 20:29:07.730000 
2012-11-08 14:19:50.093911 

派茨

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。

解决方案 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')

然后转换。要在时区之间转换,日期时间必须知道它位于哪个时区,然后我们只需使用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,您需要 backports.zoneinfo 模块:

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

其余都一样。

对于早于该版本需要pytzdateutil。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

解决方案 3:

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中使用:

sudo pip install backports.zoneinfo

然后:

from backports.zoneinfo import ZoneInfo

解决方案 4:

您无法使用标准库来实现这一点。使用 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 函数将一个简单的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)

解决方案 5:

根据 Alexei 的评论。这也应该适用于 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)

解决方案 6:

我想我已经明白了:计算自纪元以来的秒数,然后使用 time.localtime 转换为本地时间,然后将时间结构转换回日期时间......

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)

解决方案 7:

标准 Python 库根本没有提供任何tzinfo实现。我一直认为这是 datetime 模块的一个令人惊讶的缺点。

tzinfo 类的文档确实附带了一些有用的例子。请查看本节末尾的大代码块。

解决方案 8:

使用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

非常简单,只使用标准库~

解决方案 9:

我发现最简单的方法是获取所在位置的时间偏移量,然后从小时中减去该时间。

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 中,这对我来说有效。

解决方案 10:

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

import time
import datetime

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

它的优点是写出反函数很简单

解决方案 11:

这是另一种以日期时间格式更改时区的方法(我知道我在这上面浪费了精力,但是我没有看到这个页面,所以我不知道如何做),不需要分钟和秒,因为我的项目不需要它:

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)

解决方案 12:

对于特定情况:

 输入 utc 日期时间字符串。//通常来自日志

 输出语言环境日期时间字符串。

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'

解决方案 13:

这是一种糟糕的方法,但它避免了创建定义。它满足了坚持使用基本 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])

解决方案 14:

使用 timedelta 在时区之间切换。您只需要时区之间的小时偏移量。不必摆弄 datetime 对象的所有 6 个元素的边界。timedelta 也可以轻松处理闰年、闰世纪等。您必须首先

from datetime import datetime, timedelta

然后,如果offset时区增量以小时为单位:

timeout = timein + timedelta(hours = offset)

其中 timein 和 timeout 是日期时间对象。例如

timein + timedelta(hours = -8)

从 GMT 转换为 PST。

那么,如何确定offset?这是一个简单的函数,前提是您只有几种转换的可能性,而无需使用“感知时区”的日期时间对象,而其他一些答案很好地做到了这一点。有点手动,但有时清晰度最好。

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}, \n                  'GMT': {'PST': -8, 'PDT': -7, 'GMT': 0}, \n                  'PDT': {'GMT': 7, 'PST': -1, 'PDT': 0}}
    try:
        offset = tz_offset[timezone][timezone_out]
    except:
        msg = 'Input timezone=' + timezone + ' OR output time zone=' + \n            timezone_out + ' not recognized'
        raise DateTimeError(msg)

    return timein + timedelta(hours = offset)

在查看了众多答案并尝试了我能想到的最严格的代码(目前)之后,似乎最好的是,所有时间很重要且必须考虑混合时区的应用程序都应该真正努力使所有日期时间对象“意识到”。 那么看起来最简单的答案是:

timeout = timein.astimezone(pytz.timezone("GMT"))

例如转换为 GMT。当然,要转换为/从您想要的任何其他时区(本地或其他时区),只需使用 pytz 理解的适当时区字符串(来自 pytz.all_timezones)。然后还会考虑夏令时。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   984  
  在项目管理领域,CDCP(Certified Data Center Professional)认证评审是一个至关重要的环节,它不仅验证了项目团队的专业能力,还直接关系到项目的成功与否。在这一评审过程中,沟通技巧的运用至关重要。有效的沟通不仅能够确保信息的准确传递,还能增强团队协作,提升评审效率。本文将深入探讨CDCP...
华为IPD流程   0  
  IPD(Integrated Product Development,集成产品开发)是一种以客户需求为核心、跨部门协同的产品开发模式,旨在通过高效的资源整合和流程优化,提升产品开发的成功率和市场竞争力。在IPD培训课程中,掌握关键成功因素是确保团队能够有效实施这一模式的核心。以下将从五个关键成功因素展开讨论,帮助企业和...
IPD项目流程图   0  
  华为IPD(Integrated Product Development,集成产品开发)流程是华为公司在其全球化进程中逐步构建和完善的一套高效产品开发管理体系。这一流程不仅帮助华为在技术创新和产品交付上实现了质的飞跃,还为其在全球市场中赢得了显著的竞争优势。IPD的核心在于通过跨部门协作、阶段性评审和市场需求驱动,确保...
华为IPD   0  
  华为作为全球领先的通信技术解决方案提供商,其成功的背后离不开一套成熟的管理体系——集成产品开发(IPD)。IPD不仅是一种产品开发流程,更是一种系统化的管理思想,它通过跨职能团队的协作、阶段评审机制和市场需求驱动的开发模式,帮助华为在全球市场中脱颖而出。从最初的国内市场到如今的全球化布局,华为的IPD体系在多个领域展现...
IPD管理流程   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用