In case it's of interest: http://www.twinsun.com/tz/tz-link.htm David LeBlanc Seattle, WA USA
-----Original Message----- From: python-dev-admin@python.org [mailto:python-dev-admin@python.org]On Behalf Of Tim Peters Sent: Friday, January 03, 2003 22:44 To: Shane Hathaway Cc: zope3-dev@zope.org; PythonDev Subject: RE: [Zope3-dev] Re: [Python-Dev] Holes in time
[Shane Hathaway]
... That sounds perfectly reasonable, but may I suggest moving the assumption by changing the interface of the tzinfo class. The utcoffset() method leads one to naively assume that functions f and g can both depend reliably on utcoffset(). Instead, tzinfo might have two methods, to_local(utc_date) and to_utc(local_date). That way, the tzinfo object encapsulates the madness.
I think we may need from_utc() before this is over, but that most people won't have any need for it. In the other direction, it's already the tzinfo subclass author's responsibility to ensure that the current:
d - d.utcoffset()
yields exactly the same date and time members as would the hypothesized:
d.to_utc()
One downside is that then you can't expect normal programmers to write a correct tzinfo based on the C libraries. They'll never get it right. :-) It would have to be supplied with Python.
I doubt the latter will happen, and it certainly won't happen for 2.3.
The current scheme has actually become about as easy as it can become. From the next iteration of the docs, here's a full implementation of a class for DST-aware major US time zones (using the rules that have been in effect for more than a decade):
""" 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
# In the US, DST starts at 2am (standard time) on the first Sunday in # April. DSTSTART = datetime(1, 4, 1, 2) # and ends at 2am (DST time; 1am standard time) on the last Sunday # of October, which is the first Sunday on or after Oct 25. DSTEND = datetime(1, 10, 25, 2)
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 astimezone() # implementation always passes a datetimetz with # dt.tzinfo == self. return ZERO assert dt.tzinfo is self
# Find first Sunday in April & the last in October. 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
Eastern = USTimeZone(-5, "Eastern", "EST", "EDT") Central = USTimeZone(-6, "Central", "CST", "CDT") Mountain = USTimeZone(-7, "Mountain", "MST", "MDT") Pacific = USTimeZone(-8, "Pacific", "PST", "PDT") """
The test suite beats the snot out of this class, and .astimezone() behaves exactly as we've talked about here in all cases now, whether Eastern or Pacific (etc) are source zones or target zones or both. But the coding is really quite simple, doing nothing more nor less than implementing "the plain rules". (BTW, note that no use is made of the platform C time functions here)
A similar class for European rules can be found in EU.py in the Python datetime sandbox, and is just as straightforward (relative to the complexity inherent in those rules).
Because the only strong assumption astimezone() makes is that
tz.utcoffset(d) - tz.dst(d) # tz's "standard offset"
is invariant wrt d, it should work fine for tzinfo subclasses that want to use different switch points in different years, or have multiple DST periods in a year (including none at all in some years), etc. So long as a time zone's "standard offset" depends only on a location's longitude, astimezone() is very likely to do the right thing no matter how goofy the rest of the zone is.
So, at the moment, I don't have an actual use case in hand anymore that requires a from_utc() method. astimezone() could be written in terms of it, though:
def astimezone(self, tz): self -= self.utcoffset() # as UTC other = self.replace(tzinfo=tz) return other.from_utc()
and the tzinfo base class could supply a default from_utc() method capturing the current astimezone() implementation. Then we'd have a powerful hook tzinfo subclasses could override -- but I'm not sure anyone will find a need to!
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev