# 项目：标准库函数
# 模块：时间模块
# 作者：黄涛
# License:GPL
# Email:huangtao.sh@icloud.com
# 创建：2015-09-17 13:24
# 修改：2016-03-12 18:53
# 修改：2016-09-06 增加 ONEDAY,ONESECOND
# 修改：2016-11-19 将datetime 修改为按类实现

import datetime as dt
import time as _time
from .regex import R

__all__ = 'UTC', 'LOCAL', 'now', 'datetime', 'FixedOffset', 'ONEDAY',\
    'ONESECOND', 'date_add', 'LTZ'

ZERO = dt.timedelta(0)
ONEDAY = dt.timedelta(days=1)
ONESECOND = dt.timedelta(seconds=1)

# A class building tzinfo objects for fixed-offset time zones.
# Note that FixedOffset(0, "UTC") is a different way to build a
# UTC tzinfo object.


class FixedOffset(dt.tzinfo):
    """Fixed offset in minutes east from UTC."""

    def __init__(self, offset, name):
        self.__offset = dt.timedelta(minutes=offset)
        self.__name = name

    def utcoffset(self, dt):
        return self.__offset

    def tzname(self, dt):
        return self.__name

    def dst(self, dt):
        return ZERO

    def __repr__(self):
        timezone = self.__offset.total_seconds()//60
        return "UTC%+i:%02i" % (divmod(timezone, 60)) if timezone else "UTC"


UTC = FixedOffset(0, 'UTC')

# A class capturing the platform's idea of local time.

STDOFFSET = dt.timedelta(seconds=-_time.timezone)
if _time.daylight:
    DSTOFFSET = dt.timedelta(seconds=-_time.altzone)
else:
    DSTOFFSET = STDOFFSET

DSTDIFF = DSTOFFSET - STDOFFSET


class LocalTimezone(dt.tzinfo):

    def utcoffset(self, dt):
        if self._isdst(dt):
            return DSTOFFSET
        else:
            return STDOFFSET

    def dst(self, dt):
        if self._isdst(dt):
            return DSTDIFF
        else:
            return ZERO

    def tzname(self, dt):
        return _time.tzname[self._isdst(dt)]

    def _isdst(self, dt):
        tt = (dt.year, dt.month, dt.day,
              dt.hour, dt.minute, dt.second,
              dt.weekday(), 0, 0)
        stamp = _time.mktime(tt)
        tt = _time.localtime(stamp)
        return tt.tm_isdst > 0

    def __repr__(self):
        offset = STDOFFSET.total_seconds()//60
        return "UTC%+i:%02i" % (divmod(offset, 60))


LTZ = LocalTimezone()
LOCAL = LTZ


class datetime(dt.datetime):
    '''日期类，支持从字符串转换'''
    def __new__(cls, year, month=0, day=0, hour=0, minute=0, second=0,
                microsecond=0, tzinfo=None):
        tzinfo = tzinfo or LTZ
        if all([year, month, day]):
            return super().__new__(cls, year, month, day, hour, minute,
                                   second, microsecond, tzinfo)
        else:
            if isinstance(year, datetime):
                return year
            elif isinstance(year, (dt.datetime, dt.time)):
                '''
                如果是DATETIME或TIME类型，检查是否有TZINFO信息，
                如无，则设为LTZ，否则直接返回
                '''
                args = list(year.timetuple()[:6])
                if year.tzinfo:
                    tzinfo = year.tzinfo
                args.extend([year.microsecond, tzinfo])
            elif isinstance(year, str):
                '''将字符串转换为DATETIME类型'''
                args = [int(x) for x in R/r'\d+'/year]
            elif isinstance(year, (int, float)):
                '''将整数或浮点数转换成日期类型
                如果小于100000，则按EXCEL的格式转换；
                否则按UNIX TIMESTAMP 来转换'''
                if year < 100000:
                    from xlrd.xldate import xldate_as_datetime
                    dd = cls(xldate_as_datetime(year, None))
                else:
                    dd = cls.fromtimestamp(year)
                return dd
            if len(args) == 8:
                tzinfo = args.pop(7)
        return super().__new__(cls, *args, tzinfo=tzinfo)

    def add(self, years=0, months=0, **kw):
        '''增加日期，返回一个新的日期实例'''
        year, month = self.timetuple()[:2]
        year, month = divmod((year+years)*12+month+months-1, 12)
        try:
            date = self.replace(year=year, month=month+1)
        except:
            # 加上年或月之后，如果没有对应的日，则以最后一天为准
            date = self.replace(year=year, month=month+2, day=1)-1
        return date+dt.timedelta(**kw)

    def __add__(self, days):
        '''增加日期，支持整数型'''
        if isinstance(days, int):
            days = dt.timedelta(days=days)
        return datetime(super().__add__(days))

    def __sub__(self, days):
        '''减少日期，支持整数类型'''
        if isinstance(days, (dt.datetime, dt.time)):
            return super().__sub__(days)
        return self.__add__(-days)

    @property
    def first_day_of_year(self):
        return datetime(self.year, 1, 1, tzinfo=LTZ)

    @property
    def last_day_of_year(self):
        return datetime(self.year, 12, 31, tzinfo=LTZ)

    @property
    def first_day_of_quartor(self):
        month = (self.quartor-1)*3+1
        return datetime(self.year, month, 1, tzinfo=LTZ)

    @property
    def last_day_of_quartor(self):
        return self.first_day_of_quartor.add(months=3)-1

    @property
    def first_day_of_month(self):
        '''当月第一天'''
        return self.replace(day=1)

    @property
    def last_day_of_month(self):
        '''当月最后一天'''
        return self.add(months=1).first_day_of_month-1

    @property
    def is_weekend(self):
        '''是否为周末'''
        return self.weekday() > 4

    @property
    def quartor(self):
        '''当前的季度，从1开始'''
        return (self.month-1) // 3 + 1

    def format(self, fmt):
        '''格式化'''
        if '%Q' in fmt:
            fmt = fmt.replace('%Q', '%s-%s' % (self.year, self.quartor))
        if '%q' in fmt:
            fmt = fmt.replace('%q', str(self.quartor))
        return super().strftime(fmt)

    # 使用date%'%Y-%m-%d'的语法来格式化日期
    __mod__ = strftime = format

    def iter(self, end, step={'days': 1}, fmt=lambda x: x):
        '''遍历日期，如果days 为整数，则遍历days 指定的天数,
        若days 为非整数，则days 应为终止的日期,
        fmt 为返回格式：如为字符串，则格式化日期；若为可调用对象，则调用该日期'''
        if isinstance(fmt, str):
            _fmt = fmt

            def fmt(x): return x.strftime(_fmt)

        if isinstance(end, dict):
            p, a = {}, {}
            for k, v in end.items():
                if k in set(['year', 'month', 'day']):
                    p[k] = v
                else:
                    a[k] = v
            end_day = self.add(**a) if a else self
            if a:
                end_day = end_day.replace(**p)
        else:
            end_day = datetime(end)
        while self < end_day:
            yield fmt(self)
            self = self.add(**step)


def date_add(dt, *args, **kw):
    '''日期及时间的加减,
    支持的参数有：years,months,days,hours,minutes,seconds'''
    return datetime(dt).add(*args, **kw)


def now(tz=LTZ):
    return datetime(dt.datetime.now(tz))
