繁体   English   中英

Python 夏令时

[英]Python daylight savings time

如何检查夏令时是否有效?

您可以使用time.localtime并查看返回值中的tm_isdst标志。

>>> import time
>>> time.localtime()
(2010, 5, 21, 21, 48, 51, 4, 141, 0)
>>> _.tm_isdst
0

使用time.localtime() ,您可以在任意时间询问相同的问题,以查看 DST 是否会(或曾经)对您的当前时区生效。

如果您在笔记本电脑上运行代码,则接受的答案很好,但大多数 Python 应用程序都在使用 UTC 作为本地时间的服务器上运行,因此根据接受的答案,它们永远不会处于夏令时。

第二个问题是不同地区在不同的日期和时间实施夏令时。 因此,即使您有一个明确的时间,例如datetime.utcnow() ,它也可能是一个时区的夏令时,但不是另一个时区。

我们可以做的最好的事情是判断给定时间是否发生在特定时区的 DST 期间,我能找到的最佳方法已经由pytz localize函数实现,我们可以使用它来得到一个很好的答案它适用于我们的笔记本电脑和服务器。

import pytz

from datetime import datetime

def is_dst(dt=None, timezone="UTC"):
    if dt is None:
        dt = datetime.utcnow()
    timezone = pytz.timezone(timezone)
    timezone_aware_date = timezone.localize(dt, is_dst=None)
    return timezone_aware_date.tzinfo._dst.seconds != 0

一些例子

>>> is_dst() # it is never DST in UTC
False
>>> is_dst(datetime(2019, 1, 1), timezone="US/Pacific")
False
>>> is_dst(datetime(2019, 4, 1), timezone="US/Pacific")
True
>>> is_dst(datetime(2019, 3, 10, 2), timezone="US/Pacific")
NonExistentTimeError
>>> is_dst(datetime(2019, 11, 3, 1), timezone="US/Pacific")
AmbiguousTimeError

在我们的is_dst函数中,我们指定is_dst=None作为timezone.localize的参数,这将导致无意义时间抛出错误。 您可以使用is_dst=False忽略这些错误并在这些时间返回False

假设您想在datetime执行此操作

使用pytz使其具有时区意识,然后检查其dst属性:

import datetime
import pytz

def is_dst(dt,timeZone):
   aware_dt = timeZone.localize(dt)
   return aware_dt.dst() != datetime.timedelta(0,0)

timeZone = pytz.timezone("Europe/London")

dt = datetime.datetime(2019,8,2)
is_dst(dt,timeZone)
True

dt = datetime.datetime(2019,2,2)
is_dst(dt,timeZone)
False

扩展上面@Greg Hewgill 的答案,加上处理本地时区(在pip install tzlocal帮助下),您会得到:

import time
from datetime import datetime, timedelta
from tzlocal import get_localzone

def to_local(dt):
    """From any timezone to local datetime - also cope with DST"""
    localtime = time.localtime()
    if localtime.tm_isdst:
        utctime = time.gmtime()
        hours_delta = timedelta(hours=(localtime.tm_hour - utctime.tm_hour))
        dt = dt - hours_delta

    return dt.replace(tzinfo=get_localzone())

以上都没有帮助我,所以我找到了自己的解决方法。

我与https://gist.github.com/dpapathanasiou/09bd2885813038d7d3eb 中实现的逻辑有关,虽然仍然存在问题,但显然在现实生活中不起作用:(

目前我在以色列,这里我们在月底移动时钟,而在澳大利亚,他们已经移动了时钟。 对于Asia/JerusalemAustralia/Sydney所有代码都返回True

最终我使用了一个外部 3rd 方 API - https://worldtimeapi.org/ - 通过它我分析了utc_offset是否为 11 小时(而不是 10:05)。

from requests import get as Get

is_dst = True
try:
    tz_check = Get('https://worldtimeapi.org/api/timezone/Australia/Sydney')
    is_dst = tz_check.json().get('utc_offset') == '+11:00'
except Exception as e:
    print('!!! Error getting timezone', e)

我同意这是一个私人案例,但我希望这可以帮助某人:)

我会将此作为对上面@mehtunguh 的答案的评论发布,但我目前的声誉级别不允许我发表评论。

我认为省略dt参数时编写的is_dst函数可能存在问题。

dt参数被省略时, dt被设置为datetime.utcnow() ,它返回一个表示当前 UTC 时间的朴素日期时间。 当它传递给pytz.localize ,生成的本地化时间不是指定时区中的当前时间,而是与当前 UTC 时间具有相同小时、分钟、秒的本地时间。

因此,例如,当我写这篇文章时,它是美国/东部时区的美国东部标准时间上午 10:50,并且datetime.utcnow()返回一个带有hour=15minute=50的日期时间值。 正如所写的那样,当作为is_dst(timezone='US/Eastern')调用is_dst(timezone='US/Eastern')is_dst不会检查美国东部时间上午 10:50 的当前本地时间是否在夏令时期间,而是检查美国东部时间下午 3:50 是否在夏令时节约时间。

我认为is_dst也许应该编码如下:

import datetime
import pytz

def is_dst(dt=None, timezone='UTC'):
    timezone = pytz.timezone(timezone)
    if dt is None:
        dt = datetime.datetime.now(datetime.timezone.utc)
    if dt.tzinfo is None:
        tz_aware_dt = timezone.localize(dt, is_dst=None)
    else:
        tz_aware_dt = dt.astimezone(timezone)
    return tz_aware_dt.tzinfo._dst.seconds != 0

这个版本允许通过任何一个天真的日期时间值或时区感知日期时间值作为dt的说法。 dt参数被省略时,它使用当前 UTC 时间的时区感知版本,以便当它本地化到指定的时区时,它代表该时区中的当前时间。

我来自英国,这就是我处理服务器半年返回错误时间的方式:

import pytz
from typing import Optional
from datetime import datetime

class BritishTime(datetime):
    timezone = pytz.timezone('Europe/London')

    @classmethod
    def dst(cls, dt: Optional[datetime] = None):
        dt = dt if dt is not None else cls.now()
        return cls.timezone.dst(dt)

现在,如果我使用 BritishTime 创建日期时间BritishTime ,它具有dst方法,我可以使用它来检查和更新时间,如下所示:

def get_correct_time(timestamp):
    updated = BritishTime.fromtimestamp(timestamp)
    return updated + updated.dst()

效果很好。

在 Unix 中,如果您熟悉目标时区,另一种方法是简单地调用date命令:

import subprocess
# First Sun of 2022-03, 00:00
subprocess.run(['date', '--date=2022-03-10 12:00:00'], env={"TZ": "US/Eastern"})
# Next Sun, 00:00
subprocess.run(['date', '--date=2022-03-10 00:00:00'], env={"TZ": "US/Eastern"})
# Ditto, 12:00
subprocess.run(['date', '--date=2022-03-13 12:00:00'], env={"TZ": "US/Eastern"})

output:

Thu Mar 10 12:00:00 EST 2022
Sun Mar 13 00:00:00 EST 2022
Sun Mar 13 12:00:00 EDT 2022

EST用于非 DST 季节, EDT用于 DST。 无论如何,夏令时/非夏令时之间的过渡期真的很棘手

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM