Log message for revision 80907: Merged amos-datetime-pytz branch to trunk (r80618:80906) Now DateTime will use pytz for timezone data. The main advantage of this is that pytz has up to date daylight savings data.
Changed: U Zope/trunk/doc/CHANGES.txt U Zope/trunk/lib/python/DateTime/DateTime.py U Zope/trunk/lib/python/DateTime/DateTime.txt A Zope/trunk/lib/python/DateTime/pytz.txt A Zope/trunk/lib/python/DateTime/pytz_support.py U Zope/trunk/lib/python/DateTime/tests/testDateTime.py -=- Modified: Zope/trunk/doc/CHANGES.txt =================================================================== --- Zope/trunk/doc/CHANGES.txt 2007-10-17 19:59:06 UTC (rev 80906) +++ Zope/trunk/doc/CHANGES.txt 2007-10-17 22:20:17 UTC (rev 80907) @@ -173,6 +173,10 @@ view to work. (patch by Sidnei da Silva from Enfold, integration by Martijn Faassen (Startifact) for Infrae) + - DateTime now uses pytz for time zone data if available. This + means support for more time zones and up to date daylight + saving time information. + Bugs Fixed - Launchpad #147201: treat container-class in zope.conf as a string, Modified: Zope/trunk/lib/python/DateTime/DateTime.py =================================================================== --- Zope/trunk/lib/python/DateTime/DateTime.py 2007-10-17 19:59:06 UTC (rev 80906) +++ Zope/trunk/lib/python/DateTime/DateTime.py 2007-10-17 22:20:17 UTC (rev 80907) @@ -22,6 +22,11 @@ from interfaces import IDateTime from interfaces import DateTimeError, SyntaxError, DateError, TimeError from zope.interface import implements +try: + from pytz_support import PytzCache +except ImportError: + # pytz not available, use legacy timezone support + PytzCache = None default_datefmt = None @@ -951,7 +956,10 @@ _isDST = localtime(time())[8] _localzone = _isDST and _localzone1 or _localzone0 - _tzinfo = _cache() + if PytzCache is not None: + _tzinfo = PytzCache(_cache()) + else: + _tzinfo = _cache() def localZone(self, ltm=None): '''Returns the time zone on the given date. The time zone @@ -1935,4 +1943,6 @@ # Module methods def Timezones(): """Return the list of recognized timezone names""" + if PytzCache is not None: + return list(PytzCache(_cache())._zlst) return _cache._zlst Modified: Zope/trunk/lib/python/DateTime/DateTime.txt =================================================================== --- Zope/trunk/lib/python/DateTime/DateTime.txt 2007-10-17 19:59:06 UTC (rev 80906) +++ Zope/trunk/lib/python/DateTime/DateTime.txt 2007-10-17 22:20:17 UTC (rev 80907) @@ -12,7 +12,7 @@ >>> from DateTime import Timezones >>> Timezones() # doctest: +ELLIPSIS - ['Brazil/Acre', 'Brazil/DeNoronha', ..., 'NZST', 'IDLE'] + [...'Brazil/Acre', 'Brazil/DeNoronha', ..., 'NZST', 'IDLE'] Class DateTime @@ -229,7 +229,7 @@ object, represented in the indicated timezone: >>> dt.toZone('UTC') - DateTime('1997/03/09 18:45:00 Universal') + DateTime('1997/03/09 18:45:00 UTC') >>> dt.toZone('UTC') == dt True Copied: Zope/trunk/lib/python/DateTime/pytz.txt (from rev 80906, Zope/branches/amos-datetime-pytz/lib/python/DateTime/pytz.txt) =================================================================== --- Zope/trunk/lib/python/DateTime/pytz.txt (rev 0) +++ Zope/trunk/lib/python/DateTime/pytz.txt 2007-10-17 22:20:17 UTC (rev 80907) @@ -0,0 +1,193 @@ +Pytz Support +============ +If the pytz package is importable, it will be used for time zone +information, otherwise, DateTime's own time zone database is used. The +advantage of using pytz is that it has a more complete and up to date +time zone and daylight savings time database. + +This test assumes that pytz is available. + + >>> import pytz + +Usage +----- +If pytz is available, then DateTime will use it. You don't have to do +anything special to make it work. + + >>> from DateTime import DateTime, Timezones + >>> d = DateTime('March 11, 2007 US/Eastern') + +Daylight Savings +---------------- +In 2007 daylight savings time in the US was changed. The Energy Policy +Act of 2005 mandates that DST will start on the second Sunday in March +and end on the first Sunday in November. + +In 2007, the start and stop dates are March 11 and November 4, +respectively. These dates are different from previous DST start and +stop dates. In 2006, the dates were the first Sunday in April (April +2, 2006) and the last Sunday in October (October 29, 2006). + +Let's make sure that DateTime can deal with this, since the primary +motivation to use pytz for time zone information is the fact that it +is kept up to date with daylight savings changes. + + >>> DateTime('March 11, 2007 US/Eastern').tzoffset() + -18000 + >>> DateTime('March 12, 2007 US/Eastern').tzoffset() + -14400 + >>> DateTime('November 4, 2007 US/Eastern').tzoffset() + -14400 + >>> DateTime('November 5, 2007 US/Eastern').tzoffset() + -18000 + +Let's compare this to 2006. + + >>> DateTime('April 2, 2006 US/Eastern').tzoffset() + -18000 + >>> DateTime('April 3, 2006 US/Eastern').tzoffset() + -14400 + >>> DateTime('October 29, 2006 US/Eastern').tzoffset() + -14400 + >>> DateTime('October 30, 2006 US/Eastern').tzoffset() + -18000 + +Time Zones +---------- +When pytz is available, DateTime can use its large database of time +zones. Here are some examples: + + >>> d = DateTime('Pacific/Kwajalein') + >>> d = DateTime('America/Shiprock') + >>> d = DateTime('Africa/Ouagadougou') + +Of course pytz doesn't know about everything. + + >>> d = DateTime('July 21, 1969 Moon/Eastern') + Traceback (most recent call last): + ... + SyntaxError: July 21, 1969 Moon/Eastern + +You can still use zone names that DateTime defines that aren't part of +the pytz database. + + >>> d = DateTime('eet') + >>> d = DateTime('iceland') + +These time zones use DateTimes database. So it's preferable to use the +official time zone name when you have pytz installed. + +One trickiness is that DateTime supports some zone name +abbreviations. Some of these map to pytz names, so when pytz is +present these abbreviations will give you time zone date from +pytz. Notable among abbreviations that work this way are 'est', 'cst', +'mst', and 'pst'. + +Let's verify that 'est' picks up the 2007 daylight savings time changes. + + >>> DateTime('March 11, 2007 est').tzoffset() + -18000 + >>> DateTime('March 12, 2007 est').tzoffset() + -14400 + >>> DateTime('November 4, 2007 est').tzoffset() + -14400 + >>> DateTime('November 5, 2007 est').tzoffset() + -18000 + +You can get a list of time zones supported by calling the Timezones() function. + + >>> Timezones() #doctest: +ELLIPSIS + ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', ...] + +Note that you can mess with this list without hurting things. + + >>> t = Timezones() + >>> t.remove('US/Eastern') + >>> d = DateTime('US/Eastern') + +Python Versions +--------------- +Both pytz and DateTime should work under Python 2.3 as well as Python 2.4. + +Internal Components +------------------- +The following are tests of internal components. + +Cache +~~~~~ +When pytz is present, the DateTime class uses a different time zone cache. + + >>> DateTime._tzinfo #doctest: +ELLIPSIS + <DateTime.pytz_support.PytzCache instance at ...> + +The cache maps time zone names to time zone instances. + + >>> cache = DateTime._tzinfo + >>> tz = cache['GMT+730'] + >>> tz = cache['US/Mountain'] + +The cache also must provide a few attributes for use by the DateTime +class. + +The _zlst attribute is a list of supported time zone names. + + >>> cache._zlst #doctest: +ELLIPSIS + ['Africa/Abidjan', 'Africa/Accra', ... 'NZT', 'NZST', 'IDLE'] + +The _zidx attribute is a list of lower-case and possibly abbreviated +time zone names that can be mapped to offical zone names. + + >>> cache._zidx #doctest: +ELLIPSIS + ['australia/yancowinna', 'gmt+0500', ... 'europe/isle_of_man'] + +Note that there are more items in _zidx than in _zlst since there are +multiple names for some time zones. + + >>> len(cache._zidx) > len(cache._zlst) + True + +Each entry in _zlst should also be present in _zidx in lower case form. + + >>> for name in cache._zlst: + ... if not name.lower() in cache._zidx: + ... print "Error %s not in _zidx" % name.lower() + +The _zmap attribute maps the names in _zidx to official names in _zlst. + + >>> cache._zmap['africa/abidjan'] + 'Africa/Abidjan' + >>> cache._zmap['gmt+1'] + 'GMT+1' + >>> cache._zmap['gmt+0100'] + 'GMT+1' + >>> cache._zmap['utc'] + 'UTC' + +Let's make sure that _zmap and _zidx agree. + + >>> idx = list(cache._zidx) + >>> idx.sort() + >>> keys = cache._zmap.keys() + >>> keys.sort() + >>> idx == keys + True + +Timezone objects +~~~~~~~~~~~~~~~~ +The timezone instances have only one public method info(). It returns +a tuple of (offset, is_dst, name). The method takes a timestamp, which +is used to determine dst information. + + >>> t1 = DateTime('November 4, 00:00 2007 US/Mountain').timeTime() + >>> t2 = DateTime('November 4, 02:00 2007 US/Mountain').timeTime() + >>> tz.info(t1) + (-21600, 1, 'MDT') + >>> tz.info(t2) + (-25200, 0, 'MST') + +If you don't pass any arguments to info it provides daylight savings +time information as of today. + + >>> tz.info() in ((-21600, 1, 'MDT'), (-25200, 0, 'MST')) + True + Copied: Zope/trunk/lib/python/DateTime/pytz_support.py (from rev 80906, Zope/branches/amos-datetime-pytz/lib/python/DateTime/pytz_support.py) =================================================================== --- Zope/trunk/lib/python/DateTime/pytz_support.py (rev 0) +++ Zope/trunk/lib/python/DateTime/pytz_support.py 2007-10-17 22:20:17 UTC (rev 80907) @@ -0,0 +1,73 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +""" +Pytz timezone support. +""" + +import pytz +import pytz.reference +from datetime import datetime, timedelta +import re + +class Timezone: + """ + Timezone information returned by PytzCache.__getitem__ + Adapts datetime.tzinfo object to DateTime._timezone interface + """ + def __init__(self, tzinfo): + self.tzinfo = tzinfo + + def info(self, t=None): + if t is None: + dt = datetime.utcnow().replace(tzinfo=pytz.utc) + else: + dt = datetime.utcfromtimestamp(t).replace(tzinfo=pytz.utc) + + # need to normalize tzinfo for the datetime to deal with + # daylight savings time. + normalized_dt = self.tzinfo.normalize(dt.astimezone(self.tzinfo)) + normalized_tzinfo = normalized_dt.tzinfo + + offset = normalized_tzinfo.utcoffset(dt) + secs = offset.days * 24 * 60 * 60 + offset.seconds + dst = normalized_tzinfo.dst(dt) + if dst == timedelta(0): + is_dst = 0 + else: + is_dst = 1 + return secs, is_dst, normalized_tzinfo.tzname(dt) + + +class PytzCache: + """ + Wrapper for the DateTime._cache class that uses pytz for + timezone information where possible. + """ + def __init__(self, cache): + self.cache = cache + self._zlst = list(pytz.common_timezones) + for name in cache._zlst: + if name not in self._zlst: + self._zlst.append(name) + self._zmap = dict(cache._zmap) + self._zmap.update(dict([(name.lower(), name) + for name in pytz.common_timezones])) + self._zidx = self._zmap.keys() + + def __getitem__(self, key): + try: + name = self._zmap[key.lower()] + return Timezone(pytz.timezone(name)) + except (KeyError, pytz.UnknownTimeZoneError): + return self.cache[key] + Modified: Zope/trunk/lib/python/DateTime/tests/testDateTime.py =================================================================== --- Zope/trunk/lib/python/DateTime/tests/testDateTime.py 2007-10-17 19:59:06 UTC (rev 80906) +++ Zope/trunk/lib/python/DateTime/tests/testDateTime.py 2007-10-17 22:20:17 UTC (rev 80907) @@ -463,10 +463,20 @@ def test_suite(): from zope.testing import doctest - return unittest.TestSuite([ - unittest.makeSuite(DateTimeTests), - doctest.DocFileSuite('DateTime.txt', package='DateTime') - ]) + try: + # if pytz is available include a test for it + import pytz + return unittest.TestSuite([ + unittest.makeSuite(DateTimeTests), + doctest.DocFileSuite('DateTime.txt', package='DateTime'), + doctest.DocFileSuite('pytz.txt', package='DateTime'), + ]) + except ImportError: + return unittest.TestSuite([ + unittest.makeSuite(DateTimeTests), + doctest.DocFileSuite('DateTime.txt', package='DateTime') + ]) + if __name__=="__main__": unittest.main(defaultTest='test_suite') _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins