"""
$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/qp/lib/tz.py $
$Id: tz.py 28672 2006-08-30 00:53:48Z dbinger $

Adapted from http://www.python.org/doc/2.3.5/lib/tzinfo-examples.txt
"""
from datetime import tzinfo, timedelta, datetime
from types import FunctionType

class ClassMethodsClass (type):
    """
    A class that uses this class as the value of its __metaclass__ attribute
    gets all of its methods (except those with names that start with '__')
    converted into class methods.  This is a way to define
    classes that can act as singleton instances.

    The str method of classes with this metaclass returns the name of
    the class.
    """

    def __init__ (self, class_name, bases, namespace):
        for name, obj in namespace.items():
            if isinstance(obj, FunctionType) and not name.startswith('__'):
                setattr(self, name, classmethod(obj))

    def __str__ (self):
        return self.__name__


class _tzinfoclass (ClassMethodsClass, tzinfo):
    """
    Classes with this class are *instances* of tzinfo. (!)
    All of the non __* methods are automatically converted
    to classmethods.
    """

class TimeZone (object):

    __metaclass__ = _tzinfoclass

    def now(klass):
        """
        The current time, with this timezone.
        """
        return datetime.now(klass)


class UTCOffset (TimeZone):

    _utc_offset = timedelta(0)
    _name = "UTC?"
    _dst = timedelta(0)

    def utcoffset(klass, dt):
        return klass._utc_offset

    def tzname(klass, dt):
        return klass._name

    def dst(klass, dt):
        return klass._dst

class UTC (UTCOffset):
    _name = "UTC"


_ZERO_TIME = timedelta(hours=0)
_HOUR = timedelta(hours=1)

class _US (TimeZone):

    # 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 Oct.
    # which is the first Sunday on or after Oct 25.
    _DSTEND = datetime(1, 10, 25, 1)

    def tzname(klass, dt):
        if klass.dst(dt):
            return klass.dstname
        else:
            return klass.stdname

    def utcoffset(klass, dt):
        return klass.stdoffset + klass.dst(dt)

    def dst(klass, 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 default
            # fromutc() implementation (called by the default astimezone()
            # implementation) passes a datetime with dt.tzinfo is klass.
            return _ZERO_TIME
        assert dt.tzinfo is klass

        def first_sunday_on_or_after(dt):
            days_to_go = 6 - dt.weekday()
            if days_to_go:
                dt += timedelta(days_to_go)
            return dt

        # Find first Sunday in April & the last in October.
        start = first_sunday_on_or_after(klass._DSTSTART.replace(year=dt.year))
        end = first_sunday_on_or_after(klass._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_TIME

class Eastern (_US):
    stdoffset = timedelta(hours=-5)
    stdname = "EST"
    dstname = "EDT"

class Central (_US):
    stdoffset = timedelta(hours=-6)
    stdname = "CST"
    dstname = "CDT"

class Mountain (_US):
    stdoffset = timedelta(hours=-7)
    stdname = "MST"
    dstname = "MDT"

class Pacific (_US):
    stdoffset = timedelta(hours=-8)
    stdname = "PST"
    dstname = "PDT"

class UTC_plus_1 (UTCOffset):
    _name = "UTC+1"
    _utc_offset = timedelta(hours=1)

class UTC_minus_1 (UTCOffset):
    _name = "UTC-1"
    _utc_offset = timedelta(hours=-1)

class UTC_plus_2 (UTCOffset):
    _name = "UTC+2"
    _utc_offset = timedelta(hours=2)

class UTC_minus_2 (UTCOffset):
    _name = "UTC-2"
    _utc_offset = timedelta(hours=-2)

class UTC_plus_3 (UTCOffset):
    _name = "UTC+3"
    _utc_offset = timedelta(hours=3)

class UTC_minus_3 (UTCOffset):
    _name = "UTC-3"
    _utc_offset = timedelta(hours=-3)

class UTC_plus_4 (UTCOffset):
    _name = "UTC+4"
    _utc_offset = timedelta(hours=4)

class UTC_minus_4 (UTCOffset):
    _name = "UTC-4"
    _utc_offset = timedelta(hours=-4)

class UTC_plus_5 (UTCOffset):
    _name = "UTC+5"
    _utc_offset = timedelta(hours=5)

class UTC_minus_5 (UTCOffset):
    _name = "UTC-5"
    _utc_offset = timedelta(hours=-5)

class UTC_plus_6 (UTCOffset):
    _name = "UTC+6"
    _utc_offset = timedelta(hours=6)

class UTC_minus_6 (UTCOffset):
    _name = "UTC-6"
    _utc_offset = timedelta(hours=-6)

class UTC_plus_7 (UTCOffset):
    _name = "UTC+7"
    _utc_offset = timedelta(hours=7)

class UTC_minus_7 (UTCOffset):
    _name = "UTC-7"
    _utc_offset = timedelta(hours=-7)

class UTC_plus_8 (UTCOffset):
    _name = "UTC+8"
    _utc_offset = timedelta(hours=8)

class UTC_minus_8 (UTCOffset):
    _name = "UTC-8"
    _utc_offset = timedelta(hours=-8)

class UTC_plus_9 (UTCOffset):
    _name = "UTC+9"
    _utc_offset = timedelta(hours=9)

class UTC_minus_9 (UTCOffset):
    _name = "UTC-9"
    _utc_offset = timedelta(hours=-9)

class UTC_plus_10 (UTCOffset):
    _name = "UTC+10"
    _utc_offset = timedelta(hours=10)

class UTC_minus_10 (UTCOffset):
    _name = "UTC-10"
    _utc_offset = timedelta(hours=-10)

class UTC_plus_11 (UTCOffset):
    _name = "UTC+11"
    _utc_offset = timedelta(hours=11)

class UTC_minus_11 (UTCOffset):
    _name = "UTC-11"
    _utc_offset = timedelta(hours=-11)

class UTC_plus_12 (UTCOffset):
    _name = "UTC+12"
    _utc_offset = timedelta(hours=12)

class UTC_minus_12 (UTCOffset):
    _name = "UTC-12"
    _utc_offset = timedelta(hours=-12)

def now(tzinfo=UTC):
    return datetime.now(tzinfo=tzinfo)
