2017-10-19 585 views
3

我不想使用pytz库作为我正在处理的项目需要文书工作来引入依赖关系。如果我能在没有第三方图书馆的情况下实现这一目标,我会更开心。如何转换CEST和UTC时区之间的日期时间对象

当日期处于夏时制时,我无法在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时间到达8:30 UTC。

功能是:

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) 

回答

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日开始:凌晨2点,时钟转移1小时转发至凌晨3点,偏移量从+01:00变为+02:00

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

在同一时区,在2017年,DST会在10月29日第结束:在上午03时,时钟将转向背面 1小时至2 AM,和偏移将改变从+02:00+01:00

这意味着所有当地时间凌晨2点到上午2:59之间将存在两次:一次在DST(+02:00)和一次在非DST(+01:00)。这就是所谓的重叠,当创建一个属于这种情况的datetime时,您必须决定选择哪一个创建(应该是DST中的上午2点还是非DST偏移量?您决定!)。


PS:使用CET在不同的年份现在通过了,所以如果你处理旧的约会,你必须考虑过每个欧洲国家。

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

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

+2

为了回答您关于pytz更新的观点,它们是从IANA版本生成的。 IANA 2017b => pytz 2017.2。斯图亚特主教维护他们,并将它们发布到pypi这里:https://pypi.python.org/pypi/pytz#downloads –

+0

@MattJohnson我已经更新了这个信息的答案,非常感谢! – 2017-10-19 18:22:12

+1

您和Sergey的答案都非常有帮助。我认为你已经做了最多的事情来说服我,我真的应该把它交给专家。我们的解决方案是要求UTC时间作为输入,然后我们不需要转换(c: –

3

您应仔细阅读tzinfo manual,因为它是有解释的。

首先,dst()通常应该返回一个timedelta(0)timedelta(hours=1),从未2小时或更长时间,并且很少在之间(例如30分钟)的东西。这只是相对于正常TZ偏移的dst偏移,而不是dst模式中的完整TZ偏移。

其次,utcoffset()必须包含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是日期/ DateTime对象的年份。

通常情况下,关闭日期的这种逻辑要复杂得多:这些转换仅在周日晚上发生,有时在本月的最后一个星期日发生,并且在不同年份之间有所不同,有时在各州主权之间边界线也会随着时间的推移而变化),并且夏季法律和时区划分每隔几年就会发生一次(您好,俄罗斯)。

+3

实际上,您还应该检查当天的时间 - 在中欧,夏令时的变化通常发生在凌晨2点到凌晨3点之间,因此检查日期是不够准确的。无论如何,只要日子发生变化,我就会立即投票(我今天已达到我的投票限制)。 – 2017-10-19 18:01:28

+0

目前,春天凌晨2点,秋天凌晨3点。 https://www.timeanddate.com/time/change/france/paris –

+1

@Hugo你是对的。感谢您补充我的答案。然而,即使你所描述的只是冰山一角。我故意只回答了关于python和'datetime.tzinfo'部分的问题,这个问题的作者似乎是个问题。虽然提到实际的tz计算是一个非常困难和大的话题。因为它太大了,人们可以纯粹根据日期和时间(不是开玩笑)的适当工作来写一本书。 –

相关问题