You are here:  Home » Python » datetime.tzinfo基本日期和时间类型的tzinfo对象详解(24)Python语言(必读进阶学习教程)(参考资料)

datetime.tzinfo
这是一个抽象基类,这意味着不应该直接实例化此类。您需要派生一个具体的子类,并且(至少)提供您使用的tzinfo方法所需的标准方法的 实现datetime。该datetime模块提供的一个简单的具体子类tzinfotimezone,其可以代表与从UTC固定偏移,如UTC本身或北美EST和EDT时区。

(的具体子类)的实例tzinfo可以传递给构造函数datetimetime对象。后一个对象将其属性视为在本地时间,并且该tzinfo对象支持显示本地时间与UTC的偏移量,时区名称和DST偏移量的方法,所有这些都是相对于传递给它们的日期或时间对象。

酸洗的特殊要求:tzinfo子类必须有一个__init__()可以在没有参数的情况下调用的 方法,否则它可以被腌制但可能不再被打开。这是一项可能在将来放松的技术要求。

具体的子类tzinfo可能需要实现以下方法。究竟需要哪些方法取决于对知识datetime对象的使用 。如果有疑问,只需实施所有这些。

tzinfo.utcoffsetdt 
返回当地时间与UTC的偏移量,作为UTC timedelta正东方的对象。如果当地时间在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()不退货Nonedst()也不应退货 None

utcoffset()加注 的默认实现NotImplementedError

版本3.7中已更改: UTC偏移量不限于整数分钟。

tzinfo.dstdt 
返回夏令时(DST)调整,作为timedelta 对象或 None未知DST信息。timedelta(0)如果夏令时没有生效,则返回。如果DST有效,则将偏移量作为timedelta对象返回(utcoffset()有关详细信息,请参阅参考资料)。请注意,如果适用,DST偏移量已添加到返回的UTC偏移量中utcoffset(),因此dst()除非您有兴趣单独获取DST信息,否则无需咨询。例如,datetime.timetuple()调用其tzinfo 属性的dst()方法来确定tm_isdst应如何设置标志,并在跨越时区时tzinfo.fromutc()调用dst()帐户以更改DST。

在这个意义上,模拟标准时间和日光时间的子类的实例tztzinfo必须是一致的:

tz.utcoffset(dt) - tz.dst(dt)

必须返回相同的结果为每DT与 对于健全的子类,这个表达式产生的时区的“标准偏差”,它不应该依赖于日期或时间,但仅限于地理位置。执行 依赖于此,但无法检测违规行为; 程序员有责任确保它。如果子类不能保证这一点,它可能能够覆盖默认实现, 无论如何都能正常工作。datetime dt.tzinfo ==tztzinfodatetime.astimezone()tzinfotzinfo.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.tznamedt 
返回与datetime对象dt对应的时区名称,作为字符串。datetime模块没有定义任何关于字符串名称的内容,也没有要求它特别指出任何内容。例如,“GMT”,“UTC”,“ – 500”,“ – 5:00”,“EDT”,“美国/东方”,“美国/纽约”都是有效的回复。None如果字符串名称未知,则返回。请注意,这是一个方法而不是固定字符串,主要是因为某些tzinfo 子类希望根据传递的dt的特定值返回不同的名称,尤其是在tzinfo类占日光时间的情况下。

tzname()加注的默认实现NotImplementedError

这些方法由a datetimetimeobject调用,以响应它们的相同名称的方法。甲datetime对象本身传递作为参数,以及time对象传递None作为参数。一个tzinfo因此子类的方法应该准备接受DT的说法None,或一类datetime

如果None获得通过,它是由类的设计者来决定最好的回应。例如,None如果类希望说时间对象不参与tzinfo协议,则返回是合适的。utcoffset(None)返回标准UTC偏移量可能更有用,因为没有其他惯例可用于发现标准偏移量。

datetime响应datetime 方法传递对象时,dt.tzinfo该对象与self相同。 tzinfo除非用户代码tzinfo直接调用方法,否则方法可以依赖于此。目的是该tzinfo方法将dt解释为在本地时间,而不需要担心其他时区中的对象。

还有tzinfo一个子类可能希望覆盖的方法:

tzinfo.fromutcdt 
这是从默认datetime.astimezone() 实现中调用的。当从那里调用时,dt.tzinfoself,并且dt的日期和时间数据被视为表示UTC时间。目的fromutc()是调整日期和时间数据,在自己的当地时间返回一个等效的日期时间。

大多数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的挂号时间在当天没有任何意义,因此 在DST开始的那天astimezone(Eastern)不会提供结果。例如,在2016年的春季前进过渡期,我们得到了hour == 2

>>> 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时间在转换为Eastern时映射为1:MM,但较早时间将fold属性设置为0,稍后将其设置为1。例如,在2016年的秋季过渡期,我们得到了

>>> 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

 

请注意,在比较中datetime,仅由fold属性值不同的实例 被视为相等。

不能承受壁挂时间歧义的应用程序应该明确检查fold属性的值或避免使用混合 tzinfo子类; 使用时没有歧义timezone,或任何其他固定偏移tzinfo子类(例如仅代表EST(固定偏移-5小时)的类,或仅EDT(固定偏移-4小时))。

也可以看看

dateutil.tz

标准库具有timezone用于处理来自UTC的任意固定偏移和timezone.utcUTC时区实例的类。

dateutil.tz库将IANA时区数据库(也称为Olson数据库)引入Python,建议使用它。

IANA时区数据库
时区数据库(通常称为tz,tzdata或zoneinfo)包含代表全球许多代表性地点的当地时间历史的代码和数据。它会定期更新,以反映政治机构对时区边界,UTC偏移和夏令时规则所做的更改。