]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
Started work on improving timezone support and made pytz a requirement
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 6 Jul 2013 09:27:26 +0000 (11:27 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 6 Jul 2013 09:27:26 +0000 (11:27 +0200)
babel/dates.py
babel/util.py
setup.py

index 48e16af098d4970e6e854c2aa0b719a752b58efc..e667af7754214f985ac9205745da0a0b3d15d025 100644 (file)
@@ -23,10 +23,12 @@ following environment variables, in that order:
 
 from __future__ import division
 from datetime import date, datetime, time, timedelta
+from bisect import bisect_right
 import re
+import pytz as _pytz
 
 from babel.core import default_locale, get_global, Locale
-from babel.util import UTC
+from babel.util import UTC, LOCALTZ
 
 __all__ = ['format_date', 'format_datetime', 'format_time', 'format_timedelta',
            'get_timezone_name', 'parse_date', 'parse_datetime', 'parse_time']
@@ -39,6 +41,78 @@ date_ = date
 datetime_ = datetime
 time_ = time
 
+def get_timezone(zone):
+    # XXX: return _pytz.timezone with a nice fallback.
+    if zone is None:
+        return LOCALTZ
+    if not isinstance(zone, basestring):
+        return zone
+    try:
+        return _pytz.timezone(zone)
+    except _pytz.UnknownTimeZoneError:
+        raise LookupError('Unknown timezone %s' % zone)
+
+def get_next_timezone_transition(zone, dt=None):
+    zone = get_timezone(zone)
+    if dt is None:
+        dt = datetime.utcnow()
+    else:
+        dt = dt.replace(tzinfo=None)
+
+    if not hasattr(zone, '_utc_transition_times'):
+        raise TypeError('Given timezone does not have UTC transition '
+                        'times.  This can happen because the operating '
+                        'system local timezone is used of a custom '
+                        'timezone object')
+
+    try:
+        idx = max(0, bisect_right(zone._utc_transition_times, dt))
+        old_trans = zone._transition_info[idx - 1]
+        new_trans = zone._transition_info[idx]
+        old_tz = zone._tzinfos[old_trans]
+        new_tz = zone._tzinfos[new_trans]
+    except (LookupError, ValueError):
+        raise RuntimeError('Could not calculate transition')
+
+    return TimezoneTransition(
+        activates=zone._utc_transition_times[idx],
+        from_tzinfo=old_tz,
+        to_tzinfo=new_tz,
+        reference_date=dt
+    )
+
+
+class TimezoneTransition(object):
+
+    def __init__(self, activates, from_tzinfo, to_tzinfo, reference_date=None):
+        self.activates = activates
+        self.from_tzinfo = from_tzinfo
+        self.to_tzinfo = to_tzinfo
+        self.reference_date = reference_date
+
+    @property
+    def from_tz(self):
+        return self.from_tzinfo._tzname
+
+    @property
+    def to_tz(self):
+        return self.to_tzinfo._tzname
+
+    @property
+    def from_offset(self):
+        return self.from_tzinfo._utcoffset.total_seconds()
+
+    @property
+    def to_offset(self):
+        return self.to_tzinfo._utcoffset.total_seconds()
+
+    def __repr__(self):
+        return '<TimezoneTransition %s -> %s (%s)>' % (
+            self.from_tz,
+            self.to_tz,
+            self.activates,
+        )
+
 def get_period_names(locale=LC_TIME):
     """Return the names for day periods (AM/PM) used by the locale.
 
@@ -178,8 +252,7 @@ def get_timezone_gmt(datetime=None, width='long', locale=LC_TIME):
     >>> get_timezone_gmt(dt, locale='en')
     u'GMT+00:00'
 
-    >>> from pytz import timezone
-    >>> tz = timezone('America/Los_Angeles')
+    >>> tz = get_timezone('America/Los_Angeles')
     >>> dt = datetime(2007, 4, 1, 15, 30, tzinfo=tz)
     >>> get_timezone_gmt(dt, locale='en')
     u'GMT-08:00'
@@ -223,11 +296,10 @@ def get_timezone_location(dt_or_tzinfo=None, locale=LC_TIME):
     The result depends on both the local display name of the country and the
     city associated with the time zone:
 
-    >>> from pytz import timezone
-    >>> tz = timezone('America/St_Johns')
+    >>> tz = get_timezone('America/St_Johns')
     >>> get_timezone_location(tz, locale='de_DE')
     u"Kanada (St. John's) Zeit"
-    >>> tz = timezone('America/Mexico_City')
+    >>> tz = get_timezone('America/Mexico_City')
     >>> get_timezone_location(tz, locale='de_DE')
     u'Mexiko (Mexiko-Stadt) Zeit'
 
@@ -304,8 +376,7 @@ def get_timezone_name(dt_or_tzinfo=None, width='long', uncommon=False,
     r"""Return the localized display name for the given timezone. The timezone
     may be specified using a ``datetime`` or `tzinfo` object.
 
-    >>> from pytz import timezone
-    >>> dt = time(15, 30, tzinfo=timezone('America/Los_Angeles'))
+    >>> dt = time(15, 30, tzinfo=get_timezone('America/Los_Angeles'))
     >>> get_timezone_name(dt, locale='en_US')
     u'Pacific Standard Time'
     >>> get_timezone_name(dt, width='short', locale='en_US')
@@ -458,8 +529,7 @@ def format_datetime(datetime=None, format='medium', tzinfo=None,
     For any pattern requiring the display of the time-zone, the third-party
     ``pytz`` package is needed to explicitly specify the time-zone:
 
-    >>> from pytz import timezone
-    >>> format_datetime(dt, 'full', tzinfo=timezone('Europe/Paris'),
+    >>> format_datetime(dt, 'full', tzinfo=get_timezone('Europe/Paris'),
     ...                 locale='fr_FR')
     u'dimanche 1 avril 2007 17:30:00 heure avanc\xe9e d\u2019Europe centrale'
     >>> format_datetime(dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz",
index 8eb7f697de3bd2e03a25eebff5cdf9dd5b9ec086..fe202d777d9a52a4ca6978e2d59b08887a63bf89 100644 (file)
@@ -294,14 +294,9 @@ class FixedOffsetTimezone(tzinfo):
         return ZERO
 
 
-try:
-    from pytz import UTC
-except ImportError:
-    UTC = FixedOffsetTimezone(0, 'UTC')
-    """`tzinfo` object for UTC (Universal Time).
+import pytz as _pytz
 
-    :type: `tzinfo`
-    """
+UTC = _pytz.utc
 
 STDOFFSET = timedelta(seconds = -time.timezone)
 if time.daylight:
index 5e44a0a645388e5ee836029ea16b1bdabcc06f06..d793ffcd8f5be41869ec881be0db3b24ea5a0c40 100755 (executable)
--- a/setup.py
+++ b/setup.py
 # history and logs, available at http://babel.edgewall.org/log/.
 
 import os
-try:
-    from setuptools import setup
-    have_setuptools = True
-except ImportError:
-    from distutils.core import setup
-    have_setuptools = False
 import sys
+from setuptools import setup
 
 sys.path.append(os.path.join('doc', 'common'))
 try:
@@ -28,37 +23,6 @@ except ImportError:
     build_doc = test_doc = None
 
 
-extra_arguments = dict()
-if have_setuptools:
-    extra_arguments = dict(
-        zip_safe = False,
-        test_suite = 'babel.tests.suite',
-        tests_require = ['pytz'],
-
-        entry_points = """
-        [console_scripts]
-        pybabel = babel.messages.frontend:main
-
-        [distutils.commands]
-        compile_catalog = babel.messages.frontend:compile_catalog
-        extract_messages = babel.messages.frontend:extract_messages
-        init_catalog = babel.messages.frontend:init_catalog
-        update_catalog = babel.messages.frontend:update_catalog
-
-        [distutils.setup_keywords]
-        message_extractors = babel.messages.frontend:check_message_extractors
-
-        [babel.checkers]
-        num_plurals = babel.messages.checkers:num_plurals
-        python_format = babel.messages.checkers:python_format
-
-        [babel.extractors]
-        ignore = babel.messages.extract:extract_nothing
-        python = babel.messages.extract:extract_python
-        javascript = babel.messages.extract:extract_javascript
-        """,
-    )
-
 setup(
     name = 'Babel',
     version = '1.0',
@@ -82,8 +46,37 @@ setup(
     ],
     packages = ['babel', 'babel.messages'],
     package_data = {'babel': ['global.dat', 'localedata/*.dat']},
+    install_requires=[
+        'pytz',
+    ],
 
     cmdclass = {'build_doc': build_doc, 'test_doc': test_doc},
 
+    zip_safe = False,
+    test_suite = 'babel.tests.suite',
+
+    entry_points = """
+    [console_scripts]
+    pybabel = babel.messages.frontend:main
+
+    [distutils.commands]
+    compile_catalog = babel.messages.frontend:compile_catalog
+    extract_messages = babel.messages.frontend:extract_messages
+    init_catalog = babel.messages.frontend:init_catalog
+    update_catalog = babel.messages.frontend:update_catalog
+
+    [distutils.setup_keywords]
+    message_extractors = babel.messages.frontend:check_message_extractors
+
+    [babel.checkers]
+    num_plurals = babel.messages.checkers:num_plurals
+    python_format = babel.messages.checkers:python_format
+
+    [babel.extractors]
+    ignore = babel.messages.extract:extract_nothing
+    python = babel.messages.extract:extract_python
+    javascript = babel.messages.extract:extract_javascript
+    """,
+
     **extra_arguments
 )