如何使日期时间对象具有感知能力(而非简单能力)?

2024-12-19 09:23:00
admin
原创
57
摘要:问题描述:我需要做什么我有一个不了解时区的日期时间对象,我需要向其添加时区,以便能够将其与其他了解时区的日期时间对象进行比较。我不想因为这个遗留问题而将我的整个应用程序转换为不了解时区的。我尝试过的方法首先,演示一下这个问题:Python 2.6.1 (r261:67515, Jun 24 2010, 21:...

问题描述:

我需要做什么

我有一个不了解时区的日期时间对象,我需要向其添加时区,以便能够将其与其他了解时区的日期时间对象进行比较。我不想因为这个遗留问题而将我的整个应用程序转换为不了解时区的。

我尝试过的方法

首先,演示一下这个问题:

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes

首先,我尝试astimezone

>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>

失败并不奇怪,因为它实际上是在尝试进行转换。替换似乎是更好的选择(根据如何在 Python 中获取“时区感知”的 datetime.today() 值?):

>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>> 

但正如您所见,replace 似乎设置了tzinfo,但没有使对象感知。我准备回过头来修改输入字符串,使其在解析之前具有时区(dateutil如果这很重要,我正在使用它进行解析),但这似乎非常笨拙。

另外,我在 Python 2.6 和 Python 2.7 中尝试过,结果相同。

语境

我正在为一些数据文件编写解析器。我需要支持一种旧格式,其中日期字符串没有时区指示符。我已经修复了数据源,但我仍然需要支持旧数据格式。由于各种业务 BS 原因,一次性转换旧数据不是一种选择。虽然一般来说,我不喜欢硬编码默认时区的想法,但在这种情况下,它似乎是最好的选择。我有信心知道所有相关的旧数据都是 UTC,所以我准备在这种情况下接受默认使用 UTC 的风险。


解决方案 1:

一般来说,为了使简单的日期时间能够感知时区,请使用localize 方法:

import datetime
import pytz

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)

now_aware = pytz.utc.localize(unaware)
assert aware == now_aware

对于 UTC 时区,实际上没有必要使用localize,因为不需要处理夏令时计算:

now_aware = unaware.replace(tzinfo=pytz.UTC)

有效。(.replace返回一个新的日期时间;它不会修改unaware。)

解决方案 2:

所有这些示例都使用外部模块,但您只需使用 datetime 模块即可获得相同的结果,正如本 SO 答案中所示:

from datetime import datetime, timezone

dt = datetime.now()
dt = dt.replace(tzinfo=timezone.utc)

print(dt.isoformat())
# '2017-01-12T22:11:31+00:00'

更少的依赖性并且没有 pytz 问题。

注意:如果您希望将其与 python3 和 python2 一起使用,您也可以将其用于时区导入(UTC 硬编码):

try:
    from datetime import timezone
    utc = timezone.utc
except ImportError:
    #Hi there python2 user
    class UTC(tzinfo):
        def utcoffset(self, dt):
            return timedelta(0)
        def tzname(self, dt):
            return "UTC"
        def dst(self, dt):
            return timedelta(0)
    utc = UTC()

解决方案 3:

我在 2011 年编写了这个 Python 2 脚本,但从未检查过它是否适用于 Python 3。

我已经从 dt_aware 移至 dt_unaware:

dt_unaware = dt_aware.replace(tzinfo=None)

并将 dt_unware 更改为 dt_aware:

from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)

解决方案 4:

Python 3.9 添加了zoneinfo模块,因此现在只需要标准库!

from zoneinfo import ZoneInfo
from datetime import datetime
unaware = datetime(2020, 10, 31, 12)

附加时区:

>>> unaware.replace(tzinfo=ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 12:00:00+09:00'

附加系统的本地时区:

>>> unaware.replace(tzinfo=ZoneInfo('localtime'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='localtime'))
>>> str(_)
'2020-10-31 12:00:00+01:00'

随后它被正确转换为其他时区:

>>> unaware.replace(tzinfo=ZoneInfo('localtime')).astimezone(ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 20, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 20:00:00+09:00'

维基百科可用时区列表


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

pip install tzdata  

有一个反向移植允许zoneinfoPython 3.6 到 3.8中使用:

pip install backports.zoneinfo

然后:

from backports.zoneinfo import ZoneInfo

解决方案 5:

我在 Django 中使用这个语句将未知时间转换为已知时间:

from django.utils import timezone

dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())

解决方案 6:

我同意前面的答案,如果您同意以 UTC 开始,那就没问题。但我认为,人们使用具有非 UTC 本地时区的日期时间的 tz 感知值也是常见的情况。

如果只是根据名称判断,人们可能会推断 replace() 是适用的,并生成正确的日期时间感知对象。但事实并非如此。

replace(tzinfo=...) 的行为似乎是随机的。因此它毫无用处。不要使用它!

localize 是正确的函数。例如:

localdatetime_aware = tz.localize(datetime_nonaware)

或者更完整的例子:

import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())

给我一个当前本地时间的时区感知日期时间值:

datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)

解决方案 7:

使用dateutil.tz.tzlocal()来获取您使用datetime.datetime.now()和时的时区datetime.datetime.astimezone()

from datetime import datetime
from dateutil import tz

unlocalisedDatetime = datetime.now()

