Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions dateutil/test/test_tz.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

IS_WIN = sys.platform.startswith('win')

import pytest

# dateutil imports
from dateutil.relativedelta import relativedelta, SU
from dateutil.parser import parse
Expand Down Expand Up @@ -748,6 +750,22 @@ def testRepr(self):
self.assertEqual(repr(tzl), 'tzlocal()')


@pytest.mark.parametrize('args,kwargs', [
(('EST', -18000), {}),
(('EST', timedelta(hours=-5)), {}),
(('EST',), {'offset': -18000}),
(('EST',), {'offset': timedelta(hours=-5)}),
(tuple(), {'name': 'EST', 'offset': -18000})
])
def test_tzoffset_is(args, kwargs):
tz_ref = tz.tzoffset('EST', -18000)
assert tz.tzoffset(*args, **kwargs) is tz_ref


def test_tzoffset_is_not():
assert tz.tzoffset('EDT', -14400) is not tz.tzoffset('EST', -18000)


@unittest.skipIf(IS_WIN, "requires Unix")
@unittest.skipUnless(TZEnvContext.tz_change_allowed(),
TZEnvContext.tz_change_disallowed_message())
Expand Down Expand Up @@ -1951,13 +1969,13 @@ def testPickleTzUTC(self):
self.assertPicklable(tz.tzutc(), singleton=True)

def testPickleTzOffsetZero(self):
self.assertPicklable(tz.tzoffset('UTC', 0))
self.assertPicklable(tz.tzoffset('UTC', 0), singleton=True)

def testPickleTzOffsetPos(self):
self.assertPicklable(tz.tzoffset('UTC+1', 3600))
self.assertPicklable(tz.tzoffset('UTC+1', 3600), singleton=True)

def testPickleTzOffsetNeg(self):
self.assertPicklable(tz.tzoffset('UTC-1', -3600))
self.assertPicklable(tz.tzoffset('UTC-1', -3600), singleton=True)

def testPickleTzLocal(self):
self.assertPicklable(tz.tzlocal())
Expand Down
28 changes: 28 additions & 0 deletions dateutil/tz/_factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from datetime import timedelta


class _TzSingleton(type):
def __init__(cls, *args, **kwargs):
cls.__instance = None
super(_TzSingleton, cls).__init__(*args, **kwargs)

def __call__(cls):
if cls.__instance is None:
cls.__instance = super(_TzSingleton, cls).__call__()
return cls.__instance


class _TzOffsetFactory(type):
def __init__(cls, *args, **kwargs):
cls.__instances = {}

def __call__(cls, name, offset):
if isinstance(offset, timedelta):
key = (name, offset.total_seconds())
else:
key = (name, offset)

instance = cls.__instances.get(key, None)
if instance is None:
instance = cls.__instances.setdefault(key, type.__call__(cls, name, offset))
return instance
18 changes: 5 additions & 13 deletions dateutil/tz/tz.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
import os
import bisect

import six
from six import string_types
from six.moves import _thread
from ._common import tzname_in_python2, _tzinfo
from ._common import tzrangebase, enfold
from ._common import _validate_fromutc_inputs

from ._factories import _TzSingleton, _TzOffsetFactory

try:
from .win import tzwin, tzwinlocal
except ImportError:
Expand All @@ -30,6 +33,7 @@
EPOCHORDINAL = EPOCH.toordinal()


@six.add_metaclass(_TzSingleton)
class tzutc(datetime.tzinfo):
"""
This is a tzinfo object that represents the UTC time zone.
Expand All @@ -46,13 +50,6 @@ class tzutc(datetime.tzinfo):
>>> tzutc() is UTC
True
"""
__instance = None

def __new__(cls):
if tzutc.__instance is None:
tzutc.__instance = datetime.tzinfo.__new__(cls)
return tzutc.__instance

def utcoffset(self, dt):
return ZERO

Expand Down Expand Up @@ -105,13 +102,12 @@ def __repr__(self):
__reduce__ = object.__reduce__


@six.add_metaclass(_TzOffsetFactory)
class tzoffset(datetime.tzinfo):
"""
A simple class for representing a fixed offset from UTC.

:param name:
The timezone name, to be returned when ``tzname()`` is called.

:param offset:
The time zone offset in seconds, or (since version 2.6.0, represented
as a :py:class:`datetime.timedelta` object).
Expand Down Expand Up @@ -144,14 +140,10 @@ def is_ambiguous(self, dt):
"""
Whether or not the "wall time" of a given datetime is ambiguous in this
zone.

:param dt:
A :py:class:`datetime.datetime`, naive or time zone aware.


:return:
Returns ``True`` if ambiguous, ``False`` otherwise.

.. versionadded:: 2.6.0
"""
return False
Expand Down