将时区信息传递给无时区信息的Python日期时间对象,不使用pytz库。

21

我在Python中处理日期和时区的问题已经挣扎了很长时间,希望有人可以帮我一下。

基本上,我想在UTC和考虑DST更改的情况下进行转换。

我从Python教程中创建了以下tzinfo类(我知道它不是100%准确的,但它不需要):

from datetime import tzinfo, timedelta, datetime

ZERO = timedelta(0)
HOUR = timedelta(hours=1)

def first_sunday_on_or_after(dt):
    days_to_go = 6 - dt.weekday()
    if days_to_go:
        dt += timedelta(days_to_go)
    return dt

DSTSTART_2007 = datetime(1, 3, 8, 2)
DSTEND_2007 = datetime(1, 11, 1, 1)
DSTSTART_1987_2006 = datetime(1, 4, 1, 2)
DSTEND_1987_2006 = datetime(1, 10, 25, 1)
DSTSTART_1967_1986 = datetime(1, 4, 24, 2)
DSTEND_1967_1986 = DSTEND_1987_2006

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

        # Find start and end times for US DST. For years before 1967, return
        # ZERO for no DST.
        if 2006 < dt.year:
            dststart, dstend = DSTSTART_2007, DSTEND_2007
        elif 1986 < dt.year < 2007:
            dststart, dstend = DSTSTART_1987_2006, DSTEND_1987_2006
        elif 1966 < dt.year < 1987:
            dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986
        else:
            return ZERO

        start = first_sunday_on_or_after(dststart.replace(year=dt.year))
        end = first_sunday_on_or_after(dstend.replace(year=dt.year))

        # Can't compare naive to aware objects, so strip the timezone from
        # dt first.
        if start <= dt.replace(tzinfo=None) < end:
            return HOUR
        else:
            return ZERO

另一方面,我有一个在东部标准时间的任意date对象,并且我想知道它们之间相差的小时数,考虑到夏令时。

我尝试过类似以下的方法:

>>> Eastern = ustimezone.USTimeZone(-5, "Eastern",  "EST", "EDT")
>>> x = datetime.date.today() # I actually get an arbitrary date but this is for the example
>>> x_dt = datetime.datetime.combine(x, datetime.time())
>>> x_dt_tz = x_dt.astimezone(Eastern)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime

我看到多篇文章都提到了使用pytz模块中的localize函数,但不幸的是,我无法使用额外的模块,因此无法使用pyzt

有没有人知道如何将这个本地时间对象转换为带时区的对象而不使用pytz


1
x_dt.replace(tzinfo=Eastern) 可能是这样的吗?从阅读:http://groups.google.com/group/google-appengine/browse_thread/thread/17add9b3070aad7e/b995fe7e1f16cd4e - Skylar Saveland
1
@skyl 太完美了,x_dt.replace(tzinfo=Eastern).utcoffset() 返回 datetime.timedelta(-1, 72000),对应于 -4 小时!这看起来对于如此简单的事情来说有些复杂,但感谢您的帮助,您应该将其发布为答案,我会接受它。 - Charles Menguy
处理夏令时而不使用pytz就像处理没有Unicode的多种语言一样。虽然可以做到,但在许多情况下结果都会出错。 - jfs
2个回答

15

说句实话,@skyl提供的答案与pytz的做法基本相同。

这是相关的pytz源码。 它只是在datetime对象上使用tzinfo参数调用replace函数:

def localize(self, dt, is_dst=False):
    '''Convert naive time to local time'''
    if dt.tzinfo is not None:
        raise ValueError('Not naive datetime (tzinfo is already set)')
    return dt.replace(tzinfo=self)

1
@CharlesMenguy:不,这不是pytz为具有DST的时区所做的事情(“我想在UTC中进行转换并考虑DST更改。”)。您只能使用.replace()与固定偏移时区(例如UTC)一起使用(从一开始到最后相同的UTC偏移量)。 - jfs

13

使用 x_dt.replace(tzinfo=Eastern) (从此Google Groups线程中找到)。

x_dt.replace(tzinfo=Eastern).utcoffset() 返回 datetime.timedelta(-1, 72000),对应于-4小时!(来自问题的评论)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接