localisedDatetime1 = datetime.now(tz = tz.tzlocal())
localisedDatetime2 = datetime(2017, 6, 24, 12, 24, 36, tz.tzlocal())
localisedDatetime3 = unlocalisedDatetime.astimezone(tz = tz.tzlocal())
localisedDatetime4 = unlocalisedDatetime.replace(tzinfo = tz.tzlocal())

请注意,datetime.astimezone将首先将您的对象转换为 UTC,然后转换为时区,这与使用原始时区信息进行datetime调用相同。datetime.replace`None`

解决方案 8:

这将@Sérgio 和 @unutbu 的答案编纂成法典。它将“只适用于”对象pytz.timezone或IANA 时区字符串。

def make_tz_aware(dt, tz='UTC', is_dst=None):
    """Add timezone information to a datetime object, only if it is naive."""
    tz = dt.tzinfo or tz
    try:
        tz = pytz.timezone(tz)
    except AttributeError:
        pass
    return tz.localize(dt, is_dst=is_dst) 

这看起来像datetime.localize()(或.inform().awarify()) 应该做的事情,接受 tz 参数的字符串和时区对象,如果没有指定时区,则默认为 UTC。

解决方案 9:

对于那些只想制作一个时区感知日期时间的人来说

import datetime

datetime.datetime(2019, 12, 7, tzinfo=datetime.timezone.utc)

对于那些想要从 python 3.9 stdlib 开始使用非 utc 时区日期时间的人

import datetime
from zoneinfo import ZoneInfo

datetime.datetime(2019, 12, 7, tzinfo=ZoneInfo("America/Los_Angeles")) 

解决方案 10:

datetime让对象变得不幼稚的另一种方式是:

>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc)
datetime.datetime(2021, 5, 1, 22, 51, 16, 219942, tzinfo=datetime.timezone.utc)

解决方案 11:

我对 Python 还很陌生,也遇到了同样的问题。我发现这个解决方案非常简单,对我来说效果很好(Python 3.6):

unaware=parser.parse("2020-05-01 0:00:00")
aware=unaware.replace(tzinfo=tz.tzlocal()).astimezone(tz.tzlocal())

解决方案 12:

时区切换

import pytz
from datetime import datetime

other_tz = pytz.timezone('Europe/Madrid')

# From random aware datetime...
aware_datetime = datetime.utcnow().astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00

# 1. Change aware datetime to UTC and remove tzinfo to obtain an unaware datetime
unaware_datetime = aware_datetime.astimezone(pytz.UTC).replace(tzinfo=None)
>> 2020-05-21 06:28:26.984948

# 2. Set tzinfo to UTC directly on an unaware datetime to obtain an utc aware datetime
aware_datetime_utc = unaware_datetime.replace(tzinfo=pytz.UTC)
>> 2020-05-21 06:28:26.984948+00:00

# 3. Convert the aware utc datetime into another timezone
reconverted_aware_datetime = aware_datetime_utc.astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00

# Initial Aware Datetime and Reconverted Aware Datetime are equal
print(aware_datetime1 == aware_datetime2)
>> True

解决方案 13:

按照 unutbu 答案的格式;我制作了一个实用程序模块来处理此类事情,语法更直观。可以用 pip 安装。

import datetime
import saturn

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
now_aware = saturn.fix_naive(unaware)

now_aware_madrid = saturn.fix_naive(unaware, 'Europe/Madrid')

解决方案 14:

这是一个简单的解决方案,可以最大限度地减少代码更改:

from datetime import datetime
import pytz

start_utc = datetime.utcnow()
print ("Time (UTC): %s" % start_utc.strftime("%d-%m-%Y %H:%M:%S"))

时间(UTC):2021年9月1日03:49:03

tz = pytz.timezone('Africa/Cairo')
start_tz = datetime.now().astimezone(tz)
print ("Time (RSA): %s" % start_tz.strftime("%d-%m-%Y %H:%M:%S"))

时间(RSA):2021 年 9 月 1 日 05:49:03

解决方案 15:

  • 根据文件datetime.utcnow

+ > **警告:**由于许多方法将简单`datetime`对象视为`datetime`本地时间,因此最好使用感知日期时间来表示 UTC 时间。因此,建议通过调用 来创建表示 UTC 当前时间的对象`datetime.now(timezone.utc)`。
+ 其他答案中涵盖了此选项,但未提及文档引用。
  • 根据文档datetime.utcfromtimestamp

+ > **警告:**因为简单`datetime`对象...通过调用`datetime.fromtimestamp(timestamp, tz=timezone.utc)`。
+ 其他答案未涉及此选项。
  • 测试于python 3.11.2

from datetime import datetime, timezone
import time  # for timestamp
import pytz  # for aware comparison

now = datetime.now(tz=timezone.utc)
aware = datetime(now.year, now.month, now.day, now.hour, now.minute, now.second, now.microsecond, tzinfo=pytz.UTC)

print(now == aware)
[out]: True

fts = datetime.fromtimestamp(time.time(), tz=timezone.utc)
aware = datetime(fts.year, fts.month, fts.day, fts.hour, fts.minute, fts.second, fts.microsecond, tzinfo=pytz.UTC)

print(fts == aware)
[out]: True

解决方案 16:

上面提到的所有方法,当它是一个Unix时间戳时,有一个使用pandas的非常简单的解决方案。

import pandas as pd

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

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用