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



我不想使用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提前一个小时,在夏季则提前两个小时。所以我预计仲夏中欧的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)

只是对@Sergey的回答的补充。

要知道何时应该应用夏令时,您应该获取夏令时开始和结束的日期/时间的历史数据,并检查是否必须将其应用于指定的datetime。您可以从IANA数据库中获取这些信息,也可以在许多在线资源中查阅,如时间和日期网站。

另一个细节是CET是一个不明确的名称,因为它被多个时区使用。实时区域名称使用IANA数据库定义的名称(始终采用Region/City格式,如Europe/ParisEurope/Berlin)。


间隙和重叠

我将以Europe/Berlin时区为例。今年(2017年),柏林夏令时于3月26日开始:凌晨2点,时钟向前移动1小时至凌晨3点,偏移量从+01:00变为+02:00

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

在同一时区,2017年夏令时将于10月29日结束:凌晨3点,时钟将向后移动1小时至凌晨2点,偏移量将从+02:00变为+01:00

这意味着凌晨2点到凌晨2点59分之间的所有本地时间将存在两次:一次在夏令时(+02:00),一次在非夏令时(+01:00)。这被称为重叠,在这种情况下创建datetime时,您必须决定要创建哪一个(应该是夏令时中的凌晨2点还是非夏令时偏移?由您决定!)。


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

另一个细节是,DST是由政府定义的,不能保证规则会永远这样,你必须相应地更新规则——使用pytz的另一个优点是,它的更新是从IANA发布的版本中生成的,并在PyPI中发布(感谢@Matt Johnson提供此信息)。

你确定文书工作比手工编码这些规则更糟糕吗?只要检查一下如何阅读IANA tz文件,也许你会改变主意。

您应该仔细阅读tzinfo手册,正如那里所解释的那样。

首先,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&休息日期要复杂得多:这些变化只发生在周日晚上,有时发生在一个月的最后一个周日,而且随着年份的不同而变化,有时发生国家主权的变化(边界线也会随着年份的变化而变化),夏令时和时区每隔几年就会变化一次(你好,俄罗斯)。

相关内容

  • 没有找到相关文章

最新更新