"""
$URL: svn+ssh://sting.mems-exchange.org/repos/trunk/DurusWorks/qp/lib/tz.py $
$Id: tz.py 31389 2008-12-11 15:19:14Z 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
import sys

class ClassMethodsClass (type):
    """
    A class that uses this class as its type
    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.
   """
   # Make name shadowing explicit.
   __getattribute__ = type.__getattribute__
   __new__ = type.__new__

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

TimeZone = _tzinfoclass('TimeZone', (object,), {})

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)
