如何在 CEST 和 UTC 时区之间转换日期时间对象
How to convert a datetime object between CEST and UTC timezones
我不想使用 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 早 1 小时,夏令时早 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)
你应该仔细阅读tzinfo manual,因为那里有解释。
首先,dst()
通常 return 一个 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 是 date/datetime 对象的年份。
通常这种开启和关闭日期的逻辑要复杂得多:轮班仅在星期日晚上发生,有时在每月的最后一个星期日,并且在年份之间变化,有时在各州的主权之间变化(这些年来边界线也在发生变化),并且夏令时法和时区每隔几年就会发生变化(你好,俄罗斯)。
只是补充 .
要知道什么时候应该应用夏令时,你应该获取date/times夏令时开始和结束的历史数据,并检查是否必须应用到指定的datetime
.您可以从 IANA database or consult in many online sources, such as timeanddate website.
获取这些信息
另一个细节是 CET 是 ambiguous name, because it's used by more than one timezone. Real timezones names use the ones defined by IANA database(格式总是 Region/City
,如 Europe/Paris
或 Europe/Berlin
).
差距和重叠
我将以 Europe/Berlin
timezone 为例。今年(2017年),柏林夏令时开始于3月26日日:凌晨2点,时钟向前拨动1小时至凌晨3点,并且偏移量从 +01:00
更改为 +02:00
。
你也可以认为时钟从1:59AM直接跳到了凌晨3点,这意味着当天凌晨2点到2:59AM之间的所有本地时间都不存在,在那个时区。这称为间隙,您应该检查这种情况(一种常见的方法是将凌晨 2 点调整为下一个有效时间 - 在本例中为夏令时凌晨 3 点)。
在同一时区,2017 年夏令时将在 10 月 29 日日:凌晨 3 点,时钟将后移 1 小时到凌晨 2 点,偏移量将从 +02:00
变为 +01:00
。
这意味着凌晨 2 点到 2:59 AM 之间的所有本地时间将存在两次:一次是夏令时 (+02:00
) 一次是非夏令时 (+01:00
)。这称为重叠,在创建属于这种情况的 datetime
时,您必须决定选择创建哪一个( 应该是夏令时凌晨 2 点还是非夏令时偏移?你来决定!).
PS: 现在每个使用 CET 的欧洲国家都在不同的年份采用它,所以如果你要处理旧日期,你也必须考虑到这一点。
另一个细节是 DST 是由政府定义的,不能保证规则永远都是这样,你必须相应地更新规则 - 使用 pytz
的另一个好处,因为它的更新是从 IANA 发布和 published in PyPI (thanks @Matt Johnson for ).
生成的
您确定文书工作比手工编写这些规则更糟糕吗?只需检查 how to read IANA tz files,也许您会改变主意。
我不想使用 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 早 1 小时,夏令时早 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)
你应该仔细阅读tzinfo manual,因为那里有解释。
首先,dst()
通常 return 一个 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 是 date/datetime 对象的年份。
通常这种开启和关闭日期的逻辑要复杂得多:轮班仅在星期日晚上发生,有时在每月的最后一个星期日,并且在年份之间变化,有时在各州的主权之间变化(这些年来边界线也在发生变化),并且夏令时法和时区每隔几年就会发生变化(你好,俄罗斯)。
只是补充
要知道什么时候应该应用夏令时,你应该获取date/times夏令时开始和结束的历史数据,并检查是否必须应用到指定的datetime
.您可以从 IANA database or consult in many online sources, such as timeanddate website.
另一个细节是 CET 是 ambiguous name, because it's used by more than one timezone. Real timezones names use the ones defined by IANA database(格式总是 Region/City
,如 Europe/Paris
或 Europe/Berlin
).
差距和重叠
我将以 Europe/Berlin
timezone 为例。今年(2017年),柏林夏令时开始于3月26日日:凌晨2点,时钟向前拨动1小时至凌晨3点,并且偏移量从 +01:00
更改为 +02:00
。
你也可以认为时钟从1:59AM直接跳到了凌晨3点,这意味着当天凌晨2点到2:59AM之间的所有本地时间都不存在,在那个时区。这称为间隙,您应该检查这种情况(一种常见的方法是将凌晨 2 点调整为下一个有效时间 - 在本例中为夏令时凌晨 3 点)。
在同一时区,2017 年夏令时将在 10 月 29 日日:凌晨 3 点,时钟将后移 1 小时到凌晨 2 点,偏移量将从 +02:00
变为 +01:00
。
这意味着凌晨 2 点到 2:59 AM 之间的所有本地时间将存在两次:一次是夏令时 (+02:00
) 一次是非夏令时 (+01:00
)。这称为重叠,在创建属于这种情况的 datetime
时,您必须决定选择创建哪一个( 应该是夏令时凌晨 2 点还是非夏令时偏移?你来决定!).
PS: 现在每个使用 CET 的欧洲国家都在不同的年份采用它,所以如果你要处理旧日期,你也必须考虑到这一点。
另一个细节是 DST 是由政府定义的,不能保证规则永远都是这样,你必须相应地更新规则 - 使用 pytz
的另一个好处,因为它的更新是从 IANA 发布和 published in PyPI (thanks @Matt Johnson for
您确定文书工作比手工编写这些规则更糟糕吗?只需检查 how to read IANA tz files,也许您会改变主意。