【问题标题】:Time zone field in isoformatisoformat 中的时区字段
【发布时间】:2014-12-03 14:03:03
【问题描述】:

我有一个应该在 EST 中的时间戳:

2014-10-06T18:06:40-04:56

我了解第一部分:2014-10-06T18:06:40,但不是-04:56

-04:56 在这里是什么意思?`

这是我获得时间戳的方式:

import datetime
start_time = datetime.datetime(year  = 2014, 
                               month = 10, 
                               day   = 6, 
                               hour  = 18, 
                               tzinfo = pytz.timezone('US/Eastern'))
end_time   = start_time + datetime.timedelta(seconds=400)

然后:

end_time.isoformat()

返回:

2014-10-06T18:06:40-04:56

【问题讨论】:

  • 首先,EST 是 UTC-5,而不是 UTC-4。
  • 其次,当我用 EST 时区填充 datetime 对象时,无论是来自 pytztzlocal 还是我能找到的其他任何东西,我都会按预期得到 -05:00。当然,我总是可以不遗余力地构建位于-04:56 的时区,如果我愿意,可以将其命名为 EST,而 Python 或任何模块中的任何内容都不会注意到我会不遗余力地混淆自己,但是……这并不容易做到。
  • 乔希,这就是你认为的意思。它描述了一个比 UTC 滞后 4 小时 56 分钟的时区。该时间戳相当于2014-10-06T23:02:40Z。你从哪里得到那个时间戳?或者,是什么软件生成了那个时间戳?
  • @Robᵩ 抱歉耽搁了-我更新了帖子
  • @Josh:为了将来参考,通常最好包含一个可以运行的完整脚本,或者如果不是完整的交互式提示脚本,包括导入和所有内容。在这种情况下,它并没有真正模棱两可或复杂,但彻底彻底永远不会有坏处,尤其是在编辑最初缺少信息的问题时。

标签: python datetime


【解决方案1】:

问题是pytz:

……不同于记录在案的用于 tzinfo 实现的 Python API;如果您想创建本地挂钟时间,您需要使用本文档中记录的localize() 方法……

再往下说:

不幸的是,对于许多时区,使用标准日期时间构造函数的 tzinfo 参数对 pytz “不起作用”。

>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt)
'2002-10-27 12:00:00 LMT+0020'

因此,您需要按照文档的建议进行操作 - 使用 normalize、构建 UTC 时间和使用 astimezone 等。您想要哪一个取决于您想要做什么。例如:

>>> from datetime import datetime
>>> from pytz import timezone
>>> utc = timezone('UTC')
>>> eastern = timezone('US/Eastern')
>>> datetime(2014, 10, 6, 18, tzinfo=eastern).isoformat()
'2014-10-06T18:00:00-04:56'
>>> eastern.normalize(datetime(2014, 10, 6, 18, tzinfo=eastern)).isoformat()
'2014-10-06T18:56:00-04:00'
>>> datetime(2014, 10, 6, 18, tzinfo=utc).astimezone(eastern).isoformat()
'2014-10-06T14:00:00-04:00'
>>> eastern.localize(datetime(2014, 10, 6, 18)).isoformat()
'2014-10-06T18:00:00-04:00'

我认为这是你想要的最后一个。正如localize 的文档所说:

将原始时间转换为当地时间。

这个方法应该用于构建本地时间,而不是 而不是将 tzinfo 参数传递给 datetime 构造函数。

我认为构建当地时间正是您想要的。


如果您想知道为什么...好吧,如果您查看 Olson 数据库中的数据,或者只是打印出 eastern._utcoffset,您会看到 -1 天 +68640 分钟.那是 19.0166+ 小时,而不是 19 小时。为什么?因为每个时区都是用它的起始偏移量定义的,并从那里进行调整。东部基于纽约的时区,截至 1883 年 11 月 18 日 12:03:58,此时距格林威治标准时间为 -04:56:02。从 1920 年开始的日期进行了调整,减去了额外的 00:03:58。当然,每年的夏令时来回调整一小时。所以,到目前为止,东部时间是 -04:00,但不知道它应该代表什么日期,它是 -04:56。而且,因为datetime 只是询问时区的偏移量,而不是特定时间的偏移量,所以它就是这样。


最后一件事:EST 是东部标准时间,即 -05:00。这不是 2014 年 10 月 6 日美国任何地方的时区,因为在 2014 年,美国的夏令时到 11 月 2 日。 (印第安纳州曾经有一些县在夏季使用 EST,但现在不再使用了。)您要查找的是 EDT,即东部夏令时间,即 -04:00。或者,当然是 ET,夏季是 EDT,冬季是 EST,您可以通过查找 'US/Eastern''America/New_York' 得到。

【讨论】:

  • 哇,这让我很难受。
  • 方法timezone从何而来?
  • @Josh:对不起,让我添加导入。它来自pytz
  • 谢谢!一个快速说明:最后一个命令不会返回您发布的字符串(至少在我的机器上)。我得到:'2014-10-06T14:00:00-04:00'。所以我猜那条路线行不通(你必须预先计算时差..)
  • @Josh:首先,正如我之前提到的,2014 年 10 月 6 日没有 EST,只有 EDT; DST 在美国持续到 11 月 2 日。但无论如何,在这里得到你真正想要的最简单的方法是构造一个简单的日期时间,然后localize它;看我的新的第四个例子。
【解决方案2】:

-04:56 在这里是什么意思?`

