如何在CEST和UTC时区之间转换日期时间对象

时间:2017-10-19 16:04:07

标签: python datetime timezone dst

我不想使用pytz库作为我正在处理的项目需要文书工作来介绍依赖项。如果我能在没有第三方图书馆的情况下实现这一目标,我会更开心。

当日期为夏令时,我在转换CET和UTC之间的日期时遇到问题。这与我的期望不同一小时:

>>> print from_cet_to_utc(year=2017, month=7, day=24, hour=10, minute=30)
2017-07-24T09:30Z

2017-07-24T08:30Z  # expected

CET比UTC提前一小时,夏季提前2小时。所以我希望在仲夏的中欧10:30到达UTC时间的8:30。

功能是:

from datetime import datetime, tzinfo, timedelta

def from_cet_to_utc(year, month, day, hour, minute):
    cet = datetime(year, month, day, hour, minute, tzinfo=CET())
    utc = cet.astimezone(tz=UTC())
    return '{:%Y-%m-%d:T%H:%MZ}'.format(utc)

我使用两个时区信息对象:

class CET(tzinfo):
    def utcoffset(self, dt):
        return timedelta(hours=1)

    def dst(self, dt):
        return timedelta(hours=2)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return timedelta(0)

    def dst(self, dt):
        return timedelta(0)

2 个答案:

答案 0 :(得分:3)

你应该仔细阅读tzinfo manual,因为这里有解释。

首先,dst()通常应该返回timedelta(0)timedelta(hours=1),而不是2小时或更长时间,而且很少介于两者之间(例如30分钟)。这只是相对于正常TZ偏移的dst偏移,而不是dst模式下的完整TZ偏移。

其次,utcoffset()必须已包含dst更正。在手册中描述的某些情况下也会使用dst()方法(例如,如果dst是否生效,则用于检测),但除非您这样做,否则它不会自动应用于TZ偏移。

您的代码应如下所示:

from datetime import datetime, tzinfo, timedelta

class CET(tzinfo):
    def utcoffset(self, dt):
        return timedelta(hours=1) + self.dst(dt)

    def dst(self, dt):
        dston = datetime(year=dt.year, month=3, day=20)
        dstoff = datetime(year=dt.year, month=10, day=20)
        if dston <= dt.replace(tzinfo=None) < dstoff:
            return timedelta(hours=1)
        else:
            return timedelta(0)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return timedelta(0)

    def dst(self, dt):
        return timedelta(0)

def from_cet_to_utc(year, month, day, hour, minute):
    cet = datetime(year, month, day, hour, minute, tzinfo=CET())
    utc = cet.astimezone(tz=UTC())
    return '{:%Y-%m-%d:T%H:%MZ}'.format(utc)


print from_cet_to_utc(year=2017, month=7, day=24, hour=10, minute=30)
# 2017-07-24:T08:30Z

在这里,我勇敢地假设DST生效20.03.YYYY - 19.10.YYYY包含,其中YYYY是日期/日期时间对象的年份。

通常这个on&amp; amp;关闭日期要复杂得多:转变发生在周日晚上,有时是在本月的最后一个星期日,并且在多年之间变化,有时在各州的主权之间变化(边界线也在多年来变化),以及夏令时法则和时间变化每隔几年就会发生变化(你好,俄罗斯)。

答案 1 :(得分:3)

只是补充@Sergey's answer

要知道何时应该应用DST,您应该获取DST开始和结束时的日期/时间的历史数据,并检查是否必须将其应用于指定的datetime。您可以从IANA database获取这些内容,也可以参考许多在线来源,例如timeanddate website

另一个细节是 CET ambiguous name,因为它使用by more than one timezone。实时区域名称使用the ones defined by IANA database(始终采用Region/City格式,例如Europe/ParisEurope/Berlin

差距和重叠

我将以Europe/Berlin timezone为例。在今年(2017年),柏林的DST于3月26日 th 开始:凌晨2点,时钟向前移动1小时向前,并且偏移从{{{ 1}}到+01:00

您还可以认为时钟从凌晨1:59直接跳到凌晨3点,这意味着当天在凌晨2点到凌晨2点59分之间的所有当地时间都不存在。这称为间隙,您应该检查这种情况(一种常见的方法是将凌晨2点调整到下一个有效小时 - 在这种情况下,在夏令时凌晨3点)。

在同一时区,2017年DST将于10月29日 th 结束:凌晨3点,时钟将返回 1小时至凌晨2点,偏移量将从+02:00更改为+02:00

这意味着凌晨2点到凌晨2点59分之间的所有当地时间将存在两次:一次在DST(+01:00),一次在非DST(+02:00)。这称为重叠,在创建属于这种情况的+01:00时,您必须决定选择创建哪一个(如果它在DST中是2 AM还是非-DST offset?你决定了!)。

PS:现在每个使用CET的欧洲国家都会在不同的年份采用它,所以如果你要处理旧日期,你也必须考虑它。

另一个细节是DST是由政府定义的,并且不能保证规则将永远这样,并且您必须相应地更新规则 - 使用{{1 },因为其更新是从IANA版本和published in PyPI生成的(感谢@Matt Johnson pointing this info)。

你确定文书工作比手工编写这些规则更糟吗?只需查看how to read IANA tz files,也许您就会改变主意。