在下一个小时内如何找到同一天的时间戳?

2024-10-02 14:25:36 发布

您现在位置:Python中文网/ 问答频道 /正文

在Python中,我可以找到本地时间的Unix时间戳,知道时区,如下所示(使用pytz):

>>> import datetime as DT
>>> import pytz
>>> mtl = pytz.timezone('America/Montreal')
>>> naive_time3 = DT.datetime.strptime('2013/11/03', '%Y/%m/%d')
>>> naive_time3
datetime.datetime(2013, 11, 3, 0, 0)
>>> localized_time3 = mtl.localize(naive_time3)
>>> localized_time3
datetime.datetime(2013, 11, 3, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>)
>>> localized_time3.timestamp()
1383451200.0

到目前为止,还不错。naive_time不知道时区,而{}知道它在2013/11/03的Montréal,所以(UTC)Unix时间戳是好的。这个时区也是我的本地时区,这个时间戳似乎是对的:

^{pr2}$

现在,在蒙特勒亚尔,11月3日下午2点,时钟调整了一个小时,所以那天我们又多了一个小时。这意味着,在2013年11月3日至2013年11月4日期间,25小时。这表明:

>>> naive_time4 = DT.datetime.strptime('2013/11/04', '%Y/%m/%d')
>>> localized_time4 = mtl.localize(naive_time4)
>>> localized_time4
datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EST-1 day, 19:00:00 STD>)
>>> (localized_time4.timestamp() - localized_time3.timestamp()) / 3600
25.0

现在,我正在寻找一种从localized_time3获取localized_time4对象的简单方法,因为我知道我希望在下一个本地化的日期在同一时间(这里是午夜)。我试过timedelta,但我相信它不知道时区或夏令时:

>>> localized_time4td = localized_time3 + DT.timedelta(1)
>>> localized_time4td
datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>)
>>> (localized_time4td.timestamp() - localized_time3.timestamp()) / 3600
24.0

我的目的是获取与日志条目相关的信息,这些日志条目存储在Unix时间戳中,用于每个本地日。当然,如果我使用localized_time3.timestamp()并在这里添加24 * 3600(这将与localized_time4td.timestamp()相同),我将错过localized_time4td.timestamp()和{}之间发生的所有日志条目。在

换句话说,我要查找的函数或方法应该知道何时将25小时、24小时或23小时添加到Unix时间戳中,这取决于DST转换发生的时间。在


Tags: datetime时间dtunixlocalizedtimestamp小时montreal
3条回答

不使用新软件包:

def add_day(x):
    d = x.date()+DT.timedelta(1)
    return mtl.localize(x.replace(year=d.year, month=d.month, day=d.day, tzinfo=None))

完整脚本:

^{pr2}$

calendar代表Python。)

我逐渐地在这个答案的末尾提供了几种解决方案,这些方案试图解决以下问题:

  • 由于DST的utc偏移量
  • 由于与DST无关的原因,本地时区可能具有不同的utc偏移量的过去日期。dateutil和stdlib解决方案在某些系统(尤其是Windows)上失败
  • DST期间的不明确时间(不知道Arrow是否提供了处理它的接口)
  • DST期间不存在的时间(相同)

要查找给定时区中明天午夜(或其他固定时间)的POSIX时间戳,可以使用How do I get the UTC time of “midnight” for a given timezone?中的代码:

from datetime import datetime, time, timedelta
import pytz

DAY = timedelta(1)
tz = pytz.timezone('America/Montreal')

tomorrow = datetime(2013, 11, 3).date() + DAY

midnight = tz.localize(datetime.combine(tomorrow, time(0, 0)), is_dst=None)
timestamp = (midnight - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()

dt.date()方法为朴素对象和时区感知对象返回相同的初始日期dt。在

时间戳的显式公式用于支持python3.3之前的Python版本。否则,.timestamp()方法可以在python3.3+中使用。在

为了避免在DST转换期间解析输入日期时出现歧义,而这种歧义对于.localize()方法是不可避免的,除非您知道is_dst参数,您可以使用与日期一起存储的Unix时间戳:

^{pr2}$

要支持其他固定时间(不仅是午夜):

tomorrow = local_dt.replace(tzinfo=None) + DAY # tomorrow, same time
dt_plus_day = tz.localize(tomorrow, is_dst=None)
timestamp = dt_plus_day.timestamp() # use the explicit formula before Python 3.3

如果结果日期不明确或不存在,is_dst=None将引发异常。为了避免出现异常,您可以选择最接近昨天的前一个日期的时间(相同的DST状态,即is_dst=local_dt.dst()):

from datetime import datetime, time, timedelta
import pytz

DAY = timedelta(1)
tz = pytz.timezone('America/Montreal')

local_dt = datetime.fromtimestamp(timestamp_from_the_log, tz)
tomorrow = local_dt.replace(tzinfo=None) + DAY

dt_plus_day = tz.localize(tomorrow, is_dst=local_dt.dst())
dt_plus_day = tz.normalize(dt_plus_day) # to detect non-existent times                                            
timestamp = (dt_plus_day - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()

.localize()尊重给定的时间,即使它不存在,因此需要.normalize()来确定时间。如果normalize()方法更改其输入(在本例中检测到不存在的时间)以与其他代码示例保持一致,则可以在此处引发异常。在

(感谢@rdodev将我指向Arrow)。在

使用箭头,此操作变得简单:

>>> import arrow
>>> import datetime as DT
>>> lt3 = arrow.get(DT.datetime(2013, 11, 3), 'America/Montreal')
>>> lt3
<Arrow [2013-11-03T00:00:00-04:00]>
>>> lt4 = arrow.get(DT.datetime(2013, 11, 4), 'America/Montreal')
>>> lt4
<Arrow [2013-11-04T00:00:00-05:00]>
>>> lt4.timestamp - (lt3.replace(days=1).timestamp)
0
>>> (lt3.replace(days=1).timestamp - lt3.timestamp) / 3600
25.0

使用Arrow的replace方法,单数单元名替换该属性,而复数则添加到该属性中。所以lt3.replace(days=1)是2013年11月4日,lt3.replace(day=1)是2013年11月1日。在

相关问题 更多 >