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

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

酸洗的特殊要求:tzinfo子类必须有一个 __init__()可以不带参数调用的方法,否则它可以被酸洗但可能不能再次被取消酸洗。这是一项技术要求,未来可能会放宽。

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

tzinfo.utcoffsetdt 
返回本地时间与 UTC 的偏移量,作为timedeltaUTC 以东正值的对象。如果当地时间在 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()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 的变化。

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

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.tzinfoself,并且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.utcUTC时区实例的类。

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

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