datetime.tzinfo基本日期和时间类型的tzinfo类详解 – (24) Python语言(必读进阶学习教程)(参考资料)
- class
datetime.
tzinfo
- 这是一个抽象基类,这意味着不应直接实例化此类。您需要派生一个具体的子类,并且(至少)提供您使用
tzinfo
的方法所需的标准方法的 实现。datetime
该datetime
模块提供了一个简单的具体子类tzinfo
,timezone
,它可以表示与 UTC 有固定偏移的时区,例如 UTC 本身或北美 EST 和 EDT。(的具体子类)的实例
tzinfo
可以传递给datetime
和time
对象的构造函数。后者对象将它们的属性视为本地时间,并且该tzinfo
对象支持显示本地时间与 UTC 的偏移量、时区名称和 DST 偏移量的方法,所有这些都与传递给它们的日期或时间对象相关。酸洗的特殊要求:
tzinfo
子类必须有一个__init__()
可以不带参数调用的方法,否则它可以被酸洗但可能不能再次被取消酸洗。这是一项技术要求,未来可能会放宽。一个具体的子类
tzinfo
可能需要实现以下方法。究竟需要哪些方法取决于感知datetime
对象的用途。如果有疑问,只需实施所有这些。 tzinfo.
utcoffset
( dt )- 返回本地时间与 UTC 的偏移量,作为
timedelta
UTC 以东正值的对象。如果当地时间在 UTC 以西,这应该是负数。请注意,这是与 UTC 的总偏移量;例如,如果一个tzinfo
对象同时代表时区和 DST 调整,utcoffset()
则应返回它们的总和。如果 UTC 偏移量未知,则返回None
. 否则返回的值必须是timedelta
严格介于-timedelta(hours=24)
和 之间的对象timedelta(hours=24)
(偏移量的大小必须小于一天)。大多数实现utcoffset()
可能看起来像这两个之一:
-
return CONSTANT # fixed-offset class return CONSTANT + self.dst(dt) # daylight-aware class
- 如果
utcoffset()
不返回None
,dst()
也不应该返回None
。utcoffset()
raises 的默认实现NotImplementedError
。在 3.7 版更改: UTC 偏移量不限于整数分钟。
tzinfo.
dst(dt)
- 返回夏令时 (DST) 调整,作为
timedelta
对象或None
如果 DST 信息未知。timedelta(0)
如果 DST 无效,则返回。如果 DST 生效,则将偏移量作为timedelta
对象返回(详情请参阅utcoffset()
)。请注意,DST 偏移量(如果适用)已添加到由 返回的 UTC 偏移量中,因此除非您有兴趣单独获取 DST 信息,否则utcoffset()
无需咨询。dst()
例如,datetime.timetuple()
调用其tzinfo
属性的dst()
方法来确定tm_isdst
应如何设置标志,并tzinfo.fromutc()
调用dst()
以说明跨越时区时 DST 的变化。模拟标准时间和白天时间的子类的实例tz
tzinfo
在这个意义上必须是一致的:tz.utcoffset(dt) - tz.dst(dt)
必须为每个日期时间 dt 返回相同的结果,其中 dt.tzinfo == tz 对于理智的 tzinfo 子类,此表达式产生时区的“标准偏移量”,它不应该依赖于日期或时间,而只依赖于地理位置。 datetime.astimezone() 的实现依赖于此,但无法检测违规; 确保它是程序员的责任。 如果 tzinfo 子类不能保证这一点,它可能能够覆盖 tzinfo.fromutc() 的默认实现,以便与 astimezone() 正常工作。
大多数实现
dst()
可能看起来像这两个之一:
-
def dst(self, dt): # a fixed-offset class: doesn't account for DST return timedelta(0)
要么
def dst(self, dt): # Code to set dston and dstoff to the time zone's DST # transition times based on the input dt.year, and expressed # in standard local time. Then if dston <= dt.replace(tzinfo=None) < dstoff: return timedelta(hours=1) else: return timedelta(0)
dst()
加注的默认实现NotImplementedError
。在版本3.7中更改: DST偏移不限于整数分钟。
tzinfo.
tzname(dt)
datetime
以字符串形式返回对象dt对应的时区名称。模块没有定义任何关于字符串名称的内容datetime
,也没有要求它有任何特别的含义。例如,“GMT”、“UTC”、“-500”、“-5:00”、“EDT”、“US/Eastern”、“America/New York”都是有效回复。None
如果字符串名称未知,则返回。请注意,这是一种方法而不是固定字符串,主要是因为某些tzinfo
子类希望根据传递的dt的特定值返回不同的名称,尤其是在tzinfo
该类考虑白天时间的情况下。tzname()
raises的默认实现NotImplementedError
。
这些方法由日期时间或时间对象调用,以响应它们的同名方法。 datetime 对象将自身作为参数传递,而 time 对象将 None 作为参数传递。 因此,一个 tzinfo 子类的方法应该准备好接受 None 或类 datetime 的 dt 参数。
当 None 被传递时,由类设计器决定最佳响应。 例如,如果类希望说时间对象不参与 tzinfo 协议,则返回 None 是合适的。 utcoffset(None) 返回标准 UTC 偏移量可能更有用,因为没有其他约定来发现标准偏移量。
当一个 datetime 对象被传递以响应 datetime 方法时,dt.tzinfo 与 self 是同一个对象。 tzinfo 方法可以依赖于此,除非用户代码直接调用 tzinfo 方法。 目的是 tzinfo 方法将 dt 解释为本地时间,而不需要担心其他时区的对象。
还有一个子类可能希望覆盖的 tzinfo 方法:
tzinfo.
fromutc(dt)
- 这是从默认
datetime.astimezone()
实现中调用的。从那里调用时,dt.tzinfo
是self,并且dt的日期和时间数据将被视为表示 UTC 时间。的目的fromutc()
是调整日期和时间数据,返回一个等效于self本地时间的日期时间。大多数 tzinfo 子类应该能够毫无问题地继承默认的 fromutc() 实现。 它足以处理固定偏移时区,以及同时考虑标准时间和日光时间的时区,即使 DST 转换时间在不同年份有所不同。 默认 fromutc() 实现可能无法在所有情况下正确处理的时区示例是标准偏移量(来自 UTC)取决于经过的特定日期和时间,这可能出于政治原因而发生。 如果结果是跨越标准偏移量发生变化的时间之一,那么 astimezone() 和 fromutc() 的默认实现可能不会产生您想要的结果。
跳过错误情况的代码,默认
fromutc()
实现的行为如下:
-
def fromutc(self, dt): # raise ValueError error if dt.tzinfo is not self dtoff = dt.utcoffset() dtdst = dt.dst() # raise ValueError if dtoff is None or dtdst is None delta = dtoff - dtdst # this is self's standard offset if delta: dt += delta # convert to standard local time dtdst = dt.dst() # raise ValueError if dtdst is None if dtdst: return dt + dtdst else: return dt
在以下tzinfo_examples.py
文件中有一些tzinfo
类的示例 :
from datetime import tzinfo, timedelta, datetime
ZERO = timedelta(0)
HOUR = timedelta(hours=1)
SECOND = timedelta(seconds=1)
# A class capturing the platform's idea of local time.
# (May result in wrong values on historical times in
# timezones where UTC offset and/or the DST rules had
# changed in the past.)
import time as _time
STDOFFSET = timedelta(seconds = -_time.timezone)
if _time.daylight:
DSTOFFSET = timedelta(seconds = -_time.altzone)
else:
DSTOFFSET = STDOFFSET
DSTDIFF = DSTOFFSET - STDOFFSET
class LocalTimezone(tzinfo):
def fromutc(self, dt):
assert dt.tzinfo is
self stamp = (dt - datetime(1970, 1, 1, tzinfo=self)) // SECOND
args = _time.localtime(stamp)[:6]
dst_diff = DSTDIFF // SECOND
# Detect fold
fold = (args == _time.localtime(stamp - dst_diff))
return datetime(*args, microsecond=dt.microsecond, tzinfo=self, fold=fold)
def utcoffset(self, dt):
if self._isdst(dt):
return DSTOFFSET
else:
return STDOFFSET
def dst(self, dt):
if self._isdst(dt):
return DSTDIFF
else:
return ZERO
def tzname(self, dt):
return _time.tzname[self._isdst(dt)]
def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), 0, 0)
stamp = _time.mktime(tt)
tt = _time.localtime(stamp)
return tt.tm_isdst > 0
Local = LocalTimezone()
# A complete implementation of current DST rules for major US time zones.
def first_sunday_on_or_after(dt):
days_to_go = 6 - dt.weekday()
if days_to_go:
dt += timedelta(days_to_go)
return dt
# US DST Rules
#
# This is a simplified (i.e., wrong for a few cases) set of rules for US
# DST start and end times. For a complete and up-to-date set of DST rules
# and timezone definitions, visit the Olson Database (or try pytz):
# http://www.twinsun.com/tz/tz-link.htm # http://sourceforge.net/projects/pytz/ (might not be up-to-date)
#
# In the US, since 2007, DST starts at 2am (standard time) on the second
# Sunday in March, which is the first Sunday on or after Mar 8.
DSTSTART_2007 = datetime(1, 3, 8, 2)
# and ends at 2am (DST time) on the first Sunday of Nov.
DSTEND_2007 = datetime(1, 11, 1, 2)
# From 1987 to 2006, DST used to start at 2am (standard time) on the first
# Sunday in April and to end at 2am (DST time) on the last
# Sunday of October, which is the first Sunday on or after Oct 25.
DSTSTART_1987_2006 = datetime(1, 4, 1, 2)
DSTEND_1987_2006 = datetime(1, 10, 25, 2)
# From 1967 to 1986, DST used to start at 2am (standard time) on the last
# Sunday in April (the one on or after April 24) and to end at 2am (DST time)
# on the last Sunday of October, which is the first Sunday
# on or after Oct 25.
DSTSTART_1967_1986 = datetime(1, 4, 24, 2)
DSTEND_1967_1986 = DSTEND_1987_2006
def us_dst_range(year):
# Find start and end times for US DST. For years before 1967, return
# start = end for no DST.
if 2006 < year:
dststart, dstend = DSTSTART_2007, DSTEND_2007
elif 1986 < year < 2007:
dststart, dstend = DSTSTART_1987_2006, DSTEND_1987_2006
elif 1966 < year < 1987:
dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986
else:
return (datetime(year, 1, 1), ) * 2
start = first_sunday_on_or_after(dststart.replace(year=year))
end = first_sunday_on_or_after(dstend.replace(year=year))
return start, end
class USTimeZone(tzinfo):
def __init__(self, hours, reprname, stdname, dstname):
self.stdoffset = timedelta(hours=hours)
self.reprname = reprname
self.stdname = stdname
self.dstname = dstname
def __repr__(self):
return self.reprname
def tzname(self, dt):
if self.dst(dt):
return self.dstname
else:
return self.stdname
def utcoffset(self, dt):
return self.stdoffset + self.dst(dt)
def dst(self, dt):
if dt is None or dt.tzinfo is None:
# An exception may be sensible here, in one or both cases.
# It depends on how you want to treat them. The default
# fromutc() implementation (called by the default astimezone()
# implementation) passes a datetime with dt.tzinfo is self.
return ZERO
assert dt.tzinfo is self
start, end = us_dst_range(dt.year)
# Can't compare naive to aware objects, so strip the timezone from
# dt first.
dt = dt.replace(tzinfo=None)
if start + HOUR <= dt < end - HOUR:
# DST is in effect.
return HOUR
if end - HOUR <= dt < end:
# Fold (an ambiguous hour): use dt.fold to disambiguate.
return ZERO if dt.fold else HOUR
if start <= dt < start + HOUR:
# Gap (a non-existent hour): reverse the fold rule.
return HOUR if dt.fold else ZERO
# DST is off.
return ZERO
def fromutc(self, dt):
assert dt.tzinfo is self
start, end = us_dst_range(dt.year)
start = start.replace(tzinfo=self)
end = end.replace(tzinfo=self)
std_time = dt + self.stdoffset
dst_time = std_time + HOUR
if end <= dst_time < end + HOUR:
# Repeated hour
return std_time.replace(fold=1)
if std_time < start or dst_time >= end:
# Standard time
return std_time
if start <= std_time < end - HOUR:
# Daylight saving time
return dst_time
Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
Central = USTimeZone(-6, "Central", "CST", "CDT")
Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
请注意tzinfo
,在DST过渡点,在标准和日光时间的子类中,每年有两次不可避免的微妙之处。具体情况,请考虑美国东部航空(UTC -0500),其中EDT在3月的第二个星期日1:59(美国东部时间)后的一分钟开始,并在11月的第一个星期日1:59(美国东部时间)结束时分钟:
UTC 3:MM 4:MM 5:MM 6:MM 7:MM 8:MM
EST 22:MM 23:MM 0:MM 1:MM 2:MM 3:MM
EDT 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
start 22:MM 23:MM 0:MM 1:MM 3:MM 4:MM
end 23:MM 0:MM 1:MM 1:MM 2:MM 3:MM
当 DST 开始时(“开始”行),本地挂钟从 1:59 跳到 3:00。 2:MM 形式的挂起时间在那天实际上没有意义,因此 astimezone(Eastern) 在 DST 开始的那一天不会提供 hour == 2 的结果。 例如,在 2016 年的 Spring 前向过渡中,我们得到
>>> from datetime import datetime, timezone
>>> from tzinfo_examples import HOUR, Eastern
>>> u0 = datetime(2016, 3, 13, 5, tzinfo=timezone.utc)
>>> for i in range(4):
... u = u0 + i*HOUR
... t = u.astimezone(Eastern)
... print(u.time(), 'UTC =', t.time(), t.tzname())
...
05:00:00 UTC = 00:00:00 EST
06:00:00 UTC = 01:00:00 EST
07:00:00 UTC = 03:00:00 EDT
08:00:00 UTC = 04:00:00 EDT
当 DST 结束时(“结束”行),可能会出现更严重的问题:在本地挂钟时间中无法明确拼写一个小时:白天的最后一小时。 在东部,这是夏令时结束当天 5:MM UTC 形式的时间。 本地挂钟再次从 1:59(夏令时)跳回 1:00(标准时间)。 1:MM 格式的当地时间是不明确的。 astimezone() 通过将两个相邻的 UTC 小时映射到同一个本地小时来模仿本地时钟的行为。 在东部示例中,格式为 5:MM 和 6:MM 的 UTC 时间在转换为东部时都映射为 1:MM,但较早的时间将 fold 属性设置为 0,而较晚的时间将其设置为 1。例如 ,在 2016 年的 Fall back 过渡中,我们得到
>>> u0 = datetime(2016, 11, 6, 4, tzinfo=timezone.utc)
>>> for i in range(4):
... u = u0 + i*HOUR
... t = u.astimezone(Eastern)
... print(u.time(), 'UTC =', t.time(), t.tzname(), t.fold)
...
04:00:00 UTC = 00:00:00 EDT 0
05:00:00 UTC = 01:00:00 EDT 0
06:00:00 UTC = 01:00:00 EST 1
07:00:00 UTC = 02:00:00 EST 0
请注意,仅通过 fold 属性的值不同的日期时间实例在比较中被认为是相等的。
不能承受 wall-time 歧义的应用程序应该明确检查 fold 属性的值或避免使用混合 tzinfo 子类; 使用时区或任何其他固定偏移量 tzinfo 子类(例如仅表示 EST(固定偏移量 -5 小时)或仅 EDT(固定偏移量 -4 小时)的类)时没有歧义。
也可以看看
- dateutil.tz
-
标准库具有
timezone
用于处理来自UTC的任意固定偏移和timezone.utc
UTC时区实例的类。dateutil.tz库将IANA时区数据库(也称为Olson数据库)引入Python,建议使用它。
- IANA时区数据库
- 时区数据库(通常称为tz,tzdata或zoneinfo)包含代表全球许多代表性地点的当地时间历史的代码和数据。它会定期更新,以反映政治机构对时区边界,UTC偏移和夏令时规则所做的更改。