[Zope-Checkins] SVN: Zope/trunk/ Merged amos-datetime-pytz branch
to trunk (r80618:80906)
Amos Latteier
amos at latteier.com
Wed Oct 17 18:20:18 EDT 2007
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')
More information about the Zope-Checkins
mailing list