这意味着生成输入时间戳的代码被破坏,表明对pytz 时区的工作方式缺乏了解。如果您认为只有 UTC 偏移量 (-04:56) 是错误的,但日期本身是东部时区的正确时间,那么您不应该相信它的结果,那么要解析时间,请执行以下操作:

#!/usr/bin/env python
from datetime import datetime, timedelta
import pytz

tz = pytz.timezone('America/New_York')

naive = datetime.strptime("2014-10-06T18:06:40-04:56"[:-6],
                          "%Y-%m-%dT%H:%M:%S")
start_time = tz.localize(naive, is_dst=None)
end_time = tz.normalize(start_time + timedelta(seconds=400))
print(start_time.isoformat())
print(end_time.isoformat())
  • 你应该使用tz.localize()而不是直接分配tzinfo属性
  • is_dst=None 断言输入时间存在且明确
  • 如果日期算术跨越 DST 边界,tz.normalize() 是必需的

输出

2014-10-06T18:06:40-04:00
2014-10-06T18:13:20-04:00

为什么需要localize()normalize()the pytz docs (the part about localize()/normalize() is the very first note in the documentation) 中进行了描述。

为什么东部时区 2014-04:56 UTC 偏移量错误

由于 DST 转换或其他原因(例如战争或某些政客认为这是一个好主意),同一地点的 UTC 偏移量可能在不同时间有所不同,例如,以下是美国/东部的可能值:

>>> import pytz
>>> pytz.timezone('US/Eastern')
{(datetime.timedelta(-1, 72000),
  datetime.timedelta(0, 3600),
  'EWT'): <DstTzInfo 'US/Eastern' EWT-1 day, 20:00:00 DST>,
 (datetime.timedelta(-1, 68640),
  datetime.timedelta(0),
  'LMT'): <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>,
 (datetime.timedelta(-1, 72000),
  datetime.timedelta(0, 3600),
  'EDT'): <DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>,
 (datetime.timedelta(-1, 68400),
  datetime.timedelta(0),
  'EST'): <DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>,
 (datetime.timedelta(-1, 72000),
  datetime.timedelta(0, 3600),
  'EPT'): <DstTzInfo 'US/Eastern' EPT-1 day, 20:00:00 DST>}

注意 LMT tzinfo 的 UTC 偏移量:timedelta(-1, 68640) == '-4:56:00'。得到它的方法是使用错误的代码:

#XXX BROKEN, DO NOT DO IT
>>> dt = datetime(2014, 10, 6, tzinfo=pytz.timezone('US/Eastern'))
>>> dt
datetime.datetime(2014, 10, 6, 0, 0, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>)
>>> dt.isoformat()
2014-10-06T00:00:00-04:56

直接分配tzinfo 不允许pytz 在给定时间内选择正确的tzinfo,而是使用一些可用的随机tzinfo 对象。您应该始终使用tz.localize() 附加正确的时区信息。

【讨论】:

  • 我不认为它使用了一些可用的随机对象;它使用 Olson 数据库中指定的时区定义偏移量,该偏移量几乎总是第一次建立时区时的偏移量。东部被追溯定义为当地平均时间……我忘记了哪个地点……在 1920 年之前,所以这就是奥尔森使用的定义; -05:00 被列为从 1920 年开始的 4 分钟变化。
  • 从某种意义上说它是随机的,你不能依赖它。只要在文档中没有保证,实现如何选择时区并不重要。
【解决方案3】:

我建议查看arrow。上述答案有效,但使代码真正令人困惑。 Arrow 让您可以简单地使用:

In [82]: start_time = arrow.get(2014, 10, 6, 18).to('US/Eastern')
In [83]: end_time = start_time.shift(seconds=400)
In [84]: start_time
Out[84]: <Arrow [2014-10-06T14:00:00-04:00]>
In [85]: end_time
Out[85]: <Arrow [2014-10-06T14:06:40-04:00]>

您可以使用

检索datetime 对象
In [86]: start_time.datetime
Out[86]: datetime.datetime(2014, 10, 6, 14, 0, tzinfo=tzfile('/usr/share/zoneinfo/US/Eastern'))

【讨论】:

  • 这太棒了!而且很简单。
猜你喜欢
  • 1970-01-01
  • 2015-01-25
  • 1970-01-01
  • 2011-04-01
  • 2010-12-24
  • 2019-01-03
  • 1970-01-01
  • 2021-07-12
  • 2012-06-06
相关资源
最近更新 更多