python 日期基本操作封装( time,datetime,np.Timestamp,np.Period,np.datetime64,arrow.Arrow)

版本2:修改

python版本>=py3.10,采用新库zoneinfo为基础,精简函数。将重载移入类内,附录全部函数

文章目录

  • 1.用途
  • 2.函数汇总
  • 3.封装的文件
  • 4.测试结果


1.用途

本篇封装日期的原因是python的日期函数库比较多,函数也比较多,常规使用由于各个库函数的名称繁多,且有许多限制,因此为使用上的方便特此汇总各库。特点如下:

总的特点你能用time(str,int,float,struct_time),datetime-like(datetime,date,time),pd.Timpstamp,pd.Period,np.datetime64这些类型转换任意以上的类型;日期字符串解析不需要指定格式自动解析;同时在构造转换过程中可指定时区。

特点1:函数重载

为减少函数数量采用重载方式(同名函数从5到十几个(经过测试5个以下函数重载效果不如用if isinstance()),不再需要记住转换函数,构造函数,统一用一个函数名解决;显示不必在记住字符串格式,都有默认值无需输入,所有不常用参数都封装在**kwargs中。同时所有函数都有类型提示及帮助文档。

特点2:时区基本构造,转换,替代。

目前时区建议采用dateutil.tz,支持py2,py3;不建议采用pytz(旧库);不建议采用zoneinfo(新库>=py3.9,datetime支持不错,pd.Timestamp支持不够好,有时会出现一些和datetime不同的功能,另外报错多;其他一些包装类操作也存在一些问题;个人感觉dateutil目前bug少)

特点3:自动解析日期字符串

采用parse(pd.Timestamp采用此库),时区采用标准的时区名或tzinfo对象,不在是dict(不方便),若字符串中提供时区则自动识别,若提供时区则替换。

特点4:

是采用chinese_calendar来计算商业日(由于我从事金融)

读者对象:中级水平

日期对象:        

time(int,float,struct_time),datetime-like(datetime,date,time),pd.Timestamp,pd.Period,np.datetime64


版本2:

_date_tz.py时区处理

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# @Project:TcyQuantTrade
# @Module:_tz.py

# @Author:       tcy
# @Date:         2023/4/22  21:56
# @Emial:        3615693665@qq.com
# @Version:     1.18
# @Last Modified time:
# @City:           China Shanghai Songjiang Xiaokunshan
# @Company:  Weishi Machinery Manufacturing Priority

import tzlocal
import zoneinfo
import pandas as pd
from plum import Dispatcher
from zoneinfo import ZoneInfo
from datetime import datetime, timezone, timedelta, tzinfo

t_convert = datetime | pd.Timestamp | pd.DatetimeIndex
t_dttp = datetime | pd.Timestamp

dispatch = Dispatcher()


def get_tz_offset(tz: tzinfo) -> timedelta:
    return timedelta(seconds=tzlocal.utils.get_tz_offset(tz))


def get_local_tz() -> dict:
    tzname = tzlocal.get_localzone_name()
    tz = ZoneInfo(tzname)
    return {
        'tzname': tzname,  # 'Asia/Shanghai'
        'tz': tz,
        'offset': get_tz_offset(tz)  # sec
    }


class _Tz(object):

    sys_path = r'C:\ProgramData\anaconda3'
    win = get_local_tz()

    @classmethod
    def get_tzinfo(cls, text='Asia', issuffix=True):
        # type:(str|None,bool)->set
        """
        用途:获取ZoneInfo一个包含可用IANA时区的在时区路径的任何位置均可用的全部有效键的集合
        返回:set[str]
        说明:路径C:\\ProgramData\anaconda3\\Lib\\site-packages\tzdata\\zoneinfo
            包含文件夹:
            Africa:非洲
            America:美洲
            Antarctica:南极洲
            Arctic:北极
            Asia:亚洲
            Atlantic:大西洋
            Australia:澳大利亚
            Brazil:巴西
            Canada:加拿大-大西洋
            Chile:智利
            Etc:
            Europe:欧洲
            Indian:印度
            Mexico:墨西哥
            Pacific:太平洋
            US:美国
        """
        rst = set()
        _set = zoneinfo.available_timezones()
        if text is None:
            return _set
        con = 'key.startswith(text)' if issuffix else 'text in key'
        for key in _set:
            if eval(con):
                rst.add(key)
        return rst

    @classmethod
    def utcoffset(cls, dt: datetime) -> timedelta | None:
        """
        用途:获取时区偏移量-本地时间和UTC时间之间的差异
        参数:dtortz:datetime(可无时区) or 时区
        返回:timedelta(有时区) or None(无时区)
        说明:utcoffset范围[-timedelta(hours=24),timedelta(hours=24)]
        """
        return dt.utcoffset()

    @classmethod
    def _utcoffset(cls, tz, dt_naive=datetime(2022, 1, 1)):
        # type:(tzinfo|None,datetime)->timedelta|None
        dt = dt_naive.replace(tzinfo=tz)
        return tz.utcoffset(dt) if tz else None

    # 获取时区偏移量
    @classmethod
    def get_offset(cls, tz: tzinfo) -> int:
        """
        用途:用内置函数datetime.utcoffset()获取时区偏移量
        参数:tz:tzinfo对象(pytz,dateutil.tz,ZoneInfo)

        实例:
        tzlocal.utils.get_system_offset()#28800

        zone='Asia/Shanghai'
        tzlocal.utils.get_tz_offset(pytz.timezone(zone))#28800
        tzlocal.utils.get_tz_offset(tz_.gettz(zone))#28800
        tzlocal.utils.get_tz_offset(ZoneInfo(zone))#28800
        tzlocal.utils.assert_tz_offset(pytz.timezone(zone))
        """
        return get_tz_offset(tz)

    @classmethod
    def get_sysoffset(cls):
        # type:()->int
        """用途:使用 built-in库time获取系统的时区偏移量"""
        return tzlocal.utils.get_system_offset()  # 28800

    @classmethod
    def dst(cls, dt: datetime) -> timedelta | None:
        """
        用途:返回夏令时时差
        参数:dtortz:datetime(可无时区) or 时区
        """
        return dt.dst()

    @classmethod
    def _dst(cls, tz, dt_naive=datetime(2022, 1, 1)):
        # type:(tzinfo|None,datetime)->timedelta|None
        dt = dt_naive.replace(tzinfo=tz)
        return tz.dst(dt) if tz else None

    @classmethod
    def name(cls, dt: datetime) -> str | None:
        """
        用途:返回时区名称 类似'Asia/Shanghai'
        参数:dtortz:datetime(可无时区) or 时区
        """
        tz = dt.tzinfo
        return tz if tz is None else str(tz)

    @classmethod
    def _name(cls, tz: ZoneInfo | None) -> str | None:
        return tz if tz is None else str(tz)

    @classmethod
    def key(cls, tz: ZoneInfo | None) -> str | None:
        """获取时区名称-等价tz if tz is None else str(tz)"""
        return tz.key if tz else None

    @classmethod
    def fromutc(cls, tz: tzinfo, dt_aware: datetime) -> datetime:
        """
        用途:将dt的时间作为UTC时间转为本地时间,时区相同或LMT->CST
             调整日期,以 self 的本地时间返回等效的日期时间
        参数:dt_aware:datetime(必须有时区,无时区报错)
        返回:datetime aware(有时区,时区=dt_aware.tzinfo)
             相当于dt_aware+timedelta(offset=8)#中国 注1

        注1:
            tz=ZoneInfo('Asia/Shanghai')
            dt1=datetime(2021, 1, 2, 3, 4, tzinfo=tz)
            dt2=dt1.tzinfo.fromutc(dt1)
            #datetime(2021, 1, 2, 11, 4, tzinfo=tz))
            dt2-dt1==timedelta(seconds=28800)

        备注:fromutc函数实现参考:
        def fromutc(self, dt):
            # raise ValueError error if dt.tzinfo is not self
            dtoff = dt.utcoffset()
            dtdst = dt.dst()
            # raise ValueError if dtoff is None or dtdst is None
            delta = dtoff - dtdst  # this is self's standard offset
            if delta:
                dt += delta   # convert to standard local time
                dtdst = dt.dst()
                # raise ValueError if dtdst is None
            if dtdst:
                return dt + dtdst
            else:
                return dt
        """
        return tz.fromutc(dt_aware)

    @classmethod
    def from_file(cls, tz=r'Asia\Chongqing', key=None):
        # type:(str,str|None)->datetime
        """
        用途:从文件创建ZoneInfo-key默认tz
        """
        file = (r'%s\%s' % (zoneinfo.TZPATH[0], tz)).replace('/', '\\')
        with open(file, 'rb') as f:
            return ZoneInfo.from_file(f, key=key if key else tz)

    @classmethod
    def is_aware(cls, dt):
        # type:(datetime)->bool
        """
        用途:是否是有时区信息
        返回:简单型(naive)无时区返回False
             感知型(aware)对象有时区返回True
        """
        return True if dt.tzinfo else False

    @classmethod
    def get_tzpath(cls) -> tuple:
        """
        返回:zoneinfo时区文件路径
           或r'C:\\ProgramData\anaconda3\\Lib\\site-packages\tzdata\\zoneinfo'
        #获取系统路径
        import sys
        def get_syspath():#C:\\ProgramData\anaconda3
            for v in sys.path:
                if v.endswith('anaconda3'):
                    return v
            raise ValueError
        """
        path = zoneinfo.TZPATH
        if path:
            return path
        return (cls.sys_path + r'\share\zoneinfo',)

    @classmethod
    def gen_tz(cls, tz: str) -> ZoneInfo:
        """
        用途:产生时区
        实例:
        # 时区数据放在zdata\\zoneinfo文件夹里,
          在Asia文件夹下有Chongqing,Shagnhai等城市,
        dt = datetime(2021, 12, 18, 11, 20, tzinfo=ZoneInfo("Asia/Tokyo"))
        print(dt)# 2021-12-18 11:20:00+09:00
        """
        return ZoneInfo(tz)

    # 重新加载缓存的本地区域
    @classmethod
    def _reload_localzone(cls):
        """
        用途:重新加载缓存的本地区域.如时区发生了变化,您需要调用它.

        实例:
        tzlocal._reload_localzone()
        #_PytzShimTimezone(ZoneInfo(key='Asia/Shanghai'), 'Asia/Shanghai')
        """
        return tzlocal.reload_localzone()

    # 确保系统的时区偏移量等于所找到的时区偏移量
    @classmethod
    def _assert_offset(cls, tz: tzinfo) -> None:
        """
        用途:确保系统的时区偏移量等于所找到的时区偏移量.
        说明:如错误可能配置时区错误
        参数:tz:tzinfo对象(pytz,dateutil.tz,ZoneInfo)
        """
        tzlocal.utils.assert_tz_offset(tz)

    @classmethod
    def localize(cls, dt, tz=None, ambiguous='raise', nonexistent='raise'):
        # type:(t_dttp,str|tzinfo|None,str|bool,str|timedelta)->t_dttp
        """
        用途:将dt_naive转换为本地时间或dt_aware去除时区,数据不变
        参数:
        dt: datetime pd.Timestamp or pd.DatetimeIndex
            无时区tz可为任意参数,有时区tz只能是None
        ambiguous:bool|str='raise',模糊时间处理
            * bool 包含一些标志来确定时间是否为dst
            (注意此标志只适用于不明确的秋季dst日期)
            * 'NaT' 模糊时间返回NaT
            * 'raise' 抛出AmbiguousTimeError
        nonexistent='raise'不存在时间的处理
            *'shift_forward':将把不存在的时间前移到最近的现有时间
            *'shift_backward:将不存在的时间移到最近的现有时间
            *'NaT':不存在时间返回NaT
            *timedelta: 通过timedelta改变不存在的时间
            *'raise':抛出NonExistentTimeError

        """
        if isinstance(dt, datetime):
            return pd.Timestamp(dt).tz_localize(
                tz, ambiguous, nonexistent).to_pydatetime(False)
        return dt.tz_localize(tz, ambiguous, nonexistent)

    @classmethod
    def to(cls, dt_aware, tz=None):
        # type:(t_convert,str|tzinfo|None)->t_dttp
        """
        用途:将时间转换为其他时区时间
        参数:
            dt: datetime pd.Timestamp or pd.DatetimeIndex有时区,无时区抛异常
            tz: str or tzinfo or None(返回utc时间)
        注意:不同于dt.replace,数值会改变
        """
        if isinstance(dt_aware, datetime):
            return pd.Timestamp(dt_aware).tz_convert(tz).to_pydatetime(False)
        return dt_aware.tz_convert(tz)

    @classmethod
    def to_utc(cls, dt):
        # type:(datetime)->datetime
        """
        用途:将datetime转为utc格式
        参数: dt: datetime有时区值日期,无时区dt默认本地时区
        """
        if not dt.tzinfo:
            dt = dt.replace(tzinfo=cls.win['tz'])
        delta = dt.utcoffset() - dt.dst()
        return (dt - delta).replace(tzinfo=ZoneInfo('UTC'))

    @dispatch
    def _(self):
        pass  # 虚拟方法


def test():
    a = _Tz
    us = r'US/Alaska'
    sh = r'Asia/Shanghai'
    tzutc = ZoneInfo('UTC')
    tzus = ZoneInfo(us)
    tzsh = ZoneInfo(sh)
    dt0 = datetime(2023, 4, 23, 18, 40, 55)
    dtsh = datetime(2023, 4, 23, 18, 40, 55, tzinfo=tzsh)
    dtus = datetime(2023, 4, 23, 18, 40, 55, tzinfo=tzus)

    delta0 = timedelta()
    delta1 = timedelta(hours=1)
    delta8 = timedelta(seconds=28800)
    delta_8 = timedelta(seconds=-28800)

    tzinfo = a.get_tzinfo(text='Asia', issuffix=True)
    print('tzinfo=', tzinfo)
    print('get_tzpath=', a.get_tzpath())

    assert(a.utcoffset(dt0) is None)
    assert (a.utcoffset(dtsh) == delta8)
    assert (a.utcoffset(dtus) == delta_8)

    assert (a._utcoffset(dt0.tzinfo) is None)
    assert (a._utcoffset(dtsh.tzinfo) == delta8)
    assert (a._utcoffset(dtus.tzinfo) == delta_8 - delta1)

    assert (a._utcoffset(None) is None)
    assert (a._utcoffset(tzsh) == delta8)
    assert (a._utcoffset(tzus) == delta_8 - delta1)

    assert (a.get_offset(tzsh) == delta8)
    assert (a.get_offset(tzus) == delta_8)

    assert (a.get_sysoffset() == 28800)

    assert (a.dst(dt0) is None)
    assert (a.dst(dtsh) == delta0)
    assert (a.dst(dtus) == delta1)

    assert (a._dst(None) is None)
    assert (a._dst(tzsh) == delta0)
    assert (a._dst(tzus) == delta0)

    assert (a.name(dt0) is None)
    assert (a.name(dtsh) == sh)
    assert (a.name(dtus) == us)

    assert (a._name(None) is None)
    assert (a._name(tzsh) == sh)
    assert (a._name(tzus) == us)

    assert (a.key(None) is None)
    assert (a.key(tzsh) == sh)
    assert (a.key(tzus) == us)

    assert(a.fromutc(tzsh, dtsh) == dtsh + delta8)

    tz = a.from_file(tz=sh, key=None)
    assert(tz != tzsh and str(tz) == str(tzsh))

    assert(not a.is_aware(dt0))
    assert (a.is_aware(dtsh))

    assert (a.gen_tz(sh) == tzsh)

    def op(dt1, dt2, delta):
        d1 = dt1.replace(tzinfo=None)
        d2 = dt2.replace(tzinfo=None)
        assert (d2 - d1 == delta)

    d2 = a.localize(dt0)
    op(dt0, d2, delta0)

    d2 = a.localize(dtsh)
    op(dtsh, d2, delta0)

    d2 = a.localize(dtus)
    op(dtus, d2, delta0)

    d2 = a.to_utc(dt0)
    op(dt0, d2, -delta8)

    d2 = a.to_utc(dtsh)
    op(dtsh, d2, -delta8)

    d2 = a.to_utc(dtus)
    op(dtus, d2, delta8 + delta1)

    d2 = a.localize(dt0, tzsh)
    d2 = a.to(d2, tzutc)
    op(dt0, d2, -delta8)

    d2 = a.to(dtsh, tzutc)
    op(dtsh, d2, -delta8)

    d2 = a.to(dtus, tzutc)
    op(dtus, d2, delta8)


if __name__ == '__main__':
    test()

 _date_buscal.py商业日历处理

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# @Project:TcyQuantTrade
# @Module:calendar

# @Author:       tcy
# @Date:         2023/1/7  3:05  - 2023/1/10 0:10
# @Emial:        3615693665@qq.com
# @Version:      3.1
# @Last Modified time:
# @City:         China Shanghai Songjiang Xiaokunshan
# @Company:  Weishi Machinery Manufacturing Priority


import os
import arrow
import pandas as pd
from dateutil import tz

import calendar
import chinese_calendar as cn_cal
from datetime import datetime, date, time, timezone, timedelta


__all__ = ['BCal']
t_date = datetime | date

today = date.today()


# 商业日历
class BCal(object):

    # 是闰年
    @classmethod
    def isleap(cls, year):
        # type:(int)->bool
        """用途:是闰年返回True,否则为false;calendar.isleap(2017)"""
        return calendar.isleap(year)

    # 闰年总数
    @classmethod
    def num_leapyear(cls, year_start, year_end):
        # type:(int,int)->int
        """
        用途:返回在[y1, y2)之间闰年总数;calendar.leapdays(2000, 2020);  # 5
        """
        return calendar.leapdays(year_start, year_end)

    # 一个月有多少天
    @classmethod
    def nday_month(cls, year, month, back_week=False):
        # type:(int,int,bool)->int|tuple[int,int]
        """
        用途:一个月有多少天
        输出:输出tuple (int,int)(back_week=True)或int(back_week=False)
            元素1,上月最后一天为星期几(0 - 6);0表示本月第一天是星期天
            元素2,这个月的天数
        实例:
        monthRange = calendar.monthrange(2018, 10)#(0, 31)
        """
        if back_week:
            return calendar.monthrange(year, month)
        return calendar.monthrange(year, month)[1]

    # 返回给定日期的周一到日 0-6
    @classmethod
    def week_day(cls, year, month, day):
        # type:(int,int,int)->int
        """
        用途:返回给定日期周一到日 0-6 calendar.weekday(2022,7,17)#6
        """
        return calendar.weekday(year, month, day)  # 周一到日 0-6

    @classmethod
    def _is_weekend_(cls, dt):  # 是否是周六或周日
        return calendar.weekday(dt.year, dt.month, dt.day) >= 5

    @classmethod
    def is_weekend(cls, dt):
        # type:(t_date)->bool
        """
        用途:是否是周六或周日
        参数:dt:datetime or date日期时间
        """
        return cls._is_weekend_(dt)

    # 是否是节假日
    @classmethod
    def is_holiday(cls, dt):
        # type:(t_date)->bool
        """
        用途:是否是节假日
        参数:dt:datetime or date日期时间
        """
        return cn_cal.is_holiday(dt)

    # 是否是工作日
    @classmethod
    def is_workday(cls, dt):
        # type:(t_date)->bool
        """
        用途:是否是工作日
        参数:dt:datetime or date日期时间
        """
        return cn_cal.is_workday(dt)

    @classmethod
    def _is_workday_weekend(cls, dt):
        # type:(t_date)->bool
        """
        用途:周末是否是调整的法定工作日
        参数:dt:datetime or date日期时间
        """
        if cn_cal.is_holiday(dt):
            return False
        return cls._is_weekend_(dt)

    @classmethod
    def _is_finday_(cls, dt):
        if cn_cal.is_workday(dt):
            return calendar.weekday(dt.year, dt.month, dt.day) < 5  # 周一到日 0-6
        return False

    @classmethod
    def is_finday(cls, dt):
        # type:(t_date)->bool
        """
        用途:是否是金融工作日
        参数:dt:datetime or date日期时间
        """
        return cls._is_finday_(dt)

    @classmethod
    def _is_night(cls, dt):
        # type:(t_date)->bool
        """用途:判断指定日期是否有夜盘"""
        if not cls._is_finday_(dt):
            return False
        return cls._is_finday_(dt + timedelta(3))

    @classmethod
    def _is_long_holiday_(cls, dt, back_startmidend=None):
        # type:(datetime|date,str)->bool
        if cn_cal.is_workday(dt):
            return False

        todayornextworkday = cn_cal.find_workday(0, dt)  # 5 6 7 8 9
        prebusday = cn_cal.find_workday(-1, dt)       # 5 6 7 1
        if todayornextworkday - prebusday <= timedelta(3):
            return False

        if back_startmidend is None:
            return True

        one = timedelta(1)
        today = date(dt.year, dt.month, dt.day)
        # 开始假期结束假期
        lst = cn_cal.get_dates(prebusday + one, todayornextworkday - one)
        if back_startmidend == 'start':
            return today == lst[0]
        elif back_startmidend == 'end':
            return today == lst[-1]
        else:
            return today != lst[0] and today != lst[-1]

    @classmethod
    def _is_long_holiday(cls, dt, back_startmidend=None):
        # type:(datetime|date,str)->bool
        """
        用途:判断指定日期是否是长假期
        参数:
        dt:datetime or date
        back_startmidend:str=None 'start','mid','end'
        返回:bool
            back_startmidend:str=None 判断指定日期是否是长假期
            back_startmidend:str='start'判断指定日期是否是长假期首天
            back_startmidend:str='mid'判断指定日期是否是长假期中间
            back_startmidend:str='end'判断指定日期是否是长假期尾天
        """
        return cls._is_long_holiday_(dt, back_startmidend, out)

    # ********************************************
    # 处理金融:股票期货开市日
    # ********************************************
    @classmethod
    def _get_finday_(cls, dt, offset: int) -> t_date:
        while True:
            dt += timedelta(offset)
            if cls._is_finday_(dt):
                return dt

    @classmethod
    def _get_finday_eq_(cls, dt, offset: int) -> t_date:
        while True:
            if cls._is_finday_(dt):
                return dt
            dt += timedelta(offset)

    @classmethod
    def _lt_workday(cls, dt):
        # type: (t_date) ->  date
        """用途:获取前一个工作日-不含今天(< dt)"""
        return cn_cal.find_workday(-1, dt)

    @classmethod
    def _le_workday(cls, dt):
        # type: (t_date) ->  date
        """用途:获取前一个工作日-含今天(<= dt)"""
        x = 0 if cn_cal.is_workday(dt) else -1
        return cn_cal.find_workday(x, dt)

    @classmethod
    def _gt_workday(cls, dt):
        # type: (t_date) ->  date
        """用途:获取后一个工作日-不含今天(> dt)"""
        x = 1 if cn_cal.is_workday(dt) else 0
        return cn_cal.find_workday(x, dt)

    @classmethod
    def _ge_workday(cls, dt):
        # type: (t_date) ->  date
        """用途:获取后一个工作日-含今天(>= dt)"""
        return cn_cal.find_workday(0, dt)

    @classmethod
    def lt_finday(cls, dt=today):
        # type: (t_date) ->  t_date
        """用途:获取前一个金融日-不含今天(< dt)"""
        return cls._get_finday_(dt, -1)

    @classmethod
    def pre_finday(cls, dt=today):
        # type: (t_date) ->  t_date
        """用途:获取前一个金融日-不含今天(< dt)"""
        return cls._get_finday_(dt, -1)

    @classmethod
    def le_finday(cls, dt=today):
        # type: (t_date) ->  t_date
        """用途:获取前一个金融日-含今天(<= dt)"""
        return cls._get_finday_eq_(dt, -1)

    @classmethod
    def gt_finday(cls, dt=today):
        # type: (t_date) ->  t_date
        """用途:获取后一个金融日-不含今天(> dt)"""
        return cls._get_finday_(dt, 1)

    @classmethod
    def next_finday(cls, dt=today):
        # type: (t_date) ->  t_date
        """用途:获取后一个金融日-不含今天(> dt)"""
        return cls._get_finday_(dt, 1)

    @classmethod
    def ge_finday(cls, dt=today):
        # type: (t_date) ->  t_date
        """用途:获取后一个金融日-含今天(>= dt)"""
        return cls._get_finday_eq_(dt, 1)

    @classmethod
    def nearest_finday(cls, dt):
        # type: (datetime) -> date
        """ 用途:获取当前工作日-距离dt日期最近的工作日"""
        if cls._is_finday_(dt):
            return dt.date()
        d1 = cls._get_finday_(dt, -1)
        d2 = cls._get_finday_(dt, 1)
        d = d1 if dt - d1 <= d2 - dt else d2
        return d.date()

    @classmethod
    def _range_date(cls, start, end):
        # type: (t_date ,t_date) ->  list[date]
        """
        用途:获取开始日期和结束日期之间的日期(包括开始日期和结束日期)
        返回:list date
        参数:start,end: str or datetime or date可交叉
        """
        return cn_cal.get_dates(start, end)

    @classmethod
    def _range_workday(cls, start, end):
        # type: (t_date ,t_date) ->  list[date]
        """
        用途:获取开始日期和结束日期之间的工作日(包括开始日期和结束日期)
        返回:list date
        参数:start,end: str or datetime or date可交叉
        """
        return cn_cal.get_workdays(start, end)

    @classmethod
    def range_finday(cls, start, end):
        # type: (t_date|str ,t_date|str) ->  list[date]
        """
        用途:获取开始日期和结束日期之间的工作日(包括开始日期和结束日期)
        返回:list date
        参数:start,end: str or datetime or date可交叉
        """

        def get_dt(startend):
            # type: (t_date |str) ->  datetime
            """用途:获取日期"""
            if isinstance(startend, str):
                return pd.Timestamp(startend).to_pydatetime(False)
            return startend

        start = get_dt(start)
        end = get_dt(end)
        lst = cn_cal.get_workdays(start, end)
        return [dt for dt in lst if cls._is_finday_(dt)]

    # 查找距离{deltadays} 天后的日期
    @classmethod
    def workday_offset(cls, dt, deltadays=0):
        # type: (t_date,int) -> date
        """
        用途:查找距离date{deltadays} 天后的工作日
        参数:
            deltadays: int 0 下一工作日(含今天),-1 上一工作日
            dt: dt.date | dt.datetime起点
        说明:
        今天是工作日,-1返回前一个工作日;
        今天不是工作日,-1返回类似周五(工作日)

        今天是工作日,0返回今天;
        今天不是工作日,0返回类似下周一

        今天是工作日,1返回下一个工作日;
        今天不是工作日,1返回类似下周二

        实例:
        d4=date(2022,7,14)#周四
        d5=date(2022,7,15)#周五
        d6=date(2022,7,16)#周六
        d7=date(2022,7,17)#周日
        d1=date(2022,7,18)#周一
        d2=date(2022,7,19)#周二

        x1=cn_cal.find_workday(-1,d4) #date(2022, 7, 13)
        x2=cn_cal.find_workday(0,d4)  #date(2022, 7, 14)
        x3=cn_cal.find_workday(1,d4)  #date(2022, 7, 15)

        x1=cn_cal.find_workday(-1,d5) #date(2022, 7, 14)
        x2=cn_cal.find_workday(0,d5)  #date(2022, 7, 15)
        x3=cn_cal.find_workday(1,d5)  #date(2022, 7, 18)

        x1=cn_cal.find_workday(-1,d6) #date(2022, 7, 15)
        x2=cn_cal.find_workday(0,d6)  #date(2022, 7, 18)
        x3=cn_cal.find_workday(1,d6)  #date(2022, 7, 19)

        y1=cn_cal.find_workday(-1,d7) #同上d6
        y2=cn_cal.find_workday(0,d7)
        y3=cn_cal.find_workday(1,d7)

        y1=cn_cal.find_workday(-1,d1) #同上d6
        y2=cn_cal.find_workday(0,d1)
        y3=cn_cal.find_workday(1,d1)

        y1=cn_cal.find_workday(-1,d2) #date(2022, 7, 18)
        y2=cn_cal.find_workday(0,d2)  #date(2022, 7, 19)
        y3=cn_cal.find_workday(1,d2)  #date(2022, 7, 20)
        """
        return cn_cal.find_workday(deltadays, dt)

    # 查找距离{deltadays} 天后的日期
    @classmethod
    def finday_offset(cls, dt, deltadays=0):
        # type: (t_date,int) -> date
        """
        用途:查找距离date{deltadays} 天后的金融日
        参数:
            deltadays: int 0 下一金融日(含今天),-1 上一金融日
            dt: dt.date | dt.datetime起点
        """
        if isinstance(dt, datetime):
            dt = dt.date()
        if deltadays == 0:
            return cls._get_finday_eq_(dt, deltadays)
        else:
            return cls._get_finday_(dt, deltadays)

    @classmethod
    def _str(cls, dt, fmt=None):
        # type: (t_date,str) -> str
        """
        用途:获取日期字符串
        参数:
            dt: dt.date | dt.datetime
            fmt:str  or None
                默认None返回iso字符串,否则返回指定格式字符串
        """
        return dt.strftime(fmt) if fmt else dt.isoformat()

    # ********************************************
    # other:
    # ********************************************
    @classmethod
    def get_tradingday(cls, dt, lastday=10):
        # type:(t_date|str,int)->date
        """
        用途:获取本月第10个交易日-金融工作日
        参数:
        dt:datetime or date or str or tuple[year,month]
        lastday:int=10本月第10个工作日
        """
        if isinstance(dt, str):
            dt = pd.Timestamp(dt).to_pydatetime(False)
            year, month = dt.year, dt.month
        elif isinstance(dt, (datetime, date)):
            year, month = dt.year, dt.month
        else:  # isinstance(dt, Sequence):
            year, month = dt[0], dt[1]

        # 一个月有多少天
        n = calendar.monthrange(year, month)[1]
        i = 0
        for day in range(1, n + 1):
            dt = date(year, month, day)
            if cls._is_finday_(dt):
                i += 1
            if i == lastday:
                return dt

        raise 'get last workday err.'

    @classmethod
    def df_tail_time(cls, df, datecol, outfmt=None, **kwargs):
        # type: (pd.DataFrame,str,str,Any) ->  str|datetime
        """
        用途:返回df指定最后一个工作日
        注意:df日期列必须按升序排列好
        返回:str or datetime
        参数:
        df:DataFrame 包含日期列
        datecol:str需要解析的日期的列名
            df[datecol]数据类型为int,float(需指定kwargs['unit']:str),sttime_t,str,t_dt
        fmt:str 按指定格式返回str;空返回datetime
        **kwargs:
            'tz':str时区
            'unit':str int or float 时间单位
            'start_date':str datetime.time类型的开始日期
        """
        def get_str_dt(ts: str):
            if ts.isdigit():
                ts = float(ts)
                unit = kwargs.get('unit', 's')
                ts = pd.Timestamp(ts, unit=unit)
            else:
                ts = pd.Timestamp(ts)
            return ts.tz_localize(tz).to_pydatetime(False)

        ts = df[datecol].iloc[-1]
        tz = kwargs.get('tz', None)
        if isinstance(ts, str):
            ts = get_str_dt(ts)
        elif isinstance(ts, datetime):
            pass
        elif isinstance(ts, pd.Timestamp):
            ts = ts.to_pydatetime(False)
        else:
            raise ValueError

        if not outfmt:
            return ts
        return ts.strftime(outfmt)

    @classmethod
    def is_update_file_time(cls, file, closetime='15:30:00'):
        # type: (str,str) ->  bool
        """
        用途:判断文件是否被更新,主要用于更新文件
        返回:bool 文件是否要更新
        参数:
        file:str期货数据保存的文件路径
        closetime:str期货闭市时间None默认15:30:00
        说明:
        1) 当距离今天最近的工作日的日期>file修改的时间
            (若不是工作日则file修改时间的前一个工作日)
            说明文件需要修改,返回True
        2) 若二者工作日相同,若文件修改时间>=15:30:00
            说明文件已经更新,返回False,否则返回True
        """
        timestamp = os.path.getmtime(file)
        file_dt = arrow.get(timestamp, tzinfo=tz.tzlocal()).datetime
        now = datetime.now()

        workday_range = cn_cal.get_workdays(file_dt, now)
        if not workday_range:
            return False
        if len(workday_range) > 1:
            return True
        mdate = workday_range[0]
        if mdate > file_dt.date():
            return True
        elif mdate < file_dt.date():
            return False
        else:
            close_time = arrow.get(closetime, 'HH:mm:ss').time()
            return file_dt.time() < close_time

    @classmethod
    def readdata_date(cls, dt=None, closetime='15:01:00'):
        # type:(t_date,str|time|datetime)->date
        """
        用途:
        今后是长假期,今晚无夜盘,返回今天(时间>15:01:00),
        正常星期有夜盘返回前一个工作日
        """
        if dt is None:
            dt = datetime.now()
        elif isinstance(dt, date):
            now = datetime.now()
            dt = now.replace(dt.year, dt.month, dt.day)

        if cn_cal.is_holiday(dt):
            return cls._get_finday_(dt.date(), -1)
        if cls._is_weekend_(dt):
            return cls._get_finday_(dt.date(), -1)

        ts = pd.Timestamp(closetime).time()
        if dt.time() < ts:
            return cls._get_finday_(dt.date(), -1)
        else:
            nextday = dt + timedelta(1)
            if cls._is_long_holiday_(nextday):
                return dt.date()
            else:
                return cls._get_finday_(dt.date(), -1)


def test(a=BCal):
    def test_calendar():
        assert (not a.isleap(2022))
        assert (a.isleap(2024))
        assert (a.num_leapyear(2000, 2022) == 6)
        assert (a.nday_month(2022, 3) == 31)
        assert (a.nday_month(2022, 2) == 28)
        assert (a.nday_month(2022, 2, True) == (1, 28))
        assert (a.week_day(2022, 8, 27) == 5)

    def test_cn_calendar():
        d_w1 = date(2022, 7, 11)
        d_w5 = date(2022, 7, 15)
        d_w6 = date(2022, 7, 16)
        d_w7 = date(2022, 7, 17)

        dt_w1 = datetime(2022, 7, 11, 12, 34, 56)
        dt_w5 = datetime(2022, 7, 15, 12, 34, 56)
        dt_w6 = datetime(2022, 7, 16, 12, 34, 56)
        dt_w7 = datetime(2022, 7, 17, 12, 34, 56)

        assert (a.is_workday(d_w1))
        assert (not a.is_holiday(d_w1))
        assert (not a.is_workday(d_w6))
        assert (a.is_holiday(d_w6))

        assert (a.finday_offset(d_w1, -1) == date(2022, 7, 8))
        assert (a.finday_offset(d_w1, 0) == date(2022, 7, 11))
        assert (a.finday_offset(d_w1, 1) == date(2022, 7, 12))

        dt1 = datetime(2022, 7, 8, 12, 34, 56)
        dt2 = datetime(2022, 7, 11, 12, 34, 56)
        dt3 = datetime(2022, 7, 12, 12, 34, 56)
        assert (a.finday_offset(dt_w1, -1) == dt1.date())
        assert (a.finday_offset(dt_w1, 0) == dt2.date())
        assert (a.finday_offset(dt_w1, 1) == dt3.date())

        assert (a.lt_finday(d_w1) == date(2022, 7, 8))
        assert (a.le_finday(d_w1) == date(2022, 7, 11))

        assert (a.lt_finday(d_w5) == date(2022, 7, 14))
        assert (a.le_finday(d_w5) == date(2022, 7, 15))

        assert (a.lt_finday(d_w6) == date(2022, 7, 15))
        assert (a.le_finday(d_w6) == date(2022, 7, 15))

        assert (a.lt_finday(d_w7) == date(2022, 7, 15))
        assert (a.le_finday(d_w7) == date(2022, 7, 15))

        assert (a.gt_finday(d_w1) == date(2022, 7, 12))
        assert (a.ge_finday(d_w1) == date(2022, 7, 11))

        assert (a.gt_finday(d_w5) == date(2022, 7, 18))
        assert (a.ge_finday(d_w5) == date(2022, 7, 15))

        assert (a.gt_finday(d_w6) == date(2022, 7, 18))
        assert (a.ge_finday(d_w6) == date(2022, 7, 18))

        assert (a.gt_finday(d_w7) == date(2022, 7, 18))
        assert (a.ge_finday(d_w7) == date(2022, 7, 18))

        dt = datetime(2022, 7, 11, 12, 34, 56)
        assert (a.nearest_finday(dt_w1) == dt_w1.date())

        dt = datetime(2022, 7, 15, 12, 34, 56)
        assert (a.nearest_finday(dt_w6) == dt.date())

        dt = datetime(2022, 7, 18, 12, 34, 56)
        assert (a.nearest_finday(dt_w7) == dt.date())

        dt = datetime(2022, 7, 15, 11, 34, 56)
        assert (a.nearest_finday(dt) == dt.date())

        lst = [date(2022, 2, 7),
               date(2022, 2, 8),
               date(2022, 2, 9),
               date(2022, 2, 10),
               date(2022, 2, 11),
               date(2022, 2, 14),
               date(2022, 2, 15)]
        assert (a.range_finday('2022-2-4', '2022-2-15') == lst)

        start, end = date(2022, 2, 4), date(2022, 2, 15)
        assert (a.range_finday(start, end) == lst)

    def test_comp():
        lst = [
            date(2022, 9, 22),  # week 4
            date(2022, 9, 23),  # week 5
            date(2022, 9, 24),  # week 6
            date(2022, 9, 25),  # week 7
            date(2022, 9, 26),  # week 1
            date(2022, 9, 27),  # week 2
            date(2022, 9, 28),  # week 3
            date(2022, 9, 29),  # week 4
            date(2022, 9, 30),  # week 5
            date(2022, 10, 1),  # week 6
            date(2022, 10, 2),  # week 7
            date(2022, 10, 3),  # week 1
            date(2022, 10, 7),  # week 5
            date(2022, 10, 8),  # week 6
            date(2022, 10, 9),  # week 7
            date(2022, 10, 10),  # week 1
            date(2022, 10, 11),  # week 2
        ]

        def foo(op, text='test comp : <\n'):
            print(text)
            for dt in lst:
                bus = op(dt)
                s1 = dt.isoformat()
                s2 = bus.isoformat()
                w1 = a.week_day(dt.year, dt.month, dt.day) + 1
                w2 = a.week_day(bus.year, bus.month, bus.day) + 1
                print('week=%s,dt=%s' % ((w1, w2), (s1, s2)))
            print()

        foo(a.lt_finday, text='test comp : <\n')
        foo(a.le_finday, text='test comp : <=\n')
        foo(a.gt_finday, text='test comp : >\n')
        foo(a.ge_finday, text='test comp : >=\n')
        foo(a.pre_finday, text='test pre_finday : \n')

    def test_file_time():
        file = r'C:\Program Files\Windows Media Player\wmplayer.exe'
        print('is_update_file_time=', a.is_update_file_time(file))  # True
        lst1 = ['2022-7-11 13:15:35', '2022-7-12 14:45:58']
        lst2 = [1, 2]
        d = {'date': lst1, 'value': lst2}
        df = pd.DataFrame(d)
        dt1 = datetime(2022, 7, 12, 14, 45, 58)
        assert (a.df_tail_time(df, 'date') == dt1)

    # 获取本月第10个交易日 - 金融工作日
    def test_get_last_tradingday():
        def foo(year=2022, mon=1, lastday=10, rstday=None):
            s = a.get_tradingday((year, mon), lastday)
            assert(s == date(year, mon, rstday))

        foo(mon=3, rstday=14)
        foo(mon=5, rstday=18)
        foo(mon=7, rstday=14)
        foo(mon=8, rstday=12)
        foo(mon=9, rstday=15)
        foo(mon=11, rstday=14)
        foo(mon=12, rstday=14)

    def test_lt_long_holiday():
        print('\n test readdata_date start...')
        print('readdata_date(dt=None)=', a.readdata_date(dt=None))

        def get_data():
            dts1 = [datetime(2022, 9, 22, 12, 30),  # 周4
                    datetime(2022, 9, 23, 12, 30),  # 周5
                    datetime(2022, 9, 24, 12, 30),  # 周6
                    datetime(2022, 9, 25, 12, 30),  # 周7
                    datetime(2022, 9, 26, 12, 30),  # 周1
                    datetime(2022, 9, 27, 12, 30),  # 周2
                    datetime(2022, 9, 29, 12, 30),  # 周4
                    datetime(2022, 9, 30, 12, 30),  # 周5
                    datetime(2022, 10, 1, 12, 30),  # 周6
                    datetime(2022, 10, 2, 12, 30),  # 周7
                    datetime(2022, 10, 8, 12, 30),  # 周6
                    datetime(2022, 10, 9, 12, 30),  # 周7
                    datetime(2022, 10, 10, 12, 30),  # 周1
                    datetime(2022, 10, 11, 12, 30),  # 周2
                    ]
            dts2 = [
                datetime(2022, 9, 22, 15, 30),  # 周4
                datetime(2022, 9, 23, 15, 30),  # 周5
                datetime(2022, 9, 24, 15, 30),  # 周6
                datetime(2022, 9, 25, 15, 30),  # 周7
                datetime(2022, 9, 26, 15, 30),  # 周1
                datetime(2022, 9, 27, 15, 30),  # 周2
                datetime(2022, 9, 29, 15, 30),  # 周4
                datetime(2022, 9, 30, 15, 30),  # 周5
                datetime(2022, 10, 1, 15, 30),  # 周6
                datetime(2022, 10, 2, 15, 30),  # 周7
                datetime(2022, 10, 8, 15, 30),  # 周6
                datetime(2022, 10, 9, 15, 30),  # 周7
                datetime(2022, 10, 10, 15, 30),  # 周1
                datetime(2022, 10, 11, 15, 30),  # 周2
            ]
            return dts1, dts2

        dts1, dts2 = get_data()
        for dt1, dt2 in zip(dts1, dts2):
            v1 = a.readdata_date(dt1).isoformat()
            v2 = a.readdata_date(dt2).isoformat()
            w1 = a.week_day(dt1.year, dt1.month, dt1.day) + 1
            w2 = a.week_day(dt2.year, dt2.month, dt2.day) + 1
            s1 = dt1.isoformat()
            s2 = dt2.isoformat()
            print('week=%s,dt=%s,new=%s', (w1, w2), (s1, s2), (v1, v2))

        print('\ntest nearest_finday...')
        for dt1, dt2 in zip(dts1, dts2):
            v1 = a.nearest_finday(dt1).isoformat()
            v2 = a.nearest_finday(dt2).isoformat()
            w1 = a.week_day(dt1.year, dt1.month, dt1.day) + 1
            w2 = a.week_day(dt2.year, dt2.month, dt2.day) + 1
            s1 = dt1.isoformat()
            s2 = dt2.isoformat()
            print('week=%s,dt=%s,new=%s', (w1, w2), (s1, s2), (v1, v2))

    test_calendar()
    test_cn_calendar()
    test_comp()
    test_file_time()
    test_get_last_tradingday()
    test_lt_long_holiday()


if __name__ == '__main__':
    test()

 date.py基本日期处理

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# @Project:TcyQuantTrade
# @Module:date

# @Author:       tcy
# @Date:         2022/9/29  01:14 - 2023/4/27 2:10:48
# @Emial:        3615693665@qq.com
# @Version:      2.2
# @Last Modified time:
# @City:         China Shanghai Songjiang Xiaokunshan
# @Company:  Weishi Machinery Manufacturing Priority


import re
import arrow
import numpy as np
import pandas as pd
from plum import Dispatcher
from zoneinfo import ZoneInfo
from dateutil import parser, relativedelta

from types import NoneType
from time import (struct_time, time as time_s, time_ns,
                  localtime, gmtime, mktime, strptime)
from datetime import datetime, date, time, timedelta, tzinfo

from tool.date._date_buscal import BCal
from tool.date._date_tz import _Tz


t_Arr = arrow.Arrow
t_if = int | float
t_slstno = str | list | NoneType
t_ifst = int | float | struct_time
t_tuplstset = tuple | list | set
t_tzinfos = str | tzinfo | NoneType

t_dt = datetime
t_st = struct_time
t_dt64 = np.datetime64
t_period = pd._libs.tslibs.period.Period
t_ts = pd._libs.tslibs.timestamps.Timestamp
t_relativedelta = relativedelta.relativedelta

t_sdate = str | date
t_stime = str | time
t_tsdt = t_ts | datetime
t_dt_d = datetime | date
t_tsdt_d = t_ts | datetime | date
t_tsdt_d_t = t_ts | datetime | date | time
t_sidt_d = str | int | datetime | date
t_stsdt_d_p_64 = str | t_tsdt_d | t_dt64 | pd.Period
t_sstarrtsdt_d_64 = str | t_st | t_Arr | t_tsdt_d | t_dt64

dispatch = Dispatcher()


def parse(
        ts: str,
        tzinfos: dict | str | tzinfo = None,
        fuzzy: bool = False,
        dayfirst: bool = None,
        yearfirst: bool = None,
        ignoretz: bool = False,
        default: t_dt = None,
        parserinfo=None):
    """
    函数:parser.parse(timestr, parserinfo=None, **kwargs):
    用途:parser解析字符串成datetime
    参数:
    ts:str包含日期/时间戳的字符串
    parserinfo:=None 为None parserinfo类对象用默认参数构造
    kwargs:
    default:=None默认datetime如default=datetime(2023, 9,25)
    ignoretz:bool为True忽略时区对象,返回naive datetime
    tzinfos:映射时区dict or func
        1)时区别名映射时区名称到时区的字典 注1
        {"BRST": -10800};{"CDT": tz.gettz('US/Central')};
        2)两参数函数tzoffset(tzname,tzoffset)返回时区
        dateutil.tz.tzoffset("BRST", -10800)
        在此做了修改接受str or tzinfo时区对象(同时忽略原时区偏移量如+8:00)
    fuzzy=True中英文模糊解析
    dayfirst=True有歧义的解析
    yearfirst=True有歧义的解析

    说明:
    可用时间日期的英文单词,可用横线,逗号,空格等做分隔符.
    没指定时间默认是0点,没指定日期默认是今天,没指定年份默认是今年

    只有月日时,parser会将分隔符前面的数字解析为月份,后面的为日
    当有年份时,在前面的月份超出范围时,会自动判断哪个是月哪个是日

    注1:世界国家UTC时区缩写
    https://www.yestupa.com/countries-time-zone-and-abbreviation

    实例:
    #能正确解析
    20200909T102020-8:00
    2013-05-05 12:30:45CST+8:00

    #不能正确解析
    2013-05-05 12:30:45 America/Chicago#错误
    2013-05-05 12:30:45+8:00 CST#错误
    2012-01-19 17:21:00 BRST#警告
    Thu Sep 25 10:20:30 BRST 2003#警告

    实例1:
    from dateutil.parser import parse
    from dateutil.tz import gettz
    tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")}
    parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
    #datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200))
    parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos)
    #datetime.datetime(2012, 1, 19, 17, 21,
        tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago'))

    #将时区偏移量编码为字符串:
    dt = parse('2017-08-28 06:08:20-06:00')
    dt.tzinfo #tzoffset(None, -21600)

    实例2:
    datetime.now()
    #datetime(2022, 8, 27, 23, 54, 58, 289905)

    parse('2022-10-21')#datetime(2022, 10, 21, 0, 0)

    #逗号分隔符,只有月-日时要把月放在前面;有年份时年份要放在后面
    parse('3,15')#datetime(2022, 3, 15, 0, 0)

    '2022-10-21','20221021','2022/10/21'年-月-日,'10-21','10/21'
    '3/8','3-8','3/8/2022','2022/3/8'
    '8/3','8-3','8/3/2022',
    '21/10'#ValueError
    '3/15/2022','15/3/2022'自动识别月和日,'Mar 15','Mar15','15 Mar','Mar 15 2022','2022 Mar15'

    #只识别到了前面的日,月将本月11月作为默认月
    parse('15,3')#datetime(2022, 11, 15, 0, 0)
    parse('3,15,2022')#datetime(2022, 3, 15, 0, 0)
    parse('2022,3,15')#ValueError

    实例3:
    from dateutil.parser import *
    from dateutil.tz import *
    from datetime import *

    TZOFFSETS = {'BRST': -10800}
    BRSTTZ = tzoffset('BRST', -10800)
    DEFAULT=datetime(2003, 9,25)

    # 解析结果与字符串中的字符串顺序关联不大
    parse('Thu Sep 25 10:20:30 BRST 2003', tzinfos=TZOFFSETS)
    #datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))

    parse('2003 Thu Sep 25 10:20:30 BRST', tzinfos=TZOFFSETS)
    #datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))

    parse('2003 10:20:30 Thu Sep 25 BRST', tzinfos=TZOFFSETS)
    #datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))

    # 正常情况下是这样的
    parse('2003 10:20:30 Thu Sep 25')#datetime(2003, 9, 25, 10, 20, 30)
    parse('Thu Sep 25 10:20:30 BRST 2003', ignoretz=True)#datetime(2003, 9, 25, 10, 20, 30)

    # 缺省必要的数据时,可以采用default值为补全
    parse('10:20:30 Thu Sep 25', default=DEFAULT)#datetime(2003, 9, 25, 10, 20, 30)
    parse('Thu Sep 25', default=DEFAULT)#datetime(2003, 9, 25, 0, 0)

    # 支持解析标准时间格式
    parse('2020-09-09T10:20:20.5-8:00')
    #datetime(2020, 9, 9, 10, 20, 20, 500000, tzinfo=tzoffset(None, -28800))
    parse('2020-09-09T10:20')#datetime(2020, 9, 9, 10, 20)
    parse('2020-09-09T10')#datetime(2020, 9, 9, 10, 0)
    parse('2020-09-09')#datetime(2020, 9, 9, 0, 0)

    # 支持解析不带分隔符的时间格式
    parse('20200909T102020-8:00')#datetime(2020, 9, 9, 10, 20, 20, tzinfo=tzoffset(None, -28800))
    parse('20200909T102020')#datetime(2020, 9, 9, 10, 20, 20)
    parse('20200909T1020')#datetime(2020, 9, 9, 10, 20)
    parse('20200909T1020')#datetime(2020, 9, 9, 10, 20)
    parse('20200909')#datetime(2020, 9, 9, 0, 0)
    parse('20200909102020')#datetime(2020, 9, 9, 10, 20, 20)

    # 支持有歧义的解析
    parse('10-09-2020')#datetime(2020, 10, 9, 0, 0)
    parse('10-09-2020', dayfirst=True)#datetime(2020, 9, 10, 0, 0)
    parse('10-09-20', dayfirst=True)#datetime(2020, 9, 10, 0, 0)
    parse('10-09-20', yearfirst=True)#datetime(2010, 9, 20, 0, 0)

    # 支持中英文模糊解析
    s = 'today is 25 of Oct 2020 exactly at 10:20:20 with timezone +08:00'
    parse(s, fuzzy=True)#datetime(2020, 10, 25, 10, 20, 20, tzinfo=tzoffset(None, 28800))
    s = '今天是2020年10月20日 10点23分49秒'
    parse(s, fuzzy=True)#datetime(2010, 10, 20, 23, 49)
    s = '今天是2020年10月20日,明天有个预约'
    parse(s, fuzzy=True)#datetime(2022, 10, 20, 0, 0)

    """
    def get_dt(tzinfos):
        return parser.parse(
            ts,
            parserinfo,
            default=default,
            ignoretz=ignoretz,
            tzinfos=tzinfos,
            fuzzy=fuzzy,  # 中英文模糊解析
            dayfirst=dayfirst,  # 有歧义的解析
            yearfirst=yearfirst  # 有歧义的解析
        )

    if isinstance(tzinfos, (dict, NoneType)):
        return get_dt(tzinfos)
    else:
        dt = get_dt(None)
        tz = ZoneInfo(tzinfos) if isinstance(tzinfos, str) else tzinfos
        return dt.replace(tzinfo=tz)


class _Arrow:
    """
    其他arrow.Arrow库函数:
    ts.format() #2022-09-20 02:21:57+08:00
    ts.format('YYYY-MM-DD HH:mm:ss ZZ')#同上
    ts.ctime() # 返回日ctime格式Tue Sep 20 02:21:57 2022
    ts.weekday() # 以整数形式返回星期几(0-6)
    ts.isoweekday() # 以整数形式返回一周中的ISO日(1-7)
    ts.toordinal()# 返回日期的格雷戈里序数738418
    ts.isocalendar()# 返回3元组(ISO年,ISO周数,ISO工作日)
    #datetime.IsoCalendarDate(year=2022, week=38, weekday=2)

    只得到任意单位时间中的最大值或最小值
    arrow.utcnow().floor('hour')
    arrow.utcnow().ceil('hour')
    arrow.utcnow().floor('day')
    arrow.utcnow().ceil('day')

    获取任意时间单位的时间跨度
    arrow.utcnow().span('hour')
    arrow.utcnow().span('year')
    arrow.utcnow().span('month')
    arrow.utcnow().span('day')
    #(<Arrow [2022-09-19T00:00:00+00:00]>,
        <Arrow [2022-09-19T23:59:59.999999+00:00]>)

    参考:
    https://blog.csdn.net/weixin_44799217/article/details/126394161
    """
    fmt = {
        'y': 'YYYY',  # 2000, 2001, 2002 … 2012, 2013
        'y2': 'YY',  # 00, 01, 02 … 12, 13
        'm_en': 'MMMM',  # January, February, March … 1
        'm_en3': 'MMM',  # Jan, Feb, Mar … 1
        'm2': 'MM',  # 01, 02, 03 … 11, 12
        'm1': 'M',  # 1, 2, 3 … 11, 12
        'd_y3': 'DDDD',  # 001, 002, 003 … 364, 365,#Day of Year 一年中的一天
        'd_y1': 'DDD',  # 1, 2, 3 … 364, 365
        'd_m2': 'DD',  # 01, 02, 03 … 30, 31,#Day of Month 月份中的某天
        'd_m1': 'D',  # 1, 2, 3 … 30, 31
        'd_m_en2': 'Do',  # 1st, 2nd, 3rd … 30th, 31st
        'w_en': 'dddd',  # Monday, Tuesday, Wednesday … 2 Day of Week 星期几
        'w_en3': 'ddd',  # Mon, Tue, Wed … 2
        'w': 'd',  # 1, 2, 3 … 6, 7
        'w_iso': 'W',  # 2011-W05-4, 2019-W17 ISO week date ISO 星期日期
        'h2': 'HH',  # 00, 01, 02 … 23, 24 Hour 小时
        'h1': 'H',  # 0, 1, 2 … 23, 24
        'h12_2': 'hh',  # 01, 02, 03 … 11, 12
        'h12_1': 'h',  # 1, 2, 3 … 11, 12
        'AM_PM': 'A',  # AM, PM, 上午下午am, pm 1
        'am_pm': 'a',  # am, pm 1
        'min2': 'mm',  # 00, 01, 02 … 58, 59 Minute 分钟
        'min1': 'm',  # 0, 1, 2 … 58, 59
        's2': 'ss',  # 00, 01, 02 … 58, 59 Second 第二
        's1': 's',  # 0, 1, 2 … 58, 59
        's_sub': 'S',  # … 0, 02, 003, 000006, 123123123123… 3  Sub-second 亚秒级
        'tz': 'ZZZ',  # Asia/Baku, Europe/Warsaw, GMT … 4  Timezone 时区
        'tz_soffset': 'ZZ',  # -07:00, -06:00 … +06:00, +07:00, +08, Z
        'tz_offset': 'Z',  # -0700, -0600 … +0600, +0700, +08, Z
        'f_s': 'X',  # 1381685817, 1381685817.915482 … 5 Seconds Timestamp 秒时间戳
        'f_msus': 'x',  # 1569980330813, 1569980330813221 ms or µs Timestamp ms 或 µs 时间戳
    }
    tz_local = ZoneInfo('Asia/Shanghai')
    kws = {
        'locale': 'zh',  # '指定解析器指定区域en-us'
        'normalize_whitespace': True  # 规范化日期str中冗余空格(空格制表符换行符)
    }

    @classmethod
    @dispatch
    def get(cls, tz: t_tzinfos = None) -> t_Arr:
        """
        用途:获取当前时间(tz=None本地时间)
        参数:tz = None 指定的本地时区默认上海
        实例:
        arrow.get()
        #<Arrow [2013-05-08T05:51:43.316458+00:00]>
        arrow.get(arrow.utcnow())
        #<Arrow [2013-10-23T15:21:54.354846+00:00]>

        arrow.get(tz.tzlocal())
        #<Arrow [2013-05-07T22:57:28.484717-07:00]>
        """
        _tz = tz if tz else cls.tz_local
        return arrow.get(tzinfo=_tz, **cls.kws)

    @classmethod
    @dispatch
    def get(cls, ts: t_ifst, tz: t_tzinfos = None,
            isns: bool = False) -> t_Arr:
        """
        用途:获取时间戳,time.struct_time的时间(tz=None本地时间)
        参数:
        tz = None 指定的本地时区默认上海
        isns:bool=False ts为int是否是ns
        实例:
        arrow.get(1367992474.293378)
        #<Arrow [2013-05-08T05:54:34.293378+00:00]>
        arrow.get(1367992474)
        #<Arrow [2013-05-08T05:54:34+00:00]>
        """
        if isns and isinstance(ts, int):
            ts /= 10**9
        _tz = tz if tz else cls.tz_local
        return arrow.get(ts, tzinfo=_tz, **cls.kws)

    @classmethod
    @dispatch
    def get(cls, ts: str, fmt: t_slstno = None, tz: t_tzinfos = None) -> t_Arr:
        """
        用途:获取str时间(tz=None本地时间)
        参数:
        ts: str
        fmt:str|list|NoneType=None 按指定格式解析
        tz = None 指定的本地时区默认上海;若ts有时区则转为tz时区
        实例:
        #ISO 8601格式str
        arrow.get('2013-09-29T01:26:43.830580')
        #<Arrow [2013-09-29T01:26:43.830580+00:00]>

        #ISO 8601格式str基于format
        arrow.get('20160413T133656.456289')
        #<Arrow [2016-04-13T13:36:56.456289+00:00]

        #格式解析:
        arrow.get('2013-05-05 12:30:45 America/Chicago',
            'YYYY-MM-DD HH:mm:ss ZZZ')
        #<Arrow [2013-05-05T12:30:45-05:00]>

        #参数:str,list:
        arrow.get('2013-05-05 12:30:45',
            ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss'])
        #<Arrow [2013-05-05T12:30:45+00:00]>
        """
        pat = r'[+-]\d{2}[:]\d{2}|[a-z]{2,}'
        if re.search(pat, ts, re.I):
            if fmt:
                a = arrow.get(ts, fmt, **cls.kws)
            else:
                a = arrow.get(ts, **cls.kws)
            return a.to(tz) if tz else a
        else:
            _tz = tz if tz else cls.tz_local
            if fmt:
                return arrow.get(ts, fmt, tzinfo=_tz, **cls.kws)
            else:
                return arrow.get(ts, tzinfo=_tz, **cls.kws)

    @classmethod
    @dispatch
    def get(cls, ts: t_dt_d, tz: t_tzinfos = None) -> t_Arr:
        """
        用途:获取datetime时间(ts无时区默认本地时间)
        tz = None 指定的本地时区默认上海;若ts有时区则转为tz时区
        """
        if type(ts) == date:
            ts = datetime(ts.year, ts.month, ts.day)
        if ts.tzinfo:
            a = arrow.get(ts, **cls.kws)
            return a.to(tz) if tz else a
        else:
            _tz = tz if tz else cls.tz_local
            return arrow.get(ts, tzinfo=_tz, **cls.kws)

    @classmethod
    @dispatch
    def get(cls, ts: t_tuplstset, tz: t_tzinfos = None) -> t_Arr:
        """
        用途:获取日期元祖的时间(ts无时区默认本地时间)
        tz = None 指定的本地时区默认上海
        实例:
        arrow.get(2013, 5, 5, 12, 30, 45)
        #<Arrow [2013-05-05T12:30:45+00:00]>
        arrow.get(2013, 5, 5, 12, 30, 45,tzinfo=tzsh)
        #<Arrow [2013-05-05T12:30:45+08:00]>
        """
        _tz = tz if tz else cls.tz_local
        return arrow.get(*ts, tzinfo=_tz, **cls.kws)

    @classmethod
    def to(cls, a: t_Arr, tz: str | tzinfo):
        """ts.to('US/Pacific')"""
        return a.to(tz)

    @classmethod
    def dt(cls, a: t_Arr) -> t_dt:
        return a.datetime

    @classmethod
    def deltz(cls, a: t_Arr) -> t_dt:
        return a.datetime.replace(tzinfo=None)

    @classmethod
    def attr(cls, a: t_Arr):
        return {
            'year': a.year,  # 当前年
            'mon': a.month,  # 当前月份
            'day': a.day,  # 当前天
            'hour': a.hour,  # 当前第几个小时
            'min': a.minute,  # 当前多少分钟
            'sec': a.second,  # 当前多少秒
            'itimestamp': a.int_timestamp,  # 获取时间戳
            'ftimestamp': a.float_timestamp,  # 浮点数时间戳
            'dt_naive': a.naive,  # datetime 无时区
            'tz': a.tzinfo,
        }

    @classmethod
    def shift(cls, a: t_Arr, **kws) -> t_Arr:
        """
        用途:获取某个时间之前或之后的时间
        实例:
        ts = arrow.now()  # 当前时间2022-09-20T02:20:57.385667+08:00
        ts.shift(years=1, months=-1)# <Arrow [2024-03-24T23:12:58.184004+08:00]>
        ts.shift(weeks=-3) # 三周前2022-08-30T02:20:57.385667+08:00
        ts.shift(weeks=+3) # 三周后2022-10-11T02:20:57.385667+08:00
        ts.shift(days=-1)  # 一天前2022-09-19T02:20:57.385667+08:00
        ts.shift(days=1)   # 一天后2022-09-21T02:20:57.385667+08:00
        ts.shift(weekday=6)# 距离最近ts的星期日,weekday从0到6
                                    2022-09-25T02:20:57.385667+08:00
        人性化输出:a.humanize()
        a = arrow.now()         #2022-09-20T01:24:11.439204+08:00
        a.shift(hours=-1) # an hour ago
        a.shift(hours=-1).humanize() # 相对于当前时间 2022-09-20T04:24:11.439204+08:00
        a.shift(hours=2) # in 2 hours
        a.shift(hours=2).humanize()  # 相对于参数时间 =2小时后
        a.shift(hours=2).humanize(locale='zh') # locale参数可以指定地区语言
                                            2022-09-20T01:24:11.439204+08:00
        """
        return a.shift(**kws)

    @classmethod
    def replace(cls, a: t_Arr, **kws) -> t_Arr:
        """
        用途:时间替换
        返回:一被替换后arrow对象,原对象不变
        实例:
        ts.replace(year=2023, month=3, day=6,)
        ts.replace(hour=12, minute=12, second=12, ).to('UTC')
        ts.replace(microsecond=12).to('local').naive
        """
        return a.replace(**kws)

    @classmethod
    def test(cls):
        nowarr = cls.get()
        print('*** test arrow ***\n')
        print('now=', nowarr, datetime.now())
        print('attr=', cls.attr(nowarr), '\n')
        now = datetime.now()
        f = now.timestamp()

        a = cls.get(f)
        assert(cls.deltz(a) == now)
        tup = now.timetuple()
        assert (cls.deltz(a) == now)

        s = '2023-09-29T01:26:43.830580'
        a = cls.get(s)
        dt = pd.Timestamp(s).to_pydatetime(False)
        assert (cls.deltz(a) == dt)

        s = '20230413T133656.456289'
        a = cls.get(s)
        dt = pd.Timestamp(s).to_pydatetime(False)
        assert (cls.deltz(a) == dt)

        s = '2023-05-05 12:30:45 America/Chicago'
        fmt = 'YYYY-MM-DD HH:mm:ss ZZZ'
        a = cls.get(s, fmt=fmt)
        dt = datetime(2023, 5, 5, 12, 30, 45)
        assert (cls.deltz(a) == dt)

        s = '2023-05-05 12:30:45'
        fmt = ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss']
        a = cls.get(s, fmt=fmt)
        dt = pd.Timestamp(s).to_pydatetime(False)
        assert (cls.deltz(a) == dt)

        a = cls.get(dt)
        assert (cls.deltz(a) == dt)

        a = cls.get(dt.date())
        dt1 = datetime(dt.year, dt.month, dt.day)
        assert (cls.deltz(a) == dt1)

        tup = (now.year, now.month, now.day, now.hour,
               now.minute, now.second, now.microsecond)
        a = cls.get(tup)
        assert (cls.deltz(a) == now)


class Date(object):

    tzlocal = ZoneInfo('Asia/Shanghai')
    fmt = {
        'dt': '%Y-%m-%d %H:%M:%S',
        'dtf': '%Y-%m-%d %H:%M:%S.%f',
        'dtno': '%Y%m%d%H%M%S',
        'dtfno': '%Y%m%d%H%M%S.%f',
        'date': '%Y-%m-%d',
        'dateno': '%Y%m%d',
        'time': '%H:%M:%S',
        'timeno': '%H%M%S',
        'arw': 'YYYY-MM-DD HH:mm:ss',
        'arwno': 'YYYYMMDDHHmmss',
    }
    Tz = _Tz  # 时区类
    Arr = _Arrow
    Cal = BCal  # 日历类

    @classmethod
    @dispatch
    def to_ts(cls, ts: str, tz: t_tzinfos = None) -> t_ts:
        """
        用途:str转pd.Timestamp
        说明:str无时区则数值相同;有时区若指定新时区则不同
        参数:
        ts:str时间
           格式1:
           2020-03-14 15:32:52,
           2020-03-14T15:32:52.192548651
           2022-08-21 17:29:25.549638+0800
           格式2:
           20200314153252192548651
           20200314153252
        tz:str,tzinfo or None时区
        说明:
           格式1月日时分秒可为1位,后面为微秒192548+651纳秒 T可为空格
           格式2月日时分秒必为2位,后面为微秒192548+651纳秒
           格式1,2年为4位;可输入年(月,日,时分秒,微秒均可省略)
        注意:忽略ns
        """
        try:
            return pd.Timestamp(ts, tz=tz)
        except Exception:
            def str_adjust(s):
                if not s.isdigit():
                    return s
                n = len(s)
                match n:
                    case 4:
                        return '%s-01-01 00:00:00' % s
                    case 6:
                        return '%s-%s-01 00:00:00' % (
                            s[:4], s[4:6])
                    case 8:
                        return '%s-%s-%s 00:00:00' % (
                            s[:4], s[4:6], s[6:8])
                    case 10:
                        return '%s-%s-%s %s:00:00' % (
                            s[:4], s[4:6], s[6:8], s[8:])
                    case _:
                        return s

            return pd.Timestamp(str_adjust(ts), tz=tz)

    @classmethod
    @dispatch
    def to_ts(cls, i: int, unit: str = 'ns', tz: t_tzinfos = None,
              isdttimestamp: bool = True) -> t_ts:
        """
        参数:
        unit : str 'D', 'h', 'm', 's', 'ms', 'us', and 'ns'
        实例:2022-08-21 23:55:35
        t1=time.time()
        t2=time.time_ns()# (1661097335.9809928, 1661097335980992800)

        pd.Timestamp(t1,unit='s')  #Timestamp('2022-08-21 15:55:35.980992794')
        pd.Timestamp(t2,unit='ns')#Timestamp('2022-08-21 15:55:35.980992800')

        pd.Timestamp(t1,unit='s',tz='Asia/Shanghai'),
        #Timestamp('2022-08-21 23:55:35.980992794+0800', tz='Asia/Shanghai')
        pd.Timestamp(t2,unit='ns',tz='Asia/Shanghai')
        #Timestamp('2022-08-21 23:55:35.980992800+0800', tz='Asia/Shanghai')
        """
        if not isdttimestamp or tz:
            return pd.Timestamp(i, unit=unit, tz=tz)
        ts = pd.Timestamp(i, unit=unit, tz=cls.tzlocal)
        return ts.replace(tzinfo=None)

    @classmethod
    @dispatch
    def to_ts(cls, f: float, unit: str = 's', tz: t_tzinfos = None,
              isdttimestamp: bool = True) -> t_ts:
        """unit : str 'D', 'h', 'm', 's', 'ms', 'us', and 'ns' """
        if not isdttimestamp or tz:
            return pd.Timestamp(f, unit=unit, tz=tz)
        ts = pd.Timestamp(f, unit=unit, tz=cls.tzlocal)
        return ts.replace(tzinfo=None)

    @classmethod
    @dispatch
    def to_ts(cls, dt: t_st, tz: t_tzinfos = None) -> t_ts:
        return pd.Timestamp(*dt[:6], tzinfo=tz)

    @classmethod
    @dispatch
    def to_ts(cls, args: t_tuplstset, **kws) -> t_ts:
        return pd.Timestamp(*args, **kws)

    @classmethod
    @dispatch
    def to_ts(cls, dt: date, tz: t_tzinfos = None) -> t_ts:
        return pd.Timestamp(dt, tz=tz)

    @classmethod
    @dispatch
    def to_ts(cls, dt: t_tsdt, tz: t_tzinfos = None) -> t_ts:
        if tz is None or not dt.tzinfo:
            return pd.Timestamp(dt, tz=tz)
        return pd.Timestamp(dt).tz_convert(tz)

    @classmethod
    @dispatch
    def to_ts(cls, dt: time, tz: t_tzinfos = None,
              default: date = None) -> t_ts:
        if default is None:
            default = date.today()
        dt = pd.Timestamp.combine(default, dt)
        if tz is None or not dt.tzinfo:
            return pd.Timestamp(dt, tz=tz)
        return pd.Timestamp(dt).tz_convert(tz)

    @classmethod
    @dispatch
    def to_ts(cls, ts: t_Arr, tz: t_tzinfos = None) -> t_ts:
        dt = ts.datetime
        if tz is None or not dt.tzinfo:
            return pd.Timestamp(dt, tz=tz)
        return pd.Timestamp(dt).tz_convert(tz)

    @classmethod
    @dispatch
    def to_ts(cls, ts: t_period, freq: str = None,
              how='S', tz: t_tzinfos = None) -> t_ts:
        ts = ts.to_timestamp(freq if freq else ts.freqstr, how)
        _tz = ZoneInfo(tz) if isinstance(tz, str) else tz
        return ts.replace(tzinfo=_tz)

    @classmethod
    @dispatch
    def to_ts(cls, dt64: t_dt64, tz: t_tzinfos = None) -> t_ts:
        """
        用途:np.datetime64转pd.Timestamp
        参数:
            ts:np.datetime64 无时区,从1970-01-01T00:00:00的偏移量
            tz:str,tzinfo or None时区
        实例:
        pd.Timestamp(dt64).tz_localize('UTC').tz_convert(tz)
        """
        if isinstance(tz, str):
            tz = ZoneInfo(tz)
        return pd.Timestamp(dt64).replace(tzinfo=tz)

    @classmethod
    @dispatch
    def to_dt(cls, dt: str, tz: t_tzinfos = None) -> t_dt:
        """
        用途:str转datetime
        说明:str无时区则数值相同;有时区若指定新时区则不同
        参数:
        ts:str时间
           格式1:
           2020-03-14 15:32:52,
           2020-03-14T15:32:52.192548651
           2022-08-21 17:29:25.549638+0800
           格式2:
           20200314153252192548651
           20200314153252
        tz:str,tzinfo or None时区
        说明:
           格式1月日时分秒可为1位,后面为微秒192548+651纳秒 T可为空格
           格式2月日时分秒必为2位,后面为微秒192548+651纳秒
           格式1,2年为4位;可输入年(月,日,时分秒,微秒均可省略)
        注意:忽略ns
        """
        return cls.to_ts(dt, tz=tz).to_pydatetime(False)

    @classmethod
    @dispatch
    def to_dt(cls, ts: int, unit='ns', tz: t_tzinfos = None) -> t_dt:
        """unit:str= 's','ns' """
        return cls.to_ts(ts, unit=unit, tz=tz).to_pydatetime(False)

    @classmethod
    @dispatch
    def to_dt(cls, ts: float, unit='s', tz: t_tzinfos = None) -> t_dt:
        return cls.to_ts(ts, unit=unit, tz=tz).to_pydatetime(False)

    @classmethod
    @dispatch
    def to_dt(cls, dt: t_sstarrtsdt_d_64, tz: t_tzinfos = None) -> t_dt:
        return cls.to_ts(dt, tz=tz)

    @classmethod
    @dispatch
    def to_dt(cls, dt: time, tz: t_tzinfos = None,
              default: date = None) -> t_dt:
        return cls.to_ts(dt, tz, default)

    @classmethod
    @dispatch
    def to_dt(cls, dt: t_period, freq: str = None, how='S',
              tz: t_tzinfos = None) -> t_dt:
        return cls.to_ts(dt, freq, how, tz).to_pydatetime(False)

    @classmethod
    @dispatch
    def gen_period(cls, value: int, freq: str = 'D') -> t_period:
        """
        pd.Period(ordinal=28800,freq='s')
        #Period('1970-01-01 08:00:00', 'S')
        """
        return pd.Period(freq=freq, ordinal=value)

    @classmethod
    @dispatch
    def gen_period(cls, value: t_stsdt_d_p_64, freq: str = None) -> t_period:
        """
        pd.Period(p,freq='D')#Period('2012-12-01', 'D')

        pd.Period(dt,freq='D')#Period('2023-04-26', 'D')
        pd.Period(dt.date(),freq='D')#Period('2023-04-26', 'D')

        #pd.Period(dt.time(),freq='D')err
        pd.Period(ts,freq='D')# Period('2023-04-26', 'D')
        pd.Period(dt64,freq='D')#Period('2022-12-01', 'D')
        """
        return pd.Period(value, freq)

    @classmethod
    @dispatch
    def gen_period(cls, value: time, freq: str = None,
                   default: date = None) -> t_period:
        if default is None:
            default = date.today()
        ts = pd.Timestamp.combine(default, value)
        return pd.Period(ts, freq=freq)

    @classmethod
    @dispatch
    def gen_period(cls, freq: str, year: int, mon: int = None,
                   q: int = None, day: int = None, hour: int = None,
                   _min: int = None, sec: int = None) -> t_period:
        """注意:必须提供year"""
        return pd.Period(
            freq=freq, year=year, month=mon,
            quarter=q, day=day, hour=hour,
            minute=_min, second=sec
        )

    @classmethod
    @dispatch
    def gen_period(cls, *args, **kws) -> t_period:
        """
        函数:
        Period(value=None, freq=None, ordinal=None, year=None, month=None,
            quarter=None, day=None, hour=None, minute=None, second=None)
        用途:代表一段时间, 表示的是时间区间如数日、数月、数季、数年等
        参数:
            value = None:Period或compat.string_types代表的时间段
                (e.g., '4Q2005')'2005年第四季'
            freq = None:str  period周期字符串或相应对象之一
            ordinal: int = None 周期offset相对于Gregorian epoch
            year/quarter = None: int
            month/day = 1: int
            hour/minute/second = 0: int
        """
        return pd.Period(*args, **kws)

    @classmethod
    @dispatch
    def gen_dt64(cls, dt: t_sidt_d, unit: str = None) -> t_dt64:
        """
        说明:
        1) 是带单位的日期时间类型[Y,M,W,D,h,m,s,ms,us,ns,ps,fs,as]
            日期单位:Y,M,W,D
            时间单位:h,m,s,ms,us,ns,ps,fs,as和一些额外的基于 SI 前缀秒的单位
        2) 创建日期用ISO8601日期或字符串(必须补0)接受NAT or nat
            内部存储单位自动从str中选择可是日期时间
        3) 从整数创建-相对于Unix纪元(1970-1-1 00:00:00 UTC)偏移量

        注意:
            '2022-3-8 20:00:05'错误str必须为2位
            从字符串创建数组如单位不统一则一律转化成其中最小单位
            不存储时区信息。目前仍然解析时区偏移,此行为已弃用将来引发错误

        实例:
        np.datetime64('2005-02-01T03:30','D')
        np.datetime64(2022-1970, 'Y')#自 UNIX 纪元以来的1年

        np.datetime64(1, 'D')#自 UNIX 纪元以来的1年
        #numpy.datetime64('1970-01-02')

        np.datetime64(0, 'D')#自 UNIX 纪元以来的1年
        #numpy.datetime64('1970-01-01')

        np.datetime64(0, 's')#自 UNIX 纪元以来的1年
        #numpy.datetime64('1970-01-01T00:00:00')

        np.datetime64(1682478982,'s')#时间戳
        #numpy.datetime64('2023-04-26T03:16:22')
        """
        if unit is None:
            return np.datetime64(dt)
        return np.datetime64(dt, unit)

    @classmethod
    @dispatch
    def gen_dt64(cls, dt: t_period, unit: str = None, freq: str = 'ns',
                 how: str = 'S', tz: t_tzinfos = None) -> t_dt64:
        ts = dt.to_timestamp(freq, how, tz)
        if unit is None:
            return np.datetime64(ts)
        return np.datetime64(ts, unit)

    @classmethod
    @dispatch
    def gen_dt64(cls, dt: t_ts, unit: str = None) -> t_dt64:
        if unit is None:
            return np.datetime64(dt.replace(tzinfo=None))
        return np.datetime64(dt.replace(tzinfo=None), unit)

    @classmethod
    @dispatch
    def gen_dt64(cls, dt: time, unit: str = None,
                 default: date = None) -> t_dt64:
        if default is None:
            default = date.today()
        ts = pd.Timestamp.combine(default, dt)
        if unit is None:
            return np.datetime64(ts.replace(tzinfo=None))
        return np.datetime64(ts.replace(tzinfo=None), unit)

    # **********************************************
    # other:
    # **********************************************

    @classmethod
    def cat(cls, vdate: t_sdate, vtime: t_stime, backdt=False) -> t_tsdt:
        """
        用途:合并日期和时间,返回datetime or pd.Timestamp
        参数:
            vdate:str or date
            vtime:str or dt.time
        """
        if isinstance(vdate, str):
            vdate = parse(vdate).date()
        if isinstance(vtime, str):
            vtime = parse(vtime).time()
        ts = pd.Timestamp.combine(vdate, vtime)
        return ts.to_pydatetime(False) if backdt else ts

    @classmethod
    @dispatch
    def replace(cls, dt: t_tsdt_d_t, *args, **kws) -> t_tsdt_d_t:
        """
        用途:修改日期时间;替换
        返回:新对象dt.datetime or dt.date or dt.time,pd.Timestamp
        参数:
            dt: datetime,date,time,pd.Timestamp 时区或无
            *args,**kws:
            *pd.Timestamp:包含全部参数-含nanosecond
            *datetime:不包含nanosecond
            *date:仅包含year,month,day
            *time:仅包含hour,minute,second,microsecond,tzinfo,fold

        说明:
        tzinfo str,tzinfo,None;其他参数int
        pd.Timestamp int参数可为None,其他函数为None报错
        tzinfo可为None,pd.Timestamp可为str,其他报错,在此做了处理
        fold:int=None int 0 or 1关键字参数

        原函数:
        def replace(self, year=None, month=None, day=None)
        def replace(self, hour=None, minute=None, second=None,
                microsecond=None,tzinfo=None, *, fold=None):
        def replace(self, year=None, month=None, day=None,
                hour=None, minute=None, second=None, microsecond=None,
                tzinfo=None,*, fold=None):
        """
        try:
            return dt.replace(*args, **kws)
        except Exception:
            if not isinstance(dt, pd.Timestamp):
                if 'tzinfo' in kws:
                    kws['tzinfo'] = ZoneInfo(kws['tzinfo'])
                else:
                    n = len(args)
                    args = list(args)
                    if (5 <= n <= 6) and isinstance(args[4], str):
                        args[4] = ZoneInfo(args[4])
                    elif (8 <= n) and isinstance(args[7], str):
                        args[7] = ZoneInfo(args[7])
                return dt.replace(*args, **kws)

    @classmethod
    def _get_delta(cls, **kwargs) -> t_relativedelta:
        """
        用途:用于获取日期差类似datetime.timedelta
        返回:relativedelta 可直接用于datetime的加减
        参数:
        kwargs:
            dt1=None, dt2=None,
            years=0, months=0, days=0, leapdays=0, weeks=0,
            hours=0, minutes=0, seconds=0, microseconds=0,
            year=None, month=None, day=None, weekday=None,
            yearday=None, nlyearday=None, hour=None,
            minute=None, second=None, microsecond=None

        实例:
        dt=datetime.now()          #datetime(2022, 8, 20, 9, 53, 25, 50984)
        dt - relativedelta(months=3)#datetime(2022, 5, 20, 9, 53, 1, 897984)
        dt - relativedelta(month=3) #datetime(2022, 3, 20, 9, 53, 1, 897984)
        """
        return relativedelta.relativedelta(**kwargs)

    # **********************************************
    # 字符串:
    # **********************************************
    @classmethod
    def _str(cls, ts: t_tsdt_d_t, fmt: str) -> str:
        return ts.strftime(fmt)

    @classmethod
    def _isostr(cls, ts: t_tsdt_d_t, sep='T', timespec='auto') -> str:
        if type(ts) == date:
            return ts.isoformat()
        elif type(ts) == time:
            return ts.isoformat(timespec)
        return ts.isoformat(sep, timespec)

    @classmethod
    def _timestamp(cls, ts: t_tsdt) -> float:
        """
        注意:Timestamp,datetime的时间戳不同(仅仅当时区相同时才相同)
        utc='UTC'
        sh='Asia/Shanghai'
        us='US/Alaska'
        tzutc=ZoneInfo(utc)
        tzsh = ZoneInfo(sh)
        tzus = ZoneInfo(us)

        dt0=datetime(1970, 1, 2, 8, 1, 2, 300000)
        dt1=dt0.replace(tzinfo=tzutc)
        dt2 = dt0.replace(tzinfo=tzsh)
        dt3 = dt0.replace(tzinfo=tzus)

        ts0=pd.Timestamp(dt0)
        ts1 = pd.Timestamp(dt1)
        ts2 = pd.Timestamp(dt2)
        ts3 = pd.Timestamp(dt3)

        dt0.timestamp(),dt1.timestamp(),dt2.timestamp(),dt3.timestamp(),
        # (86462.3, 115262.3, 86462.3, 151262.3)

        ts0.timestamp(),ts1.timestamp(),ts2.timestamp(),ts3.timestamp(),
        # (115262.3, 115262.3, 86462.3, 151262.3)
        """
        return ts.timestamp()

    @classmethod
    def _timestampnow(cls, isint=False) -> t_if:
        return time_s() if isint else time_ns()

    # **********************************************
    # 日期相互转换:
    # **********************************************

    @classmethod
    def _sttime_to_f(cls, ts: t_st) -> float:
        """用途:将时间元组转为时间戳float秒数time.mktime(ts)"""
        return mktime(ts)

    @classmethod
    def _f_to_sttime(cls, sec: t_if = None, is_utc=False) -> t_st:
        """
        用途:将时间秒毫秒转换为时间元组 产生秒函数:
            (time.time(),dt.datetime.timpstamp(),pd.Timestamp.timestamp())
            1661172299.9821737 s
            time.time_ns()直接输入错误需将该int  1661172299,982,173,700 ns处理上述类型float
        参数:
            sec:float =None秒,int 纳秒
            utc=True 将自Epoch以来的秒转换为UTC格林尼治标准时间
            utc=False 将自这个时代以来的秒转为本地时间的时间元组
        说明:当“秒”没有传入时转换当前时间
        返回:(tm_year,tm_mon,tm_mday,tm_hour,tm_min,
              tm_sec,tm_wday,tm_yday,tm_isdst)
        """
        if isinstance(sec, int):
            sec = round(sec / 1000000000, 7)
        return gmtime(sec) if is_utc else localtime(sec)

    @classmethod
    @dispatch
    def to_sttime(cls, sec: float = None, islocal=True) -> t_st:
        """
        用途:生成struct_time
        参数:
        secs:float 用time.time()生成的秒,time_ns()错误
            无指定用当前时间
        islocal=True 生成本地时间或UTC时间
        """
        return localtime(sec) if islocal else gmtime(sec)

    @classmethod
    @dispatch
    def to_sttime(cls, st: str, fmt: str) -> t_st:
        """
        实例:
        st = time.strptime(strtime, fmt)
        2020-03-14 15:32:52,2020-03-14T15:32:52.192548651
        20200314153252192548651,20200314153252
        """
        return strptime(st, fmt)

    @classmethod
    def f_to_dt(cls, ts: t_if, tz: tzinfo = None, isns=False) -> t_dt:
        """
        用途:将整数ns或float秒转日期
        参数:
        f_ts:int or float
        tz:tzinfo对象
        **kwargs:当tz=None时提供struct_time的默认时区
            无提供默认tzwinlocal(),否则按kwargs['tz_struct_time']
        """
        return datetime.fromtimestamp(ts / 10**9 if isns else ts, tz)

    @classmethod
    def ts_to_dt64(cls, ts: t_ts) -> t_dt64:
        """
        用途:Timestamp 转np.datetime64
        注意:和本地时区中国差8h
        实例:
        ts=Timestamp('2022-08-22 10:51:51.617978+0800', tz='Asia/Shanghai')
        ts.to_datetime64()#numpy.datetime64('2022-08-22T02:51:51.617978000')

        2020-03-14 15:32:52,2020-03-14T15:32:52.192548651
        20200314153252192548651,20200314153252
        """
        return ts.to_datetime64()

    @classmethod
    def ts_to_period(cls, ts: t_ts, freq: str = 'D') -> t_period:
        """
        参数:
            ts:pd.Timestamp
            freq: 周期字符串,np.datetime64格式字符串 [
                'A-DEC','A-JAN','A-FEB','A-MAR','A-APR','A-MAY','A-JUN','A-JUL',
                'A-AUG','A-SEP','A-OCT','A-NOV','Q-DEC','Q-JAN','Q-FEB','Q-MAR',
                'Q-APR','Q-MAY','Q-JUN','Q-JUL','Q-AUG','Q-SEP','Q-OCT','Q-NOV',
                'M','W-SUN','W-MON','W-TUE','W-WED','W-THU','W-FRI','W-SAT',
                'B','D','H','T','S','L','U','N','Q','A','W','C']
        构造:
            pd.Period(ts, freq=freq)
            ts.tz_convert(None).to_period(freq)#ts有时区
            ts.to_period(freq)#ts无时区

            2020-03-14 15:32:52,2020-03-14T15:32:52.192548651
            20200314153252192548651,20200314153252
        """
        return ts.replace(tzinfo=None).to_period(freq=freq)

    @classmethod
    def dt64_astype(cls, dt64: t_dt64, _type: t_tsdt_d | t_period) -> int:
        """
        dt64.astype(datetime)
        #datetime.date(2022, 12, 1) 682518812197596000 1682518812.197596

        dt64.astype(pd.Timestamp)
        #datetime.date(2022, 12, 1)

        dt64.astype(date)
        #datetime.date(2022, 12, 1)

        #dt64.astype(time) err
        dt64.astype(pd.Period)
        #datetime.date(2022, 12, 1)
        """
        return dt64.astype(_type)

    @classmethod
    def dt64_to_ts(cls, dt64: t_dt64, tz: t_tzinfos = None) -> t_ts:
        """
        dt64 = np.datetime64(dt, 's')
        dt64.astype(datetime.datetime)
        # datetime.datetime(2022, 3, 8, 20, 0, 5)
        dt64.astype(datetime.date)  # 同上
        """
        return pd.Timestamp(dt64).tz_localize('UTC').tz_convert(tz)
        # pd.Timestamp(dt64, tz=tz) 警告

    @classmethod
    def dt64_to_dt(cls, dt64: t_dt64, tz: t_tzinfos = None) -> t_dt:
        """
        dt64 = np.datetime64(dt, 's')
        dt64.astype(datetime.datetime)
        # datetime.datetime(2022, 3, 8, 20, 0, 5)
        dt64.astype(datetime.date)  # 同上
        """
        if isinstance(tz, str):
            tz = ZoneInfo(tz)
        dt = dt64.astype(datetime)
        if isinstance(dt, int):  # 控制台输出类似datetime,pycharm输出int
            dt = pd.Timestamp(dt, unit='ns')
        return dt.replace(tzinfo=tz)

    @classmethod
    def dt64_to_f(cls, dt64: t_dt64) -> float:
        """
        dt64.tolist()#1663499215522302000
        dt64.item()#1663499215522302000
        dt64.astype(pd.Timestamp)# 1663499215522302000
        pd.Timestamp(1663499215522302000,unit='ns')
        #Timestamp('2022-09-18 11:06:55.522302')
        """
        return dt64.item()

    @classmethod
    def dt64_str(cls, dt64: t_dt64) -> str:
        """
        dt = np.datetime64('2022-03-08 20:00:05')
        # numpy.datetime64('2022-03-08T20:00:05')
        dt64 = np.datetime_as_string(dt)  # '2022-03-08T20:00:05'
        """
        return np.datetime_as_string(dt64)

    @classmethod
    def period_to_ts(
            cls,
            p: t_period,
            freq='D',
            how='S',
            tz: t_tzinfos = None) -> t_ts:
        """
        freq='D'
        how='S' start,finish,begin,end
        """
        return p.to_timestamp(freq=freq, how=how, tz=tz)

    @classmethod
    def period_str(cls, p: t_period, fmt: str) -> str:
        """
        参数:fmt:
        '%a':区域设置的缩写的工作日名称.
        '%A':区域设置的完整工作日名称.
        '%b':地区设置的缩写月份名称.
        '%B':区域设置的全月名.
        '%c':区域设置的适当日期和时间表示.
        '%d':当月的日期,作为十进制数字[01,31].
        '%f':财政年度,没有世纪,作为十进制数字[00,99] (1)
        '%F':财政年度,以世纪为小数(2)

        '%H':24小时十进制数[00,23]
        '%I':12小时十进制数字[01,12]
        '%j':作为十进制数字[001,366]
        '%m':月份作为十进制数[01,12]
        '%M':分钟,作为十进制数[00,59]
        '%p':区域设置对应的AM或PM.(3)

        '%q':季度[01,04]
        '%S':第二个,表示为十进制数[00,61].(4)
        '%U':一年周数(星期日为一周的第一天)[00,53]
        新年第一个星期天之前的所有日子都被认为是在第0周.(5)
        '%w':工作日作为十进制数字[0(星期日),6].
        '%W':一年中的周数(星期一为一周的第一天),作为十进制数[00,53]
        在新年的第一个星期一之前的所有日子都被认为是在第0周.(5)
        '%x':区域设置的适当日期表示.
        '%X':区域设置的适当时间表示.
        '%y':没有世纪的年份作为小数[00,99].
        '%Y':以世纪为小数的年份.
        '%Z':时区名称(如果没有时区存在,则没有字符).
        '%%':文字'%'字符.

        说明:
        (1)如频率不是按季显示一次,则'%f'指令与'%y'指令相同
        否则,它对应于'财政'年度,由:attr:`qyear`属性定义.
        (2)如频率不是每季度显示一次,则'%F'指令与'%Y'指令相同.
        否则,它对应于'财政'年度,由:attr:`qyear`属性定义.
        (3)如果使用'%I'指令来解析小时,则'%p'指令只影响输出小时字段.
        (4)这个范围实际上是'0'到'61';这就解释了闰秒和(非常罕见的)双闰秒.
        (5)'%U'和'%W'指令仅在指定了一周和年度的日期时用于计算.

        实例:
        a = Period(freq='Q-JUL', year=2006, quarter=1)
        a.strftime('%F-Q%q')#'2006-Q1'
        a.strftime('%b-%Y')# 'Oct-2005'本季度最后一个月date

        a = Period(freq='D', year=2001, month=1, day=1)
        a.strftime('%d-%b-%Y')#'01-Jan-2001'
        a.strftime('%b. %d, %Y was a %A')#'Jan. 01, 2001 was a Monday'
        """
        return p.strftime(fmt)

    @classmethod
    def period_asfreq(cls, dt: t_period, freq: str, how='S') -> t_period:
        """
        参数:
        freq : str, BaseOffset所需的频率。
        how : {'E', 'S', 'end', 'start'}该时间跨度的开始或结束。

        """
        return dt.asfreq(freq, how)


def test_data():
    utc = 'UTC'
    sh = 'Asia/Shanghai'
    us = 'US/Alaska'
    tzutc = ZoneInfo(utc)
    tzsh = ZoneInfo(sh)
    tzus = ZoneInfo(us)

    dt0 = datetime(1970, 1, 2, 8, 1, 2, 300000)
    dt1 = dt0.replace(tzinfo=tzutc)
    dt2 = dt0.replace(tzinfo=tzsh)
    dt3 = dt0.replace(tzinfo=tzus)
    dttup = dt0.replace(microsecond=0)

    ts0 = pd.Timestamp(dt0)
    ts1 = pd.Timestamp(dt1)
    ts2 = pd.Timestamp(dt2)
    ts3 = pd.Timestamp(dt3)

    s = '1970-1-2 8:1:2.3'
    f = dt0.timestamp()
    tup = dt0.timetuple()
    period = pd.Period(ts0, 'ns')
    dt64 = np.datetime64(ts0)

    return (utc, sh, us, tzutc, tzsh, tzus, dt0, dt1,
            dt2, dt3, dttup, ts0, ts1, ts2, ts3, s, f,
            tup, period, dt64,)


def test_to_dt(a=Date):

    def dt_cleartime(dt, tz=None):
        if isinstance(tz, str):
            tz = ZoneInfo(tz)
        dt = datetime(dt.year, dt.month, dt.day)
        return dt.replace(tzinfo=tz)

    utc, sh, us, tzutc, tzsh, tzus, dt0, dt1,\
        dt2, dt3, dttup, ts0, ts1, ts2, ts3, s, f, \
        tup, period, dt64 = test_data()

    assert(a.to_dt(s) == dt0)
    assert (a.to_dt(s, utc) == dt1)
    assert (a.to_dt(s, tzsh) == dt2)
    assert (a.to_dt(s, tzus) == dt3)
    assert (a.to_dt(f) == dt0)
    assert (a.to_dt(tup) == dttup)

    assert (a.to_dt(dt0) == dt0)
    assert (a.to_dt(dt0, utc) == dt1)
    assert (a.to_dt(dt0, tzsh) == dt2)
    assert (a.to_dt(dt0, tzus) == dt3)

    assert (a.to_dt(dt0) == dt0)
    assert (a.to_dt(dt1) == dt1)
    assert (a.to_dt(dt2) == dt2)
    assert (a.to_dt(dt3) == dt3)

    assert (a.to_dt(dt0.date()) == dt_cleartime(dt0))
    assert (a.to_dt(dt0.date(), utc) == dt_cleartime(dt0, utc))
    assert (a.to_dt(dt0.date(), tzsh) == dt_cleartime(dt0, tzsh))
    assert (a.to_dt(dt0.date(), tzus) == dt_cleartime(dt0, tzus))

    assert (a.to_dt(dt0.date()) == dt_cleartime(dt0))
    assert (a.to_dt(dt1.date()) == dt_cleartime(dt1))
    assert (a.to_dt(dt2.date()) == dt_cleartime(dt2))
    assert (a.to_dt(dt3.date()) == dt_cleartime(dt3))

    assert (a.to_dt(dt0.time(), default=dt0.date()) == dt0)
    assert (a.to_dt(dt1.time(), tz=utc, default=dt0.date()) == dt1)
    assert (a.to_dt(dt2.time(), tz=sh, default=dt0.date()) == dt2)
    assert (a.to_dt(dt3.time(), tz=us, default=dt0.date()) == dt3)

    assert (a.to_dt(ts0) == dt0)
    assert (a.to_dt(ts1, tz=utc) == dt1)
    assert (a.to_dt(ts2, tz=sh) == dt2)
    assert (a.to_dt(ts3, tz=us) == dt3)

    assert (a.to_dt(dt64) == dt0)
    assert (a.to_dt(dt64, tz=utc) == dt1)
    assert (a.to_dt(dt64, tz=sh) == dt2)
    assert (a.to_dt(dt64, tz=us) == dt3)

    assert (a.to_dt(period) == dt0)
    assert (a.to_dt(period, tz=utc) == dt1)
    assert (a.to_dt(period, tz=sh) == dt2)
    assert (a.to_dt(period, tz=us) == dt3)


def test_to_ts(a=Date):

    def ts_cleartime(dt, tz=None):
        if isinstance(tz, str):
            tz = ZoneInfo(tz)
        dt = datetime(dt.year, dt.month, dt.day)
        return pd.Timestamp(dt.replace(tzinfo=tz))

    utc, sh, us, tzutc, tzsh, tzus, dt0, dt1, \
        dt2, dt3, dttup, ts0, ts1, ts2, ts3, s, f, \
        tup, period, dt64 = test_data()

    assert (a.to_ts(s) == ts0)
    assert (a.to_ts(s, utc) == ts1)
    assert (a.to_ts(s, tzsh) == ts2)
    assert (a.to_ts(s, tzus) == ts3)
    assert (a.to_ts(f) == ts0)
    assert (a.to_ts(tup) == pd.Timestamp(dttup))

    assert (a.to_ts(dt0) == ts0)
    assert (a.to_ts(dt0, utc) == ts1)
    assert (a.to_ts(dt0, tzsh) == ts2)
    assert (a.to_ts(dt0, tzus) == ts3)

    assert (a.to_ts(dt0) == ts0)
    assert (a.to_ts(dt1) == ts1)
    assert (a.to_ts(dt2) == ts2)
    assert (a.to_ts(dt3) == ts3)

    assert (a.to_ts(dt0.date()) == ts_cleartime(dt0))
    assert (a.to_ts(dt0.date(), utc) == ts_cleartime(dt0, utc))
    assert (a.to_ts(dt0.date(), tzsh) == ts_cleartime(dt0, tzsh))
    assert (a.to_ts(dt0.date(), tzus) == ts_cleartime(dt0, tzus))

    assert (a.to_ts(dt0.date()) == ts_cleartime(dt0))
    assert (a.to_ts(dt1.date()) == ts_cleartime(dt1))
    assert (a.to_ts(dt2.date()) == ts_cleartime(dt2))
    assert (a.to_ts(dt3.date()) == ts_cleartime(dt3))

    assert (a.to_ts(dt0.time(), default=dt0.date()) == ts0)
    assert (a.to_ts(dt1.time(), tz=utc, default=dt0.date()) == ts1)
    assert (a.to_ts(dt2.time(), tz=sh, default=dt0.date()) == ts2)
    assert (a.to_ts(dt3.time(), tz=us, default=dt0.date()) == ts3)

    assert (a.to_ts(ts0) == ts0)
    assert (a.to_ts(ts0, tz=utc) == ts1)
    assert (a.to_ts(ts0, tz=sh) == ts2)
    assert (a.to_ts(ts3, tz=us) == ts3)

    assert (a.to_ts(dt64) == ts0)
    assert (a.to_ts(dt64, tz=utc) == ts1)
    assert (a.to_ts(dt64, tz=sh) == ts2)
    assert (a.to_ts(dt64, tz=us) == ts3)

    assert (a.to_ts(period) == ts0)
    assert (a.to_ts(period, tz=utc) == ts1)
    assert (a.to_ts(period, tz=sh) == ts2)
    assert (a.to_ts(period, tz=us) == ts3)


def test_gen_period(cls=Date):

    s = '2023-12-01 15:35:59.300000'
    ts = pd.Timestamp(s)
    dt = ts.to_pydatetime(False)
    p = pd.Period(s, freq='us')
    p2 = pd.Period(s, freq='D')
    dt64 = np.datetime64(s)

    assert (cls.gen_period(s, freq='us') == p)
    assert (cls.gen_period(p, freq='us') == p)
    assert (cls.gen_period(dt, freq='us') == p)
    assert (cls.gen_period(ts, freq='us') == p)
    assert (cls.gen_period(dt64, freq='us') == p)
    assert (cls.gen_period(dt.date(), freq='D') == p2)
    assert (cls.gen_period(dt.time(), 'us', dt.date()) == p)

    p = pd.Period(ordinal=28800, freq='s')
    assert (cls.gen_period(28800, freq='s') == p)
    assert (cls.gen_period('D', 2023, 12, day=1) == p2)
    assert (cls.gen_period(year=2023, month=12, day=1, freq='D') == p2)


def test_gen_dt64(cls=Date):
    s = '2023-04-27T00:44:49'
    dt64 = np.datetime64(s)
    # t_sidt_d
    assert(cls.gen_dt64(s) == dt64)
    assert (cls.gen_dt64(s, 's') == dt64)

    v = np.datetime64('2023')
    assert (cls.gen_dt64(2023 - 1970, 'Y') == v)
    v = np.datetime64('1970-01-01')
    assert (cls.gen_dt64(0, 'D') == v)  # 自 UNIX 纪元以来的1年
    v = np.datetime64('1970-01-01T00:00:00')
    assert (cls.gen_dt64(0, 's') == v)

    s = '2023-04-26T03:16:22'
    v = np.datetime64(s)
    assert (cls.gen_dt64(1682478982, 's') == v)  # 时间戳

    ts = pd.Timestamp(s)
    dt = ts.to_pydatetime(False)
    v = np.datetime64(s)
    vd = np.datetime64(s, 'D')
    assert (cls.gen_dt64(ts, 's') == v)
    assert (cls.gen_dt64(dt, 's') == v)
    assert (cls.gen_dt64(dt.date(), 'D') == vd)
    assert (cls.gen_dt64(dt.time(), 's', dt.date()) == v)

    p = pd.Period(s)
    assert (cls.gen_dt64(p.to_timestamp(), 's') == v)


def test_str(cls=Date):
    dt = datetime.now()
    ts = pd.Timestamp(dt)
    s1 = cls._str(dt, fmt='%Y-%m-%d %H:%M:%S.%f')
    s2 = cls._str(ts, fmt='%Y-%m-%d %H:%M:%S.%f')
    assert(s1 == s2)

    s11 = cls._isostr(dt)
    s12 = cls._isostr(dt.date())
    s13 = cls._isostr(dt.time())
    s21 = cls._isostr(ts)
    s22 = cls._isostr(ts.date())
    s23 = cls._isostr(ts.time())
    assert(s11 == s21 and s12 == s22 and s13 == s23)

    f1 = cls._timestamp(dt)
    f2 = cls._timestamp(ts)
    assert(f1 != f2)
    f1 = cls._timestampnow()
    f2 = cls._timestampnow()
    assert (f1 == f2)


def test_convert(cls=Date):
    print('*** test convert*** \n')
    dt = datetime.now(cls.tzlocal)
    tup = dt.timetuple()
    f1 = dt.timestamp()
    f2 = cls._sttime_to_f(tup)
    tup2 = cls._f_to_sttime(f2)
    tup3 = cls.to_sttime(f2)
    assert (tup2 == tup3 == tup)
    assert (dt == cls.f_to_dt(f1, tz=cls.tzlocal))
    assert (dt != cls.f_to_dt(f2))  # 缺失微秒

    ts = pd.Timestamp(dt)
    dt64 = cls.ts_to_dt64(ts)
    print('1.1.to_dt64=', dt64)
    # 2023-04-26T12:51:35.167074000
    print('1.2.to_period=', cls.ts_to_period(ts, freq='ns'))
    # 2023-04-26 20:51:35.167074000

    i = cls.dt64_astype(dt64, datetime)
    i2 = cls.dt64_astype(dt64, date)
    i3 = cls.dt64_astype(dt64, date)
    i4 = cls.dt64_astype(dt64, pd.Period)
    i5 = cls.dt64_astype(dt64, pd.Timestamp)
    assert(i == i2 == i3 == i4 == i5)

    f1 = pd.Timestamp(dt64).timestamp()
    assert(i / 10**9 == f1)
    ts1 = pd.Timestamp(f1, unit='s')
    ts2 = pd.Timestamp(i, unit='ns')
    print('1.3.astype:ts1-ts2=', ts1 - ts2)
    # 0 days 00:00:00.000000067

    ts1 = cls.dt64_to_ts(dt64)
    ts2 = cls.dt64_to_ts(dt64, cls.tzlocal).replace(tzinfo=None)
    assert(ts2 - ts1 == pd.Timedelta(hours=8))
    print('1.4.dt64_to_dt=', cls.dt64_to_dt(dt64), dt)
    # 2023-04-26 14:48:28.356253 2023-04-26 22:48:28.356253+08:00
    print('1.5.dt64_to_f=', cls.dt64_to_f(dt64))
    # 1682520734957452000
    print('1.6.dt64_str=', cls.dt64_str(dt64))
    # 2023-04-26T14:52:14.957452000
    p = pd.Period('2023-04-26 14:48:28.356253')
    d = cls.period_to_ts(p, freq='s', how='S')
    print('1.7.period_to_ts=', d)  # 2023-04-26 14:48:28
    d = cls.period_str(p, fmt='%Y-%m-%d %H:%M:%S')
    print('1.8.period_str=', d)  # 2023-04-26 14:48:28
    d = cls.period_asfreq(p, 'D', how='S')
    print('1.9.period_asfreq=', d)  # 2023-04-26


def test_parse():
    sh = 'Asia/Shanghai'
    tzus = ZoneInfo('US/Central')
    tzsh = ZoneInfo('Asia/Shanghai')
    print('\n*** test parse start ***')

    s1 = ('2017-08-28 06:08:20-06:00|2022-10-21|20221021|'
          '2022/10/21|10-21|10/21|3/8|3-8|3/8/2022|2022/3/8|'
          '8/3|8-3|8/3/2022|3/15/2022|15/3/2022|Mar 15|'
          'Mar15|15 Mar|Mar 15 2022|2022 Mar15|15,3|3,15,2022|'
          '2003 10:20:30 Thu Sep 25|2020-09-09T10:20:20.5-8:00|'
          '2020-09-09T10:20|2020-09-09T10|2020-09-09|'
          '20200909T102020-8:00|20200909T102020|20200909T1020|'
          '20200909T1020|20200909|20200909102020|10-09-2020|'
          '10-09-2020|10-09-20|10-09-20')

    s2 = (
        '2017-08-28 06:08:20 CDT|Thu Sep 25 10:20:30 BRST 2003|'
        '2003 Thu Sep 25 10:20:30 BRST|2003 10:20:30 Thu Sep 25 BRST|'
        'Thu Sep 25 10:20:30 BRST 2003|'
        'today is 25 of Oct 2020 exactly at 10:20:20 with timezone +08:00|'
        '今天是2020年10月20日 10点23分49秒|今天是2020年10月20日,明天有个预约'
    )

    def op(data, no, disp):
        lst = [None, sh, tzus]
        for i, s in enumerate(data):
            if i in disp:
                print('%s.%s s  = %s' % (no, i, s))
            for j, tz in enumerate(lst):
                try:
                    dt = parse(s, tzinfos=tz)
                    if i in disp:
                        print('dt%s=%s, tz=%s' % (j, dt, dt.tzinfo))
                except Exception as e:
                    if i in disp:
                        print('err:%s, s=%s' % (e, s))
                    dt = parse(s, tzinfos=tz, fuzzy=True)
                    if i in disp:
                        print('dt%s=%s, tz=%s' % (j, dt, dt.tzinfo))
            if i in disp:
                print()

    disp1 = [4, 7, 20, 22, 23]
    disp2 = [0, 5]

    op(s1.split('|'), 1, disp1)
    op(s2.split('|'), 2, disp2)


def test_date():
    test_to_dt()
    test_to_ts()
    test_str()
    test_convert()
    test_gen_period()
    test_gen_dt64()


if __name__ == '__main__':
    _Arrow.test()
    test_parse()
    test_date()

以下为版本1:老版本

2.1.类方法汇总

class Date(object):
      
    def arrow_get(cls, ts=None, tz=None, **kwargs):
    def dt_to_ts(cls, dt):
    def dt64_str(cls, dt64):
    def dt64_to_dt(cls, dt64, tz=None):
    def dt64_to_float(cls, dt64):
    def dt64_to_timestamp(cls, dt64):
    def f_timestamp(cls, ts=None, **kwargs):
    def f_timestamp_to_dt(cls, f_ts, tz=None, **kwargs):
    def f_to_timetuple(cls, second=None, is_utc=False):
    def gen_dt(cls, dt, tz=None, **kwargs):
    def gen_dt64(cls, dt, f=None, **kwargs):
    def gen_period(cls, dt=None, freq=None, **kwargs):
    def gen_structtime(cls, secs=None, islocal=True):
    def gen_ts(cls, ts=None, tz=None, **kwargs):
    def get_delta(cls, **kwargs) -> relativedelta:
    def get_df_tail_next_bustime(cls, df, datecol, outfmt='', **kwargs):
    def get_df_tail_time(cls, df, datecol, outfmt=None, **kwargs):
    def is_update_file_time(cls, file, close_market_time='15:30:00'):
    def isostr(cls, dt, sep='T', timespec='auto', **kwargs):
    def join(cls, vdate, vtime, back_timestamp=False):
    def now(cls, tz=None, fmt=None):
    def parse_dt(cls, st, tz=None, **kwargs):
    def period_str(cls, period, fmt):
    def period_to_ts(cls, period, freq='D', how='S'):
    def replace()
    def str_to_timetuple(cls, st, fmt=''):
    def sub(cls, dt, delta=None, **kwargs):
    def sub_month(cls, dt, delta=None, monthend=None, **kwargs):
    def timetuple_to_f(cls, ts: t_sttime) -> float:
    def to_str(cls, dt, fmt=None, **kwargs):
    def today(cls, fmt=None):
    def ts_to_dt(cls, ts):
    def ts_to_dt64(cls, ts):
    def ts_to_period(cls, ts, freq):

2.2.时区函数

# 时区函数
Date.exists_datetime = exists_datetime  # 检查日期时间是否存在
Date.get_naive = get_naive  # 返回无时区且原值保持不变日期(或有时区)
Date.get_tz = get_tz  # 获取时区对象
Date.get_tz_offset = get_tz_offset  # 获取时区偏移量
Date.get_win_localzone = get_win_localzone  # 获取win本地时区
Date.get_win_timeones = get_win_timeones  # 获取窗口系统全部时区名称
Date.is_ambiguous = is_ambiguous  # 检查日期时间是否不明确
Date.is_aware = is_aware
Date.is_dst = is_dst  # 是否是夏令时
Date.is_naive = is_naive
Date.is_tz = is_tz  # 是否是时区对象
Date.is_tz_dateutil = is_tz_dateutil  # 是否是dateutil时区
Date.is_tz_pytz = is_tz_pytz
Date.is_tz_tzlocal = is_tz_tzlocal  # 是否是tzlocal.PytzShimTimezone时区
Date.is_tz_zoneinfo = is_tz_zoneinfo  # 是否是zoneinfo.ZoneInfo时区
Date.m_win_tz = m_win_tz
Date.m_win_tzname = m_win_tzname
Date.M_WIN_TZNAMES = M_WIN_TZNAMES
Date.tz_as = tz_as  # 转换为新时区的本地时间
Date.tz_del = tz_del  # 删除时区
Date.tz_dst = tz_dst  # 获取夏令时偏移
Date.tz_fromutc = tz_fromutc  # 将dt的时间作为UTC时间转为本地时间
Date.tz_name = tz_name  # 获取时区名称
Date.tz_str = tz_str
Date.tz_toutc = tz_toutc  # 将dt,ts转为utc格式日期
Date.tz_transitions = tz_transitions  # 获取夏令时的开始结束时间或None
Date.tz_upgrade_tzinfo = tz_upgrade_tzinfo  # 将pytz时区提升为zoneinfo时区
Date.tz_utcoffset = tz_utcoffset  # 显示与UTC的偏移量
Date.tzwin_normalize = tzwin_normalize

2.3.:节假日函数

Date.busday_offset = busday_offset  # 查找距离{delta_days} 天后的日期
Date.ge_busday = ge_busday  # 取当前或后一个工作日(> =dt)
Date.ge_busday_today = ge_busday_today  # 取当前或后一工作日(>=dt)
Date.get_workday_range = get_workday_range  # 取开始结束日期间工作日
Date.gt_busday = gt_busday  # 取后一工作日(> dt)
Date.gt_busday_today = gt_busday_today  # 取后一工作日
Date.is_holiday = is_holiday  # 是否是节假日
Date.is_workday = is_workday  # 是否是工作日
Date.isleap = isleap  # 是闰年
Date.le_busday = le_busday  # 取当前或前一工作日(<= dt)
Date.le_busday_today = le_busday_today  # 取当前或前一工作日(<=dt)
Date.leap_n = leap_n  # 闰年总数
Date.lt_busday = lt_busday  # 取前一工作日(< dt)
Date.lt_busday_today = lt_busday_today  # 取前一工作日
Date.month_days = month_days  # 一个月有多少天
Date.nearest_busday = nearest_busday  # 取当前工作日-距离dt日期最近工作日
Date.nearest_busday_today = nearest_busday_today  # 取距离今天最近工作日
Date.next_busday = next_busday  # 取后一个工作日(> dt)
Date.pre_busday = pre_busday  # 取前一工作日(< dt)
Date.week_day = week_day  # 返回给定日期的周一到日 0-6

说明:代码比较多,全部大概有10000行,在此仅列出核心文件date.py;cal.py(日期)date_tz.py(时区)未列出, 


3.date.py 日期基本操作及测试代码及测试结果

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# @Project:TcyQuantTrade
# @Module:date

# @Author:       tcy
# @Date:         2022/9/21  4:34
# @Emial:        3615693665@qq.com
# @Version:     1.18
# @Last Modified time:
# @City:           China Shanghai Songjiang Xiaokunshan
# @Company:  Weishi Machinery Manufacturing Priority
import time

import pandas as pd
import pytz_deprecation_shim as pds
from date_base import *
from cal import *
from date_tz import *

import wrapt
# from decorator import decorate, decorator
from plum import Dispatcher

dispatch = Dispatcher()

t_intfloat = Union[int, float]
t_strnone = Union[str, NoneType]
t_dictnone = Union[dict, NoneType]
t_arrow_get = Union[str, t_intfloat, t_sttime, t_datetime, t_Timestamp, tuple]
DT_MIN = -9223372036.854776  # pd.Timestamp.min.timestamp()
DT_MAX = 9223372036.854776  # pd.Timestamp.max.timestamp()
TZ_NAMES = get_tznames(have_num=True)  # {'ACST': ['Australia/Darwin',...],

_m_timenames = ['year', 'month', 'day', 'hour', 'minute', 'second']
_m_period_kws_keys1 = _m_timenames + ['freq', 'ordinal', 'quarter']
_m_period_kws_keys = _m_period_kws_keys1 + ['value']
_m_dt_kws_keys = _m_timenames + ['microsecond', 'tzinfo', 'fold']
_m_ts_kws_keys1 = _m_dt_kws_keys + ['freq', 'tz', 'unit', 'nanosecond']
_m_ts_kws_keys = _m_ts_kws_keys1 + ['ts_input']


def __enabled():
    return True


@wrapt.decorator(enabled=__enabled)
def __pass_through(wrapped, instance, args, kwargs):
    return wrapped(*args, **kwargs)


# 获取时区对象
def __create_dt(dt, tz):
    # type:(datetime|t_Timestamp,tzinfo_t)->datetime
    return datetime(year=dt.year,
                    month=dt.month,
                    day=dt.day,
                    hour=dt.hour,
                    minute=dt.minute,
                    second=dt.second,
                    microsecond=dt.microsecond,
                    tzinfo=tz,
                    fold=dt.fold,
                    )


def __create_ts_from_dt(dt, tz):
    # type:(datetime,t_tzinfos)->t_Timestamp
    return pd.Timestamp(year=dt.year,
                        month=dt.month,
                        day=dt.day,
                        hour=dt.hour,
                        minute=dt.minute,
                        second=dt.second,
                        microsecond=dt.microsecond,
                        tz=tz,
                        fold=dt.fold,
                        )


def __create_ts_from_ts(ts, tz):
    # type:(t_Timestamp,t_tzinfos)->t_Timestamp
    return pd.Timestamp(year=ts.year,
                        month=ts.month,
                        day=ts.day,
                        hour=ts.hour,
                        minute=ts.minute,
                        second=ts.second,
                        microsecond=ts.microsecond,
                        nanosecond=ts.nanosecond,
                        tz=tz,
                        fold=ts.fold,
                        )


def __get_args2_ts_to_period(kwargs):
    # type:(dict)->tuple
    freq = kwargs.get('freq', 'D')
    how = kwargs.get('how', 'S')
    return freq, how


def __get_args_tz1(tz):
    # type:(t_tzinfos)->tzinfo_t|none_t
    if isinstance(tz, str):
        return tz_.gettz(tz)
    return tz


def __get_args_tz2(dtorts, tz=None):
    # type:(datetime|time_t|t_Timestamp,t_tzinfos)->tzinfo_t|none_t
    if tz is None:
        tz = dtorts.tzinfo
    elif isinstance(tz, str):
        tz = tz_.gettz(tz)
    return tz


def __get_args_date_combine(vtime, **kws):
    # type:(time_t,Any)->date
    if 'start_day' in kws:
        if isinstance(kws['start_day'], str):
            vdate = parse(kws['start_day']).date()
        elif kws['start_day'] is None:
            vdate = date.today()
        else:
            vdate = kws['start_day']
    else:
        vdate = date.today()
    return vdate


def __dttime_to_ts1(vtime, **kws):
    # type:(time_t,Any)->t_Timestamp
    vdate = __get_args_date_combine(vtime, **kws)
    return pd.Timestamp.combine(vdate, vtime).replace(tzinfo=vtime.tzinfo)


def __dttime_to_ts2(vtime, tz=None, **kws):
    # type:(time_t,t_tzinfos,Any)->t_Timestamp
    vdate = __get_args_date_combine(vtime, **kws)
    tz = __get_args_tz2(vtime, tz)
    return pd.Timestamp.combine(vdate, vtime).replace(tzinfo=tz)


def __dttime_to_dt1(vtime, **kws):
    # type:(time_t,Any)->t_Timestamp
    vdate = __get_args_date_combine(vtime, **kws)
    return datetime.combine(vdate, vtime).replace(tzinfo=vtime.tzinfo)


def __dttime_to_dt2(vtime, tz=None, **kws):
    # type:(time_t,t_tzinfos,Any)->t_Timestamp
    vdate = __get_args_date_combine(vtime, **kws)
    tz = __get_args_tz2(vtime, tz)
    return datetime.combine(vdate, vtime).replace(tzinfo=tz)


def __gen_obj_none_none(clsobj: object, kws_keys: list, kws):
    kws = {key: kws[key] for key in kws_keys if key in kws}
    return clsobj(**kws)


def __gen_obj_none_str(
        clsobj: object,
        kws_keys: list,
        freq: str,
        key: str,
        kws):
    kws = {k: kws[k] for k in kws_keys if k in kws}
    if freq:
        kws[key] = freq
    return clsobj(**kws)


@dispatch.multi((none_t, none_t, dict))
def _gen_ts(ts=None, freq=None, kws={}):
    # type:(none_t,none_t,dict)->t_Timestamp
    return __gen_obj_none_none(pd.Timestamp, _m_ts_kws_keys1, kws)


@dispatch.multi((none_t, str, dict))
def _gen_ts(ts=None, freq='', kws={}):
    # type:(none_t,str,dict)->t_Timestamp
    return __gen_obj_none_str(pd.Timestamp, _m_ts_kws_keys1, freq, 'freq', kws)


@dispatch
def _gen_ts(ts: str, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    """
    用途:str转pd.Timestamp
    说明:str无时区则数值相同;有时区若指定新时区则不同
    参数:
    dt:str时间
        格式1:'2020-03-14 15:32:52', '2020-03-14T15:32:52.192548651'
                     '2022-08-21 17:29:25.549638+0800'
                     '2022-08-21 17:29:25.549638CST+0800'
                     '2022-08-21 17:29:25.549638CST'#err
        格式2:'20200314153252192548651', '20200314153252'
    tz:str,dateutil or None( pytz,tzlocal,zoneinfo 不建议用)时区
    **kwargs:忽略
    说明:
        格式1月日时分秒可为1位,后面为微秒192548+651纳秒 T可为空格
        格式2月日时分秒必为2位,后面为微秒192548+651纳秒
        格式1,2年为4位;可输入年(月,日,时分秒,微秒均可省略)
    """
    def str_adjust(s):
        if not s.isdigit():
            return s

        n = len(s)
        if n == 4:
            return '%s-01-01 00:00:00' % s
        elif n == 6:
            return '%s-%s-01 00:00:00' % (s[:4], s[4:6])
        elif n == 8:
            return '%s-%s-%s 00:00:00' % (s[:4], s[4:6], s[6:8])
        elif n == 10:
            return '%s-%s-%s %s:00:00' % (s[:4], s[4:6], s[6:8], s[8:])
        else:
            return s

    return pd.Timestamp(str_adjust(ts), tz=tz)


@dispatch
def _gen_ts(ts: int, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    """
    用途:time(int or float ,struct_time) 转pd.Timestamp
    说明:不同时区数值不同
    参数:
    ts : int, float 要转换为Timestamp的值
    tz:str,dateutil or None( pytz,tzlocal,zoneinfo 不建议用)时区
    **kwargs:unit : str 'D', 'h', 'm', 's', 'ms', 'us', and 'ns'.

    实例:2022-08-21 23:55:35
    t1=time.time()
    t2=time.time_ns()# (1661097335.9809928, 1661097335980992800)

    pd.Timestamp(t1,unit='s')  #Timestamp('2022-08-21 15:55:35.980992794')
    pd.Timestamp(t2,unit='ns')#Timestamp('2022-08-21 15:55:35.980992800')

    pd.Timestamp(t1,unit='s',tz='Asia/Shanghai'),
    #Timestamp('2022-08-21 23:55:35.980992794+0800', tz='Asia/Shanghai')
    pd.Timestamp(t2,unit='ns',tz='Asia/Shanghai')
    #Timestamp('2022-08-21 23:55:35.980992800+0800', tz='Asia/Shanghai')
    """
    unit = kws.get('unit', 'ns')
    return pd.Timestamp(ts, unit=unit, tz=tz)


@dispatch
def _gen_ts(ts: float, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    unit = kws.get('unit', 's')
    return pd.Timestamp(ts, unit=unit, tz=tz)


@dispatch
def _gen_ts(ts: t_sttime, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    """
    用途:struct_time 转pd.Timestamp
    参数:
    ts : struct_time 要转换为Timestamp的值
    tz:str,dateutil or None( pytz,tzlocal,zoneinfo 不建议用)时区
    """
    return pd.Timestamp(year=ts.tm_year, month=ts.tm_mon, day=ts.tm_mday,
                        hour=ts.tm_hour, minute=ts.tm_min, second=ts.tm_sec,
                        tz=tz if tz else tzwinlocal_t())


@dispatch
def _gen_ts(dt: datetime, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    """
    用途:datetime转pd.Timestamp(不同时区值不同)
    参数:
        dt : datetime-like(dt.datetime,dt.date,dt.time) 要转换为Timestamp的值
        tz:str,dateutil or None( pytz,tzlocal,zoneinfo 不建议用)时区
    """
    tz = tz if tz else dt.tzinfo
    return __create_ts_from_dt(dt, tz=tz)


@dispatch
def _gen_ts(ts: t_arrow, tz: t_tzinfos = None,
            kwargs: dict = {}) -> t_Timestamp:
    dt = ts.datetime
    tz = tz if tz else dt.tzinfo
    return __create_ts_from_dt(dt, tz=tz)


@dispatch
def _gen_ts(vdate: date, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    return pd.Timestamp(vdate, tz=tz)


@dispatch
def _gen_ts(dt: time_t, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    return __dttime_to_ts2(dt, tz, **kws)


@dispatch.multi((pd.Timestamp, str, dict), (pd.Timestamp,
                none_t, dict), (pd.Timestamp, tzinfo_t, dict))
def _gen_ts(ts, tz=None, kws={}):
    # type:(pd.Timestamp, t_tzinfos, dict)->t_Timestamp
    if tz is None:
        return ts
    return __create_ts_from_ts(ts, tz=tz)


@dispatch
def _gen_ts(ts: t_Period, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    freq, how = __get_args2_ts_to_period(kws)
    return ts.to_timestamp(freq, how, tz)


@dispatch
def _gen_ts(ts: t_dt64, tz: t_tzinfos = None, kws: dict = {}) -> t_Timestamp:
    """
    用途:np.datetime64转pd.Timestamp
    参数:
        ts:np.datetime64 无时区,从1970-01-01T00:00:00的偏移量
        tz:str,dateutil or None( pytz,tzlocal,zoneinfo 不建议用)时区
    实例:
    pd.Timestamp(dt64).tz_localize('UTC').tz_convert(tz)
    """
    ts = pd.Timestamp(ts)
    if tz is None:
        return ts
    return __create_ts_from_ts(ts, tz=tz)


@dispatch.multi((none_t, none_t, dict))
def _gen_dt(ts=None, freq=None, kws={}):
    # type:(none_t,none_t,dict)->datetime
    return __gen_obj_none_none(datetime, _m_dt_kws_keys1, kws)


@dispatch.multi((none_t, str, dict))
def _gen_dt(ts=None, freq='', kws={}):
    # type:(none_t,str,dict)->datetime
    return __gen_obj_none_str(datetime, _m_dt_kws_keys1, freq, 'freq', kws)


@dispatch
def _gen_dt(dt: str, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    """
    用途:str转datetime
    说明:str无时区则数值相同;有时区若指定新时区则不同
    参数:
    ts:str时间
       格式1:2020-03-14 15:32:52, 2020-03-14T15:32:52.192548651
                   '2022-08-21 17:29:25.549638+0800'
       格式2:20200314153252192548651, 20200314153252
    tz:str,dateutil or None( pytz,tzlocal,zoneinfo 不建议用)时区
    说明:
       格式1月日时分秒可为1位,后面为微秒192548+651纳秒 T可为空格
       格式2月日时分秒必为2位,后面为微秒192548+651纳秒
       格式1,2年为4位;可输入年(月,日,时分秒,微秒均可省略)
    注意:忽略ns
    """
    # """ pd.Timestamp tz='China Standard Time'报错"""
    # if str(tz) in M_WIN_TZNAMES:
    #     tz = tz_.gettz(M_WIN_TZNAMES[str(tz)])
    return pd.Timestamp(dt, tz=tz).to_pydatetime(warn=False)


@dispatch
def _gen_dt(ts: int, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    unit = kws.get('unit', 'ns')
    return pd.Timestamp(ts, unit=unit, tz=tz).to_pydatetime(warn=False)


@dispatch
def _gen_dt(ts: float, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    unit = kws.get('unit', 's')
    return pd.Timestamp(ts, unit=unit, tz=tz).to_pydatetime(warn=False)


@dispatch
def _gen_dt(dt: t_sttime, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    tz = __get_args_tz2(tz, tzwinlocal_t())
    return datetime(*dt[:6], tzinfo=tz)


@dispatch
def _gen_dt(dt: datetime, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    tz = __get_args_tz2(dt, tz)
    return __create_dt(dt, tz)


@dispatch
def _gen_dt(dt: t_arrow, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    dt = dt.datetime
    tz = __get_args_tz2(tz, dt.tzinfo)
    return __create_dt(dt, tz)


@dispatch
def _gen_dt(dt: date, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    tz = __get_args_tz1(tz)
    return datetime(dt.year, dt.month, dt.day, tzinfo=tz)


@dispatch
def _gen_dt(dt: time_t, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    return __dttime_to_dt2(dt, tz, **kws)


@dispatch
def _gen_dt(dt: t_Timestamp, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    tz = __get_args_tz2(dt, tz)
    return __create_dt(dt, tz)


@dispatch
def _gen_dt(dt: t_Period, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    freq = kws.get('freq', 'D')
    how = kws.get('how', 'S')
    return dt.to_timestamp(freq, how, tz).to_pydatetime(False)


@dispatch
def _gen_dt(dt64: t_dt64, tz: t_tzinfos = None, kws: dict = {}) -> datetime:
    dt = dt64.astype(datetime)
    if tz:
        dt = dt.replace(tzinfo=tz_.gettz(tz) if isinstance(tz, str) else tz)
    return dt


@dispatch
def _gen_dt64(dt: str, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    if f is None:
        tz = kws.get('tz', None)
        return pd.Timestamp(dt, tz=tz).to_datetime64()
    return np.datetime64(dt, f)


@dispatch
def _gen_dt64(dt: int, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    if f is None:
        unit = kws.get('unit', 'ns')
        return pd.Timestamp(dt, unit=unit).to_datetime64()
    return np.datetime64(dt, f)  # int 必须提供单位


@dispatch
def _gen_dt64(dt: float, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    tz = kws.get('tz', None)
    unit = kws.get('unit', 's')
    return pd.Timestamp(dt, unit=unit, tz=tz).to_datetime64()


@dispatch
def _gen_dt64(dt: t_sttime, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    dt = datetime(*dt[:6], tzinfo=tzwinlocal_t())
    return pd.Timestamp(dt).to_datetime64()


@dispatch
def _gen_dt64(dt: t_date, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    return pd.Timestamp(dt).to_datetime64()


@dispatch
def _gen_dt64(dt: t_arrow, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    return pd.Timestamp(dt.datetime).to_datetime64()


@dispatch
def _gen_dt64(dt: time_t, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    _kws = {'start_day': kws.get('start_day', None)}
    return __dttime_to_ts1(dt, **kws).to_datetime64()


@dispatch
def _gen_dt64(dt: t_Timestamp, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    return dt.to_datetime64()


@dispatch
def _gen_dt64(ts: t_Period, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    freq, how = __get_args2_ts_to_period(kws)
    return ts.to_timestamp(freq, how).to_datetime64()


@dispatch
def _gen_dt64(dt: t_dt64, f: t_strnone = None, kws: dict = {}) -> t_dt64:
    return dt


@dispatch.multi((none_t, none_t, dict))
def _gen_period(ts=None, freq=None, kws={}):
    # type:(none_t,none_t,dict)->t_Period
    """ Period(value=None, freq=None, ordinal=None, year=None, month=None,
       quarter=None, day=None, hour=None, minute=None, second=None)
    """
    if 'freq' not in kws:
        kws['freq'] = 'D'
    return __gen_obj_none_none(pd.Period, _m_period_kws_keys1, kws)


@dispatch.multi((none_t, str, dict))
def _gen_period(ts=None, freq='D', kws={}):
    # type:(none_t,str,dict)->t_Period
    return __gen_obj_none_str(
        pd.Period,
        _m_period_kws_keys1,
        freq,
        'freq',
        kws)


@dispatch.multi((str, str, dict), (str, none_t, dict))
def _gen_period(period, freq=None, kws={}):
    # type:(str,t_strnone,dict)->t_Period
    return pd.Period(period, freq)


@dispatch.multi((int, none_t, dict))
def _gen_period(period, freq=None, kws={}):
    # type:(int,none_t,dict)->t_Period
    try:
        return pd.Period(period)
    except Exception:
        ts = pd.Timestamp(period, unit=kws.get('unit', 'ns'))
        return ts.tz_localize(None).to_period(freq='ns')


@dispatch.multi((int, str, dict))
def _gen_period(period, freq, kws={}):
    # type:(int,str,dict)->t_Period
    try:
        return pd.Period(period)
    except Exception:
        ts = pd.Timestamp(period, unit=kws.get('unit', 'ns'))
        return ts.tz_localize(None).to_period(freq=freq)


@dispatch.multi((float, none_t, dict))
def _gen_period(period, freq=None, kws={}):
    # type:(float,none_t,dict)->t_Period
    ts = pd.Timestamp(period, unit=kws.get('unit', 's'))
    return ts.tz_localize(None).to_period(freq='ns')


@dispatch.multi((float, str, dict))
def _gen_period(period, freq, kws={}):
    # type:(float,str,dict)->t_Period
    ts = pd.Timestamp(period, unit=kws.get('unit', 's'))
    return ts.tz_localize(None).to_period(freq=freq)


@dispatch.multi((arrow_t, none_t, dict))
def _gen_period(dt, freq=None, kws={}):
    # type:(t_arrow,none_t,dict)->t_Period
    return pd.Period(dt.datetime, 'us')


@dispatch.multi((arrow_t, str, dict))
def _gen_period(dt, freq, kws={}):
    # type:(t_arrow,str,dict)->t_Period
    return pd.Period(dt.datetime, freq)


@dispatch.multi((datetime, none_t, dict))
def _gen_period(dt, freq=None, kws={}):
    # type:(datetime,none_t,dict)->t_Period
    return pd.Period(dt, 'us')


@dispatch.multi((datetime, str, dict))
def _gen_period(dt, freq, kws={}):
    # type:(datetime,str,dict)->t_Period
    return pd.Period(dt, freq)


@dispatch.multi((date, none_t, dict))
def _gen_period(dt, freq=None, kws={}):
    # type:(date,none_t,dict)->t_Period
    return pd.Period(dt, 'D')


@dispatch.multi((date, str, dict))
def _gen_period(dt, freq, kws={}):
    # type:(date,str,dict)->t_Period
    return pd.Period(dt, freq)


@dispatch.multi((time_t, none_t, dict))
def _gen_period(dt, freq=None, kws={}):
    # type:(t_time,none_t,dict)->t_Period
    dt = __dttime_to_dt1(dt, **kws)
    return pd.Period(dt, 'us')


@dispatch.multi((time_t, str, dict))
def _gen_period(dt, freq, kws={}):
    # type:(t_time,str,dict)->t_Period
    dt = __dttime_to_dt1(dt, **kws)
    return pd.Period(dt, freq)


@dispatch.multi((sttime_t, none_t, dict))
def _gen_period(dt, freq=None, kws={}):
    # type:(t_sttime,none_t,dict)->t_Period
    dt = datetime(*dt[:6], tzinfo=tzwinlocal_t())
    return pd.Period(dt, 'us')


@dispatch.multi((sttime_t, str, dict))
def _gen_period(dt, freq, kws={}):
    # type:(t_sttime,str,dict)->t_Period
    dt = datetime(*dt[:6], tzinfo=tzwinlocal_t())
    return pd.Period(dt, freq)


@dispatch.multi((pd.Period, none_t, dict))
def _gen_period(period, freq=None, kws={}):
    # type:(t_Period,none_t,dict)->t_Period
    return period


@dispatch.multi((pd.Period, str, dict))
def _gen_period(period, freq, kws={}):
    # type:(t_Period,str,dict)->t_Period
    how = kws.get('how', 'S')
    return period.asfreq(dt, freq, how)


@dispatch.multi((pd.Timestamp, none_t, dict))
def _gen_period(ts, freq=None, kws={}):
    # type: (t_Timestamp,none_t,dict) ->  pd.Period
    return ts.tz_localize(None).to_period(freq='ns')


@dispatch.multi((pd.Timestamp, str, dict))
def _gen_period(ts, freq, kws={}):
    # type: (t_Timestamp,str,dict) ->  pd.Period
    return ts.tz_localize(None).to_period(freq)


@dispatch.multi((np.datetime64, none_t, dict))
def _gen_period(dt64, freq=None, kws={}):
    # type: (np.datetime64,none_t,dict) ->  pd.Period
    dt = dt64.astype(datetime)
    return pd.Period(dt, 'us' if isinstance(dt, datetime) else 'D')


@dispatch.multi((np.datetime64, str, dict))
def _gen_period(dt64, freq, kws={}):
    # type: (np.datetime64,str,dict) ->  pd.Period
    dt = dt64.item()
    return pd.Period(dt, freq)


@dispatch.multi((str, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    tz = kws.get('tz', None)
    return _gen_ts(dt, tz).isoformat(sep, timespec)


@dispatch.multi((int, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    # type:(int,str,str,dict)->str
    tz = kws.get('tz', None)
    _kws = {'unit': kws.get('unit', 'ns')}
    return _gen_ts(dt, tz, _kws).isoformat(sep, timespec)


@dispatch.multi((float, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    # type:(float,str,str,dict)->str
    tz = kws.get('tz', None)
    _kws = {'unit': kws.get('unit', 's')}
    return _gen_ts(dt, tz, _kws).isoformat(sep, timespec)


@dispatch.multi((sttime_t, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    # type:(t_sttime,str,str,dict)->str
    return _gen_ts(dt).isoformat(sep, timespec)


@dispatch.multi((t_arrow, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    # type:(t_arrow,str,str,dict)->str
    return dt.datetime.isoformat(sep, timespec)


@dispatch
def _isostr(dt: Union[datetime, t_Timestamp], sep: str = 'T',
            timespec: str = 'auto', kws: dict = {}) -> str:
    return dt.isoformat(sep, timespec)


@dispatch.multi((date, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    # type:(date,str,str,dict)->str
    return dt.isoformat()


@dispatch.multi((time_t, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    # type:(time_t,str,str,dict)->str
    return dt.isoformat(timespec)


@dispatch.multi((pd.Period, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    # type:(t_Period,str,str,dict)->str
    _kws = {'freq': kws.get('freq', 'D'), 'how': kws.get('how', 'S')}
    return _gen_ts(dt, tz, _kws).isoformat(sep, timespec)


@dispatch.multi((np.datetime64, str, str, dict))
def _isostr(dt, sep='T', timespec='auto', kws={}):
    # type:(np.datetime64,str,str,dict)->str
    return pd.Timestamp(dt).isoformat(sep, timespec)


def __default_fmt(fmt='', dft_fmt='%Y-%m-%d %H:%M:%S.%f'):
    if fmt:
        return fmt
    return dft_fmt


@dispatch
def _to_str(dt: str, fmt: str, kws: dict = {}) -> str:
    tz = kws.get('tz', None)
    fmt = __default_fmt(fmt)
    return _gen_ts(dt, tz).strftime(fmt)


@dispatch
def _to_str(dt: str, fmt: none_t = None, kws: dict = {}) -> str:
    tz = kws.get('tz', None)
    return _gen_ts(dt, tz).isoformat()


@dispatch.multi((int, str, dict), (float, str, dict))
def _to_str(dt, fmt, kws={}):
    # type:(t_intfloat,str,dict)->str
    _kws = {'unit': kws.get('unit', 's' if isinstance(dt, float) else 'ns')}
    tz = kws.get('tz', None)
    fmt = __default_fmt(fmt)
    return _gen_ts(dt, tz, _kws).strftime(fmt)


@dispatch.multi((int, none_t, dict), (float, none_t, dict))
def _to_str(dt, fmt=None, kws={}):
    # type:(t_intfloat,none_t,dict)->str
    _kws = {'unit': kws.get('unit', 's' if isinstance(dt, float) else 'ns')}
    tz = kws.get('tz', None)
    return _gen_ts(dt, tz, _kws).isoformat()


@dispatch.multi((sttime_t, str, dict))
def _to_str(dt, fmt, kws={}):
    # type:(t_sttime,str,dict)->str
    fmt = __default_fmt(fmt, dft_fmt='%Y-%m-%d %H:%M:%S')
    return time.strftime(fmt, dt)


@dispatch.multi((sttime_t, none_t, dict))
def _to_str(dt, fmt=None, kws={}):
    # type:(t_sttime,none_t,dict)->str
    tz = kws.get('tz', None)
    return _gen_dt(dt, tz).isoformat()


@dispatch.multi((arrow_t, str, dict))
def _to_str(dt, fmt, kws={}):
    # type:(t_arrow,str,dict)->str
    fmt = __default_fmt(fmt)
    return dt.datetime.strftime(fmt)


@dispatch.multi((arrow_t, none_t, dict))
def _to_str(dt, fmt, kws={}):
    # type:(t_arrow,none_t,dict)->str
    return dt.datetime.isoformat()


@dispatch.multi((datetime, str, dict))
def _to_str(dt, fmt, kws={}):
    # type:(datetime,str,dict)->str
    fmt = __default_fmt(fmt)
    return dt.strftime(fmt)


@dispatch.multi((datetime, none_t, dict))
def _to_str(dt, fmt, kws={}):
    # type:(datetime,none_t,dict)->str
    return dt.isoformat()


@dispatch.multi((date, str, dict))
def _to_str(dt, fmt, kws={}):
    # type:(date,str,dict)->str
    fmt = __default_fmt(fmt, dft_fmt='%Y-%m-%d')
    return dt.strftime(fmt)


@dispatch.multi((date, none_t, dict))
def _to_str(dt, fmt, kws={}):
    # type:(date,none_t,dict)->str
    return dt.isoformat()


@dispatch.multi((time_t, str, dict))
def _to_str(dt, fmt, kws={}):
    # type:(time_t,str,dict)->str
    fmt = __default_fmt(fmt, dft_fmt='%H:%M:%S.%f')
    return dt.strftime(fmt)


@dispatch.multi((time_t, none_t, dict))
def _to_str(dt, fmt, kws={}):
    # type:(time_t,none_t,dict)->str
    return dt.isoformat()


@dispatch.multi((pd.Timestamp, str, dict))
def _to_str(dt, fmt, kws={}):
    # type:(t_Timestamp,str,dict)->str
    fmt = __default_fmt(fmt)
    return dt.strftime(fmt)


@dispatch.multi((pd.Timestamp, none_t, dict))
def _to_str(dt, fmt=None, kws={}):
    # type:(t_Timestamp,none_t,dict)->str
    return dt.isoformat()


@dispatch.multi((pd.Period, str, dict))
def _to_str(dt, fmt, kws={}):
    # type:(t_Period,str,dict)->str
    return dt.strftime(fmt)


@dispatch.multi((pd.Period, none_t, dict))
def _to_str(dt, fmt=None, kws={}):
    # type:(t_Period,none_t,dict)->str
    tz = kwargs.get('tz', None)
    _kws = {'freq': kwargs.get('freq', 'D'), 'how': kwargs.get('how', 'S')}
    return _gen_ts(dt, tz, _kws).isoformat()


@dispatch.multi((np.datetime64, str, dict))
def _to_str(dt64, fmt, kws={}):
    # type:(np.datetime64,str,dict)->str
    fmt = __default_fmt(fmt)
    return dt64.item().strftime(fmt)


@dispatch.multi((np.datetime64, none_t, dict))
def _to_str(dt64, fmt=None, kws={}):
    # type:(np.datetime64,none_t,dict)->str
    return dt64.item().isoformat()  # dt64.astype(datetime)


@dispatch.multi((str, dict))
def _f_timestamp(dt, kws={}):
    # type:(str,dict)->str
    tz = kws.get('tz', None)
    return _gen_ts(dt, tz).timestamp()


@dispatch
def _f_timestamp(dt: t_intfloat, kws: dict = {}) -> Union[int, float]:
    return dt


@dispatch
def _f_timestamp(dt: none_t = None, kws: dict = {}) -> Union[int, float]:
    if 'isfloat' in kws and kws['isfloat']:
        return time.time()
    else:
        return time.time_ns()


@dispatch.multi((sttime_t, dict))
def _f_timestamp(dt, kws={}):
    # type:(t_sttime,dict)->str
    return _gen_ts(dt).timestamp()


@dispatch.multi((arrow_t, dict))
def _f_timestamp(dt, kws={}):
    # type:(date,dict)->str
    return dt.datetime.timestamp()


@dispatch.multi((datetime, dict), (pd.Timestamp, dict))
def _f_timestamp(dt, kws={}):
    # type:(datetime|t_Timestamp,dict)->str
    return dt.timestamp()


@dispatch.multi((date, dict))
def _f_timestamp(dt, kws={}):
    # type:(date,dict)->str
    return pd.Timestamp(dt).timestamp()


@dispatch.multi((time_t, dict))
def _f_timestamp(dt, kws={}):
    # type:(time_t,dict)->str
    tz = kws.get('tz', None)
    _kws = {'start_day': kws.get('start_day', None)}
    return _gen_ts(dt, tz, _kws).timestamp()


@dispatch.multi((pd.Period, dict))
def _f_timestamp(dt, kws={}):
    # type:(t_Period,dict)->str
    tz = kwargs.get('tz', None)
    _kws = {'freq': kws.get('freq', 'D'), 'how': kws.get('how', 'S')}
    return _gen_ts(dt, tz, _kws).timestamp()


@dispatch.multi((np.datetime64, dict))
def _f_timestamp(dt, kws={}):
    # type:(np.datetime64,dict)->str
    return pd.Timestamp(dt).timestamp()


def _parse_dt_match_num_tzstr(s, parserinfo, old_kws, kws):
    # 匹配'2013-05-05 12:30:45 America/Chicago'
    lst = re.findall(r'[a-zA-Z_]+[/][a-zA-Z_]+', s)
    if lst:
        tzname = lst[0]
        s = s.replace(tzname, '')
        if 'tzinfos' not in old_kws:
            return parse(s).replace(tzinfo=tz_.gettz(tzname))
    return parse(s, parserinfo, **kws)


def _parse_dt_match_tzname(s, tz_name: str, tz: tzinfo_t, parserinfo, kws):
    # 匹配'2013-05-05 12:30:45CST+8:00'中的CST
    if 'tzinfos' in kws:
        kws['tzinfos'][tz_name] = tz
    else:
        kws['tzinfos'] = {tz_name: tz}
    return parse(s, parserinfo, **kws)


def _parse_dt_none(s, tz=None, **kws):
    # type: (str,none_t,Any) ->  datetime
    def get_tzinfos(tz_name, kws):
        tzinfos = kws['tzinfos']
        if tz_name in tzinfos:
            _tz = tzinfos[tz_name]
            _tz = tz_.gettz(_tz) if isinstance(_tz, str) else _tz
            tzinfos[tz_name] = _tz
        else:
            if tz_name in TZ_NAMES:
                tzinfos[tz_name] = tz_.gettz(TZ_NAMES[tz_name][0])
            else:
                tzinfos[tz_name] = tzwinlocal_t()

    old_kws = kws.copy()
    parserinfo = kws.pop('parserinfo', None)
    lst = re.findall('[A-Z]{3,4}', s)
    if lst:
        tz_name = lst[0]
        if 'tzinfos' in kws:
            get_tzinfos(tz_name, kws)
        else:
            kws['ignoretz'] = True
        return parse(s, parserinfo, **kws)
    else:
        return _parse_dt_match_num_tzstr(s, parserinfo, old_kws, kws)


def _parse_dt_str(s, tz, **kws):
    # type: (str,str,Any) ->  datetime
    old_kws = kws.copy()
    parserinfo = kws.pop('parserinfo', None)
    lst = re.findall('[A-Z]{3,4}', s)
    if lst:
        tz = tz_.gettz(tz)
        return _parse_dt_match_tzname(s, lst[0], tz, parserinfo, kws)
    else:
        return _parse_dt_match_num_tzstr(s, parserinfo, old_kws, kws)


def _parse_dt_tz(s, tz, **kws):
    # type: (str,tzinfo_t,Any) ->  datetime
    old_kws = kws.copy()
    parserinfo = kws.pop('parserinfo', None)
    lst = re.findall('[A-Z]{3,4}', s)
    if lst:
        return _parse_dt_match_tzname(s, lst[0], tz, parserinfo, kws)
    else:
        return _parse_dt_match_num_tzstr(s, parserinfo, old_kws, kws)


@dispatch.multi((none_t, none_t, dict),
                (none_t, tzinfo_t, dict), (none_t, str, dict))
def _arrow_get(ts=None, tz=None, kws={}):
    # type:(none_t,t_tzinfos,dict)->arrow.Arrow
    if tz is None:
        return arrow.get(**kws)  # arw_utc
    else:
        return arrow.get(**kws).to(tz)


@dispatch.multi((tuple, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(tuple,nont_t,dict)->arrow.Arrow
    return arrow.get(**ts, **kws)


@dispatch.multi((tuple, str, dict), (tuple, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(tuple,str|tzinfo_t,dict)->arrow.Arrow
    return arrow.get(**args, **kws).to(tz)


@dispatch.multi((int, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(int,none_t,dict)->arrow.Arrow
    return arrow.get(ts * 1.0 / 10**9, **kws)


@dispatch.multi((int, str, dict), (int, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(int,str|tzinfo_t,dict)->arrow.Arrow
    return arrow.get(ts * 1.0 / 10**9, **kws).to(tz)


@dispatch.multi((float, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(float,none_t,dict)->arrow.Arrow
    return arrow.get(ts, **kws)


@dispatch.multi((float, str, dict), (float, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(float,str|tzinfo_t,dict)->arrow.Arrow
    return arrow.get(ts, **kws).to(tz)


@dispatch.multi((str, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(str,none_t,dict)->arrow.Arrow
    try:
        return arrow.get(ts, **kws)  # ISO 8601-formatted str
    except Exception:
        dt = _parse_dt_none(ts, tz, **kws)
        return arrow.get(dt, **kws)


@dispatch.multi((str, str, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(str,str,dict)->arrow.Arrow
    try:
        return arrow.get(ts, **kws).to(tz)  # ISO 8601-formatted str
    except Exception:
        dt = _parse_dt_str(ts, tz, **kws)
        return arrow.get(dt, **kws)


@dispatch.multi((str, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(str,tzinfo_t,dict)->arrow.Arrow
    try:
        return arrow.get(ts, **kws).to(tz)  # ISO 8601-formatted str
    except Exception:
        dt = _parse_dt_tz(ts, tz, **kws)
        return arrow.get(dt, **kws)


@dispatch.multi((sttime_t, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(t_sttime,none_t,dict)->arrow.Arrow
    return arrow.get(ts, **kws)


@dispatch.multi((sttime_t, str, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(t_sttime,str,dict)->arrow.Arrow
    return arrow.get(ts, **kws).to(tz)


@dispatch.multi((sttime_t, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(t_sttime,tzinfo_t,dict)->arrow.Arrow
    return arrow.get(ts, **kws).to(tz)


@dispatch.multi((tuple, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(tuple,none_t,dict)->arrow.Arrow
    return arrow.get(ts, **kws)


@dispatch.multi((tuple, str, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(tuple,str,dict)->arrow.Arrow
    return arrow.get(ts, **kws).to(tz)


@dispatch.multi((tuple, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(tuple,tzinfo_t,dict)->arrow.Arrow
    return arrow.get(ts, **kws).to(tz)


@dispatch.multi((datetime, none_t, dict), (date, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(datetime|date,none_t,dict)->arrow.Arrow
    return arrow.get(ts, **kws)


@dispatch.multi((datetime, str, dict), (date, str, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(datetime|date,str,dict)->arrow.Arrow
    return arrow.get(ts, tz, **kws)


@dispatch.multi((datetime, tzinfo_t, dict), (date, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(datetime|date,tzinfo_t|none_t,dict)->arrow.Arrow
    return arrow.get(ts, tz, **kws)


@dispatch.multi((time_t, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(time_t,none_t,dict)->arrow.Arrow
    ts = __dttime_to_dt2(ts, tz, **kws)
    return arrow.get(ts, **kws)


@dispatch.multi((time_t, str, dict), (time_t, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(datetime|date,str|tzinfo_t,dict)->arrow.Arrow
    ts = __dttime_to_dt2(ts, tz, **kws)
    return arrow.get(ts, **kws)


@dispatch.multi((pd.Timestamp, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(t_Timestamp,none_t,dict)->arrow.Arrow
    return arrow.get(ts.to_pydatetime(False), **kws)


@dispatch.multi((pd.Timestamp, str, dict), (pd.Timestamp, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(t_Timestamp,str|tzinfo_t,dict)->arrow.Arrow
    return arrow.get(ts.to_pydatetime(False), tz, **kws)


@dispatch.multi((arrow_t, none_t, dict))
def _arrow_get(ts, tz=None, kws={}):
    # type:(t_arrow,none_t,dict)->arrow.Arrow
    return ts


@dispatch.multi((arrow_t, str, dict), (arrow_t, tzinfo_t, dict))
def _arrow_get(ts, tz, kws={}):
    # type:(t_arrow,str|tzinfo_t,dict)->arrow.Arrow
    return ts.to(tz)


class Date(object):
    m_fmt = datetime_fmt
    _m_date = date.today()

    def __init__(self):
        pass

    # 日期操作:修改日期时间

    @classmethod
    def _replace_adj_data(cls, dt, tzinfo):
        if isinstance(tzinfo, str):
            tzinfo = tz_.gettz(tzinfo)

        if dt.tzinfo and tzinfo:
            if isinstance(dt.tzinfo, tzlocal_t):
                dt = dt.replace(tzinfo=tzwinlocal_t())
            if isinstance(tzinfo, tzlocal_t):
                tzinfo = tzwinlocal_t()
        return dt, tzinfo

    @classmethod
    def replace(
            cls,
            dt: Union[t_datetime, t_Timestamp],
            year: int = None,
            month: int = None,
            day: int = None,
            hour: int = None,
            minute: int = None,
            second: int = None,
            microsecond: int = None,  # 微秒
            nanosecond: int = None,
            tzinfo: t_tzinfos = None,
            fold=None) -> Union[t_datetime, t_Timestamp]:
        """
        用途:修改日期时间;替换dt.datetime or dt.date or dt.time
        返回:新对象
        参数:
            dt: t_datetime,pd.Timestamp dateutil时区或无
            year: int = None,
            month: int = None,
            day: int = None,
            hour: int = None,
            minute: int = None,
            second: int = None,
            microsecond: int = None,#微秒
            nanosecond: int = None, datetime忽略该参数
            tzinfo=None str or tzinfo (原函数不能为str,在此做了处理)
            fold=None int 0 or 1
        原函数:
        def replace(self, year=None, month=None, day=None)
        def replace(self, hour=None, minute=None, second=None, microsecond=None,tzinfo=None, *, fold=None):
        def replace(self, year=None, month=None, day=None,
            hour=None, minute=None, second=None, microsecond=None, tzinfo=None,*, fold=None):
        """

        def get_kwargs(dt):
            lst1 = ['year', 'month', 'day']
            lst2 = [
                'hour',
                'minute',
                'second',
                'microsecond',
                'tzinfo',
                'fold']
            if isinstance(dt, date):
                lst = lst1
            elif isinstance(dt, time_t):
                lst = lst2
            elif not isinstance(dt, pd.Timestamp):
                lst = lst1 + lst2 + ['nanosecond', ]
            else:
                lst = lst1 + lst2
            return {k: eval(k) for k in lst if eval(k)}

        try:
            return dt.replace(**kwargs)
        except Exception:
            dt, tzinfo = cls._replace_adj_data(dt, tzinfo)
            if tzinfo:
                kwargs['tzinfo'] = tzinfo
            else:
                kwargs.pop('tzinfo', None)
                return dt.replace(**kwargs)

    @classmethod
    def get_delta(cls, **kwargs) -> relativedelta:
        """
        用途:用于获取日期差类似datetime.timedelta
        返回:relativedelta 可直接用于datetime的加减
        参数:
        kwargs:
            dt1=None, dt2=None,
            years=0, months=0, days=0, leapdays=0, weeks=0, hours=0, minutes=0, seconds=0, microseconds=0,
            year=None, month=None, day=None, weekday=None, yearday=None, nlyearday=None,
                hour=None, minute=None, second=None, microsecond=None

        实例:
        dt=datetime.now()                #datetime(2022, 8, 20, 9, 53, 25, 50984)
        dt - relativedelta(months=3)#datetime(2022, 5, 20, 9, 53, 1, 897984)
        dt - relativedelta(month=3) #datetime(2022, 3, 20, 9, 53, 1, 897984)
        """
        return relativedelta(**kwargs)

    @classmethod
    def sub(cls, dt, delta=None, **kwargs):
        # type:(t_datetime,timedelta|relativedelta,Any)->  t_datetime
        """
        用途:获取日期差
        参数:
        dt:dt.datetime or dt.date or dt.time
        delta=None  timedelta or relativedelta 若存在本参数忽略kwargs
        kwargs:relativedelta的关键字参数
            dt1=None, dt2=None,
            years=0, months=0, days=0, leapdays=0, weeks=0,
            hours=0, minutes=0, seconds=0, microseconds=0,
            year=None, month=None, day=None, weekday=None, yearday=None, nlyearday=None,
            hour=None, minute=None, second=None, microsecond=None

        ------------------------------------------------------------------------------------------
        实例:
        dt=datetime.now()                #datetime(2022, 8, 20, 9, 53, 25, 50984)
        dt - relativedelta(months=3)#datetime(2022, 5, 20, 9, 53, 1, 897984)
        dt - relativedelta(month=3) #datetime(2022, 3, 20, 9, 53, 1, 897984)
        """
        if delta is None:
            delta = relativedelta(**kwargs)
        return dt - delta

    @classmethod
    def sub_month(cls, dt, delta=None, monthend=None, **kwargs):
        # type: (t_datetime,timedelta|relativedelta,int,Any) ->  t_datetime
        """
        用途:获取日期差(未考虑时区)
        参数:
        dt:dt.datetime or dt.date or dt.time
        delta=None  timedelta or relativedelta 若存在本参数忽略kwargs
        monthend:int=None(不处理) 指定月末 负值左偏移量;正值指定月末日期
        kwargs:relativedelta的关键字参数
             dt1=None, dt2=None,
             years=0, months=0, days=0, leapdays=0, weeks=0,
             hours=0, minutes=0, seconds=0, microseconds=0,
             year=None, month=None, day=None, weekday=None, yearday=None, nlyearday=None,
             hour=None, minute=None, second=None, microsecond=None)
        ------------------------------------------------------------------------------------------
        实例:
        dt=datetime.now()                #datetime(2022, 8, 20, 9, 53, 25, 50984)
        dt - relativedelta(months=3)#datetime(2022, 5, 20, 9, 53, 1, 897984)
        dt - relativedelta(month=3) #datetime(2022, 3, 20, 9, 53, 1, 897984)
        """
        if delta is None:
            delta = relativedelta(**kwargs)
        dt = dt - delta
        if not monthend or dt.day == monthend:  # None or 0
            return dt

        n_month = calendar.monthrange(dt.year, dt.month)[1]  # (0, 31)
        day = (n_month + monthend) if monthend < 0 else (
            monthend if monthend <= n_month else n_month
        )
        return dt.replace(day=day)

    @classmethod
    def join(cls, vdate, vtime, back_timestamp=False):
        # type: (str|t_datetime, str|t_datetime,bool) ->  datetime|t_Timestamp
        """
        用途:合并日期和时间,返回datetime or pd.Timestamp
        参数:
            vdate:str or date
            vtime:str or dt.time
        实例:
        pd.Timestamp.combine(date(2020, 3, 14), time(15, 30, 15))
        #Timestamp('2020-03-14 15:30:15')
        """
        bt = isinstance(vtime, str)
        s1 = vdate if isinstance(vdate, str) else vdate.strftime('%Y-%m-%d')
        s2 = vtime if bt else vtime.strftime('%H:%M:%S.%f')
        tz = None if bt else vtime.tzinfo
        ts = pd.Timestamp('%s %s' % (s1, s2)).tz_localize(tz)

        if back_timestamp:
            return ts
        return ts.to_pydatetime(False)

    # ===================================================
    # ===================================================
    @classmethod
    def arrow_get(cls, ts=None, tz=None, **kwargs):
        # type:(t_arrow_get,t_tzinfos,Any)->arrow.Arrow
        """
        用途:其他数据类中转arrow.Arrow
        用法:
         _arrow_get()
        _arrow_get(ts:str,tz=None)
        _arrow_get(ts:int,tz=None)     # ns
        _arrow_get(ts:float,tz=None)  # s
        _arrow_get(ts:struct_time,tz=None)
        _arrow_get(ts:datetime,tz=None)
        _arrow_get(ts:date,tz=None)
        _arrow_get(ts:dt.time,tz=None,*,start_day:str|date|None)不提供kwargs用现在日期替代
        _arrow_get(ts:pd.Timestamp,tz=None)#提供时区仅仅替换时区,否则保留原时区
        _arrow_get(ts:tuple,tz=None)
        _arrow_get(ts:arrow.Arrow,tz=None)

        函数:
            get(*args, **kwargs) -> arrow.arrow.Arrow
        参数:
            locale: str ='en-us'(optional) 指定解析器指定区域设置 locale='zh'
            tzinfo:=UTC str or tzinfo object.(原函数参数,本设计用tz替代,取消此参数)
               替换时区,除非使用显式UTC的输入表单或指定位置参数中的时区
            normalize_whitespace: =Flase(optional)
                是否规范化日期时间字符串中的冗余空格(spaces, tabs, and newlines)

        说明:str类型用parse自动推导kws参数为parse的参数;参考parse_dt

        ------------------------------------------------------------------------------------------
        实例1:
        import arrow

        arw_utc=arrow.get()
        arw_local=arw_utc.replace(tzinfo=tz_.tzlocal())
        arw_local.timestamp - arw_utc.timestamp==28800

        #获取当前UTC时间
        arrow.get()#<Arrow [2022-08-22T15:46:56.217806+00:00]>
        arw = arrow.utcnow()

        #获取对象拷贝
        arrow.get(arw)#<Arrow [2022-08-22T15:48:10.207192+00:00]>

        #可转换为浮点时间戳,以在UTC中获得该时间戳
        arrow.get(1367992474.293378)#<Arrow [2013-05-08T05:54:34.293378+00:00]>
        arrow.get(1367992474)             #<Arrow [2013-05-08T05:54:34+00:00]>

        #ISO 8601-formatted 'str'
        arrow.get('2013-09-29T01:26:43.830580')#<Arrow [2013-09-29T01:26:43.830580+00:00]>
        arrow.get('20160413T133656.456289')#<Arrow [2016-04-13T13:36:56.456289+00:00]>

        #tzinfo`:将当前时间转换为该时区
        arrow.get(tz.tzlocal())#<Arrow [2022-08-22T23:54:36.818851+08:00]>

        #单datetime获取UTC datetime
        arrow.get(datetime(2022, 5, 5))#<Arrow [2022-05-05T00:00:00+00:00]>
        arrow.get(date(2013, 5, 5))#<Arrow [2013-05-05T00:00:00+00:00]>

        #datetime含时区获取datetime
        arrow.get(datetime(2022, 5, 5, tzinfo=tz.tzlocal()))#<Arrow [2022-05-05T00:00:00-07:00]>

        #time.struct time
        arrow.get(time.gmtime(0))#<Arrow [1970-01-01T00:00:00+00:00]>

        #iso calendar 'tuple', to get that week date in UTC:
        arrow.get((2022, 18, 7))#<Arrow [2022-05-08T00:00:00+00:00]>

        #2个参数: a naive or aware 'datetime or date',tz:str ot tzinfo:
        arrow.get(datetime(2013, 5, 5), 'US/Pacific')#<Arrow [2013-05-05T00:00:00-07:00]>
        arrow.get(date(2013, 5, 5), 'US/Pacific')#<Arrow [2013-05-05T00:00:00-07:00]>

        #2个参数:both 'str',根据第二个节点的格式来解析第一个节点:
        arrow.get('2013-05-05 12:30:45 America/Chicago', 'YYYY-MM-DD HH:mm:ss ZZZ')
        #<Arrow [2013-05-05T12:30:45-05:00]>

        #2个参数:first a 'str' to parse and second a 'list' of formats to try::
        arrow.get('2013-05-05 12:30:45', ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss'])
        #<Arrow [2013-05-05T12:30:45+00:00]>

        #Three or more** arguments, as for the direct constructor of an 'Arrow' object::
        arrow.get(2013, 5, 5, 12, 30, 45)#<Arrow [2013-05-05T12:30:45+00:00]>

        ------------------------------------------------------------------------------------------
        实例2:属性
        print(ts.year)                     # 当前年
        print(ts.month)                  # 当前月份
        print(ts.day)                      # 当前天
        print(ts.hour)                     # 当前第几个小时
        print(ts.minute)                 # 当前多少分钟
        print(ts.second)                # 当前多少秒
        print(ts.timestamp)           # 获取时间戳
        print(ts.float_timestamp)  # 浮点数时间戳

        utc=arrow.get()#<Arrow [2022-08-27T16:43:10.472553+00:00]>
        utc.datetime#datetime(2022, 8, 27, 16, 43, 10, 472553, tzinfo=tzutc())
        utc.time()#time_t(16, 43, 10, 472553)
        utc.timetz()#time_t(16, 43, 10, 472553, tzinfo=tzutc())
        utc.date()#date(2022, 8, 27)
        utc.timestamp()# 1661618590.472553
        utc.naive#datetime.datetime(2022, 9, 19, 18, 16, 37, 312429)
        utc.tzinfo# tzutc()
        utc.timetuple()

        ts.to('US/Pacific')

        ts.shift(**kwargs):shift获取某个时间之前或之后的时间
        ts = arrow.now()                  # 当前时间2022-09-20T02:20:57.385667+08:00
        print(ts.shift(weeks=-3))     # 三周前2022-08-30T02:20:57.385667+08:00
        print(ts.shift(weeks=+3))    # 三周后2022-10-11T02:20:57.385667+08:00
        print(ts.shift(days=-1))       # 一天前2022-09-19T02:20:57.385667+08:00
        print(ts.shift(days=1))        # 一天后2022-09-21T02:20:57.385667+08:00
        print(ts.shift(weekday=6))  # 距离最近ts的星期日,weekday从0到6
                                                                2022-09-25T02:20:57.385667+08:00

        时间替换a.replace(**kwargs):返回一被替换后arrow对象,原对象不变
        print(ts.replace(year=2023, month=3, day=6,))
        print(ts.replace(hour=12, minute=12, second=12, ).to('UTC'))
        print(ts.replace(microsecond=12).to('local').naive)

        print(ts.format())          #2022-09-20 02:21:57+08:00
        print(ts.format('YYYY-MM-DD HH:mm:ss ZZ'))#同上
        print(ts.ctime())            # 返回日ctime格式Tue Sep 20 02:21:57 2022
        print(ts.weekday())      # 以整数形式返回星期几(0-6)
        print(ts.isoweekday())  # 以整数形式返回一周中的ISO日(1-7)
        print(ts.isocalendar())  # 返回3元组(ISO年,ISO周数,ISO工作日)
                datetime.IsoCalendarDate(year=2022, week=38, weekday=2)
        print(ts.toordinal())       # 返回日期的格雷戈里序数738418

        人性化输出:a.humanize()
        aa = arrow.now()         #2022-09-20T01:24:11.439204+08:00
        print(aa.shift(hours=-1))                     # an hour ago
        print(aa.shift(hours=-1).humanize())  # 相对于当前时间 2022-09-20T04:24:11.439204+08:00
        print(aa.shift(hours=2))                      # in 2 hours
        print(aa.shift(hours=2).humanize())  # 相对于参数时间 =2小时后
        print(aa.shift(hours=2).humanize(locale='zh'))  # locale参数可以指定地区语言
                                                                    2022-09-20T01:24:11.439204+08:00

        只得到任意单位时间中的最大值或最小值
        arrow.utcnow().floor('hour')
        arrow.utcnow().ceil('hour')
        arrow.utcnow().floor('day')
        arrow.utcnow().ceil('day')

        获取任意时间单位的时间跨度
        arrow.utcnow().span('hour')
        arrow.utcnow().span('year')
        arrow.utcnow().span('month')
        arrow.utcnow().span('day')
        #(<Arrow [2022-09-19T00:00:00+00:00]>, <Arrow [2022-09-19T23:59:59.999999+00:00]>)

        参考:
        https://blog.csdn.net/weixin_44799217/article/details/126394161
        """
        return _arrow_get(ts, tz, kwargs)

    @classmethod
    def parse_dt(cls, st, tz=None, **kwargs):
        # type: (str,t_tzinfos,Any) ->  datetime
        """
        用途:parser解析字符串成datetime
        参数:
            timestr:str一种包含日期/时间戳的字符串
            parserinfo:=None 为None parserinfo类对象用默认参数构造

           **kwargs:
            default:=None默认datetime如default=datetime(2023, 9,25)
            ignoretz:bool为True忽略时区对象,返回naive datetime
            tzinfos:映射时区dict or func 如 {"BRST": -10800};
                {"CDT": tz_.gettz('US/Central')};dateutil.tz.tzoffset("BRST", -10800)
                在此做了修改接受str or tzinfo时区对象

            fuzzy=True中英文模糊解析
            dayfirst=True有歧义的解析
            yearfirst=True有歧义的解析

        说明:
        可用时间日期的英文单词,可用横线、逗号、空格等做分隔符。
        没指定时间默认是0点,没指定日期默认是今天,没指定年份默认是今年

        只有月日时,parser会将分隔符前面的数字解析为月份,后面的为日
        当有年份时,在前面的月份超出范围时,会自动判断哪个是月哪个是日

        ------------------------------------------------------------------------------------------
        实例1:
        tzinfos = {"CDT": -21600}
        dt = parse('2017-08-28 06:08:20 CDT', tzinfos=tzinfos)
        dt.tzinfo #tzoffset('CDT', -21600)

        tzinfos = {"CDT": tz_.gettz('US/Central')}
        dt = parse('2017-08-28 06:08:20 CDT', tzinfos=tzinfos)
        dt.tzinfo #tzfile('/usr/share/zoneinfo/US/Central')

        #将时区偏移量编码为字符串:
        dt = parse('2017-08-28 06:08:20-06:00')
        dt.tzinfo #tzoffset(None, -21600)

        实例2:
        datetime.now()
        #datetime(2022, 8, 27, 23, 54, 58, 289905)

        parse('2022-10-21')#datetime(2022, 10, 21, 0, 0)

        #逗号分隔符,只有月-日时要把月放在前面;有年份时年份要放在后面
        parse('3,15')#datetime(2022, 3, 15, 0, 0)

        '2022-10-21','20221021','2022/10/21'年-月-日,'10-21','10/21'
        '3/8','3-8','3/8/2022','2022/3/8'
        '8/3','8-3','8/3/2022',
        '21/10'#ValueError
        '3/15/2022','15/3/2022'自动识别月和日,'Mar 15','Mar15','15 Mar','Mar 15 2022','2022 Mar15'

        #只识别到了前面的日,月将本月11月作为默认月
        parse('15,3')#datetime(2022, 11, 15, 0, 0)
        parse('3,15,2022')#datetime(2022, 3, 15, 0, 0)
        parse('2022,3,15')#ValueError

        实例3:
        from dateutil.parser import *
        from dateutil.tz import *
        from datetime import *

        TZOFFSETS = {'BRST': -10800}
        BRSTTZ = tzoffset('BRST', -10800)
        DEFAULT=datetime(2003, 9,25)

        # 解析结果与字符串中的字符串顺序关联不大
        parse('Thu Sep 25 10:20:30 BRST 2003', tzinfos=TZOFFSETS)
        #datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))

        parse('2003 Thu Sep 25 10:20:30 BRST', tzinfos=TZOFFSETS)
        #datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))

        parse('2003 10:20:30 Thu Sep 25 BRST', tzinfos=TZOFFSETS)
        #datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))

        # 正常情况下是这样的
        parse('2003 10:20:30 Thu Sep 25')#datetime(2003, 9, 25, 10, 20, 30)
        parse('Thu Sep 25 10:20:30 BRST 2003', ignoretz=True)#datetime(2003, 9, 25, 10, 20, 30)

        # 缺省必要的数据时,可以采用default值为补全
        parse('10:20:30 Thu Sep 25', default=DEFAULT)#datetime(2003, 9, 25, 10, 20, 30)
        parse('Thu Sep 25', default=DEFAULT)#datetime(2003, 9, 25, 0, 0)

        # 支持解析标准时间格式
        parse('2020-09-09T10:20:20.5-8:00')
        #datetime(2020, 9, 9, 10, 20, 20, 500000, tzinfo=tzoffset(None, -28800))
        parse('2020-09-09T10:20')#datetime(2020, 9, 9, 10, 20)
        parse('2020-09-09T10')#datetime(2020, 9, 9, 10, 0)
        parse('2020-09-09')#datetime(2020, 9, 9, 0, 0)

        # 支持解析不带分隔符的时间格式
        parse('20200909T102020-8:00')#datetime(2020, 9, 9, 10, 20, 20, tzinfo=tzoffset(None, -28800))
        parse('20200909T102020')#datetime(2020, 9, 9, 10, 20, 20)
        parse('20200909T1020')#datetime(2020, 9, 9, 10, 20)
        parse('20200909T1020')#datetime(2020, 9, 9, 10, 20)
        parse('20200909')#datetime(2020, 9, 9, 0, 0)
        parse('20200909102020')#datetime(2020, 9, 9, 10, 20, 20)

        # 支持有歧义的解析
        parse('10-09-2020')#datetime(2020, 10, 9, 0, 0)
        parse('10-09-2020', dayfirst=True)#datetime(2020, 9, 10, 0, 0)
        parse('10-09-20', dayfirst=True)#datetime(2020, 9, 10, 0, 0)
        parse('10-09-20', yearfirst=True)#datetime(2010, 9, 20, 0, 0)

        # 支持中英文模糊解析
        s = 'today is 25 of Oct 2020 exactly at 10:20:20 with timezone +08:00'
        parse(s, fuzzy=True)#datetime(2020, 10, 25, 10, 20, 20, tzinfo=tzoffset(None, 28800))
        s = '今天是2020年10月20日 10点23分49秒'
        parse(s, fuzzy=True)#datetime(2010, 10, 20, 23, 49)
        s = '今天是2020年10月20日,明天有个预约'
        parse(s, fuzzy=True)#datetime(2022, 10, 20, 0, 0)
        """
        if tz is None:
            return _parse_dt_none(st, tz, **kwargs)
        elif isinstance(tz, str):
            return _parse_dt_str(st, tz, **kwargs)
        else:
            return _parse_dt_tz(st, tz, **kwargs)

    @classmethod
    def now(cls, tz=None, fmt=None):
        # type:(t_tzinfos,str|None)->datetime|str
        """
        用途:获取当前时间(数值不相同)
        参数:
            tz=None时区 str or tzinfo对象
            fmt:str or None 默认返回datetime; fmt=''返回iso字符串,否则返回指定格式str
        """
        dt = datetime.now(tz_.gettz(tz) if isinstance(tz, str) else tz)
        if fmt is None:
            return dt
        if not fmt:
            return dt.isoformat()
        return dt.strftime(fmt)

    @classmethod
    def today(cls, fmt=None):
        # type:(str|None)->date|str
        """
        用途:返回date or str(指定格式字符串或iso字符串)
        参数:fmt:str or None 默认返回date;
                    fmt=''返回iso字符串,否则返回指定格式str
        返回 :date  date(2022, 8, 20)
        """
        dt = date.today()
        if fmt is None:
            return dt
        if fmt == '':
            return dt.isoformat()
        return dt.strftime(fmt)

    @classmethod
    def gen_ts(cls, ts=None, tz=None, **kwargs):
        # type: (t_pydatetimes,t_tzinfos,Any) ->  t_Timestamp
        """
        用途:其他数据类中转pd.Timestamp
        用法:
        _gen_ts(ts:str,tz=None) ts无时区值相同;有时区若指定新时区则不同
        _gen_ts(ts:int,tz=None,*,unit='ns')
        _gen_ts(ts:float,tz=None,*,unit='s')
        _gen_ts(ts:struct_time,tz=None,*,unit=tz.tzwinlocal()) 数据为本地时间
         _gen_ts(ts:arraw.Arraw,tz=None)
        _gen_ts(ts:datetime,tz=None)
        _gen_ts(ts:date,tz=None)
        _gen_ts(ts:dt.time,tz=None,*,start_day:str|date|None)不提供kwargs用现在日期替代
        _gen_ts(ts:pd.Timestamp,tz=None)#提供时区仅仅替换时区,否则保留原时区
        _gen_ts(ts:pd.Period,tz=None,*,freq='D',how='S')
        _gen_ts(ts:np.datetime64,tz=None)
        _gen_ts(**kwargs:dict)

        说明:本类方法是以上函数的便捷包装
        参数:
            ts : str,datetime-like(dt.datetime,dt.date,dt.time), time(int or float ,sttime_t)
                  arraw.Arraw,pd.Timestamp,pd.Period,np.datetime64
            tz : str,dateutil or None(pytz,tzlocal,zoneinfo不建议)时区
            **kwargs:
                start_day=None str or date仅用于ts类型是datetime.time
                unit=None int or None仅用于ts类型是int 默认'ns' 注1
                unit=None float or None仅用于ts类型是float 默认's' 注1
                freq=None str or None仅用于ts类型是pd.Period 默认'D'
                how=None str or None仅用于ts类型是pd.Period 默认'S'
            注1:unit:[ 'D', 'h', 'm', 's', 'ms', 'us', and 'ns'.]

        实例:
        pd.Timestamp('2020-03-14 15:32:52'),
        pd.Timestamp('2020-03-14T15:32:52.192548651')
        pd.Timestamp('2022-08-21 17:29:25.549638+0800')
        pd.Timestamp('20200314153252192548651'),
        pd.Timestamp('20200314153252')

        pd.Timestamp(1513393355.5, unit='s')
        #Timestamp('2017-12-16 03:02:35.500000')

        #这将转换以Unix-epoch的int为单位的秒
        pd.Timestamp(1513393355, unit='s', tz='US/Pacific')
        #Timestamp('2017-12-15 19:02:35-0800', tz='US/Pacific')
        """
        return _gen_ts(ts, tz, kwargs)

    @classmethod
    def gen_dt(cls, dt, tz=None, **kwargs):
        # type: (t_dtall,t_tzinfos,Any) ->  datetime
        """
        用途:将其他日期转为datetime
        用法:
        _gen_dt(dt:str,tz=None) ts无时区值相同;有时区若指定新时区则不同
        _gen_dt(dt:int,tz=None,*,unit='ns')
        _gen_dt(dt:float,tz=None,*,unit='s')
        _gen_dt(dt:struct_time,tz=None,*,unit=tz.tzwinlocal()) 数据为本地时间
        _gen_dt(dt:arraw.Arraw,tz=None)
        _gen_dt(dt:datetime,tz=None)
        _gen_dt(dt:date,tz=None)
        _gen_dt(dt:dt.time,tz=None,*,start_day:str|date|None)不提供kwargs用现在日期替代
        _gen_dt(dt:pd.Timestamp,tz=None)#提供时区仅仅替换时区,否则保留原时区
        _gen_dt(dt:pd.Period,tz=None,*,freq='D',how='S')
        _gen_dt(dt:np.datetime64,tz=None)
         _gen_dt(**kwargs)


        说明:本类方法是以上函数的便捷包装
        参数:
            ts : str,datetime-like(dt.datetime,dt.date,dt.time), time(int or float ,sttime_t)
                  arraw.Arraw,pd.Timestamp,pd.Period,np.datetime64
            tz : str,dateutil or None(pytz,tzlocal,zoneinfo不建议)时区
            **kwargs:
                start_day=None str or date仅用于ts类型是datetime.time
                unit=None int or None仅用于ts类型是int 默认'ns' 注1
                unit=None float or None仅用于ts类型是float 默认's' 注1
                freq=None str or None仅用于ts类型是pd.Period 默认'D'
                how=None str or None仅用于ts类型是pd.Period 默认'S'
            注1:unit:[ 'D', 'h', 'm', 's', 'ms', 'us', and 'ns'.]
        """
        return _gen_dt(dt, tz, kwargs)

    @classmethod
    def gen_dt64(cls, dt, f=None, **kwargs):
        """
        用途:将其他日期转为np.datetime64
        用法:
        _gen_dt64(dt:str,None,*,unit=None) Timestamp.to_datetime64
        _gen_dt64(dt:str,f:str)                        datetime64(str,'D') dt64 str创建
        _gen_dt64(dt:int,None,*,unit=None) Timestamp.to_datetime64
         _gen_dt64(dt:int,f:str)                       datetime64(int,'Y') dt64 int创建
        _gen_dt64(dt:struct_time,f=None)    Timestamp.to_datetime64
        _gen_dt64(dt:arraw.Arraw,f=None)   Timestamp.to_datetime64
        _gen_dt64(dt:datetime,f=None)        Timestamp.to_datetime64
        _gen_dt64(dt:date,f=None)               Timestamp.to_datetime64
        _gen_dt64(dt:dt.time,f=None)           Timestamp.to_datetime64
        _gen_dt64(dt:pd.Timestamp)
        _gen_dt64(dt:pd.Period,None,freq='D',how='S')
        _gen_dt64(dt:np.datetime64)            Timestamp.to_datetime64


        说明:本类方法是以上函数的便捷包装
        格式:np.datetime64(str or int or dt or ts,[freq])
        参数:
           ts : str,datetime-like(dt.datetime,dt.date,dt.time), time(int or float ,sttime_t)
                 arraw.Arraw,pd.Timestamp,pd.Period,np.datetime64
           f : np.datetime64格式字符串[Y, M, W, D, h, m, s, ms, us, ns, ps, fs, as]

           **kwargs:
               start_day=None str or date仅用于ts类型是datetime.time
               freq=None str or None仅用于ts类型是pd.Period 默认'D'
               how=None str or None仅用于ts类型是pd.Period 默认'S'
        注意:原函数不能含有时区;在此处理为有时区的是删除时区
        """
        return _gen_dt64(dt, f, kwargs)

    @classmethod
    def gen_period(cls, dt=None, freq=None, **kwargs):
        """
        用途:将其他日期转为pd.Period
        用法:
        _gen_period(dt:str,freq=None)
        _gen_period(dt:int,freq=None)
        _gen_period(dt:int,freq=None,*,unit=None)            构建日期后在转换
        _gen_period(dt:float,freq=None,*,unit=None)         构建日期后在转换
        _gen_period(dt:struct_time,freq=None,*,tz=None) 默认本地时间
        _gen_period(dt:arraw.Arraw,freq=None)
        _gen_period(dt:datetime,freq=None)
        _gen_period(dt:date,freq=None)
        _gen_period(dt:dt.time,freq=None)
        _gen_period(dt:pd.Timestamp,freq=None)
        _gen_period(dt:pd.Period,freq='D',*,how='S')
        _gen_period(dt:pd.Period,None)
        _gen_period(dt:np.datetime64,freq=None)
        _gen_period(*,freq=None,**kwargs) key=
            ['ordinal', 'year', 'month', 'quarter', 'day', 'hour', 'minute', 'second']

        说明:本类方法是以上函数的便捷包装
        格式:np.Period(str or int or dt or ts,[freq])
        参数:
           ts : str,datetime-like(dt.datetime,dt.date,dt.time), time(int or float ,sttime_t)
                 arraw.Arraw,pd.Timestamp,pd.Period,np.datetime64
           freq : np.datetime64格式字符串 [
                'A-DEC', 'A-JAN', 'A-FEB', 'A-MAR', 'A-APR', 'A-MAY','A-JUN', 'A-JUL',
                'A-AUG', 'A-SEP', 'A-OCT', 'A-NOV', 'Q-DEC', 'Q-JAN', 'Q-FEB', 'Q-MAR',
                'Q-APR', 'Q-MAY', 'Q-JUN', 'Q-JUL', 'Q-AUG', 'Q-SEP', 'Q-OCT', 'Q-NOV',
                'M', 'W-SUN', 'W-MON', 'W-TUE', 'W-WED', 'W-THU', 'W-FRI', 'W-SAT',
                'B', 'D', 'H', 'T', 'S', 'L', 'U', 'N', 'Q', 'A', 'W', 'C']

                注意:当ts=None,freq必为关键字参数

           **kwargs:
               how=None str or date仅用于ts类型是Period
               当dt=None,关键字参数字典为
                    ['ordinal', 'year', 'month', 'quarter', 'day', 'hour', 'minute', 'second']
        """
        return _gen_period(dt, freq=freq, kws=kwargs)

    @classmethod
    def gen_structtime(cls, secs=None, islocal=True):
        # type: (float,bool) ->  t_sttime
        """
        用途:生成struct_time
        参数:secs:float 用time.time()生成的秒,time_ns()错误
        """
        if islocal:
            return time.localtime(secs)
        else:
            return time.gmtime(secs)

    # ===================================================
    # ===================================================
    # 日期相互转换
    @classmethod
    def timetuple_to_f(cls, ts: t_sttime) -> float:
        """用途:将时间元组转为时间戳float秒数time.mktime(ts)"""
        return time.mktime(ts)

    @classmethod
    def f_to_timetuple(cls, second=None, is_utc=False):
        # type: (float|int,bool) ->  t_sttime
        """
        用途:将时间秒毫秒转换为时间元组 产生秒函数:
            (time.time(),dt.datetime.timpstamp(),pd.Timestamp.timestamp())1661172299.9821737 s
            time.time_ns()直接输入错误需将该int  1661172299,982,173,700 ns处理上述类型float
        参数:
            second:float =None秒,int 纳秒
            utc=True 将自Epoch以来的秒转换为UTC格林尼治标准时间
            utc=False 将自这个时代以来的秒转为本地时间的时间元组
        说明:当“秒”没有传入时转换当前时间
        返回:(tm_year,tm_mon,tm_mday,tm_hour,tm_min,
                              tm_sec,tm_wday,tm_yday,tm_isdst)
        """
        if isinstance(second, int):
            second = round(second / 1000000000, 7)
        if is_utc:
            return time.gmtime(second)
        return time.localtime(second)

    @classmethod
    def str_to_timetuple(cls, st, fmt=''):
        # type: (str,str) ->  t_sttime
        """
        实例:
        st = time.strptime(strtime, fmt)
        2020-03-14 15:32:52,2020-03-14T15:32:52.192548651
        20200314153252192548651,20200314153252
        """
        if not fmt:
            fmt = cls.m_fmt['dt']
        return time.strptime(st, fmt)

    @classmethod
    def f_timestamp_to_dt(cls, f_ts, tz=None, **kwargs):
        # type:(int|float,tzinfo_t,Any)->datetime
        """
        用途:将整数ns或float秒转日期
        参数:
            f_ts:int or float
            tz:tzinfo对象
            **kwargs:当tz=None时提供struct_time的默认时区
            无提供默认tzwinlocal(),否则按kwargs['tz_struct_time']
        """
        v = f_ts * 1.0 / 10**9 if f_ts > DT_MAX else f_ts
        tz = tz if tz else kwargs.get('tz_struct_time', tz_.tzwinlocal())
        return datetime.fromtimestamp(v, tz)

    @classmethod
    def dt_to_ts(cls, dt):
        # type: (datetime) ->t_Timestamp
        return pd.Timestamp(dt)

    @classmethod
    def ts_to_dt(cls, ts):
        # type: (t_Timestamp) -> datetime
        return ts.to_pydatetime(False)

    @classmethod
    def ts_to_dt64(cls, ts):
        # type: (t_Timestamp) -> np.datetime64
        """
        实例:
        ts=Timestamp('2022-08-22 10:51:51.617978+0800', tz='Asia/Shanghai')
        ts.to_datetime64()#numpy.datetime64('2022-08-22T02:51:51.617978000')

        2020-03-14 15:32:52,2020-03-14T15:32:52.192548651
        20200314153252192548651,20200314153252
        """
        return ts.to_datetime64()

    @classmethod
    def ts_to_period(cls, ts, freq):
        # type: (t_Timestamp,str) ->  t_Period
        """
        参数:
            ts:pd.Timestamp
            freq: 周期字符串
        构造:
            pd.Period(ts, freq=freq)
            ts.tz_convert(None).to_period(freq)
            ts.to_period(freq)

            2020-03-14 15:32:52,2020-03-14T15:32:52.192548651
            20200314153252192548651,20200314153252
        """
        return ts.to_period(freq=freq)

        # np.datetime64[s]转datetime

    @classmethod
    def dt64_to_dt(cls, dt64, tz=None):
        # type: (np.datetime64,t_tzinfos) ->  datetime
        """
        dt64 = np.datetime64(dt, 's')
        dt64.astype(datetime.datetime)  # datetime.datetime(2022, 3, 8, 20, 0, 5)
        dt64.astype(datetime.date)  # 同上
        """
        dt = dt64.astype(datetime)
        if tz:
            dt = dt.replace(
                tzinfo=tz_.gettz(tz) if isinstance(
                    tz, str) else tz)
        return dt

    @classmethod
    def dt64_to_float(cls, dt64):
        # type: (np.datetime64) ->  float
        """
        dt64.tolist()#1663499215522302000
        dt64.item()#1663499215522302000
        dt64.astype(pd.Timestamp)# 1663499215522302000
        pd.Timestamp(1663499215522302000,unit='ns')
        #Timestamp('2022-09-18 11:06:55.522302')
        """
        return dt64.item()

    @classmethod
    def dt64_to_timestamp(cls, dt64):
        # type: (np.datetime64) ->  t_Timestamp
        return pd.Timestamp(dt64)

    @classmethod
    def dt64_str(cls, dt64):
        # type: (np.datetime64) ->  str
        """
        dt = np.datetime64('2022-03-08 20:00:05')  # numpy.datetime64('2022-03-08T20:00:05')
        dt64 = np.datetime_as_string(dt)  # '2022-03-08T20:00:05'
        """
        return np.datetime_as_string(dt)

    @classmethod
    def period_to_ts(cls, period, freq='D', how='S'):
        # type: (t_Period,str,str) ->  t_Timestamp
        return p.to_timestamp('D', how='S')

    @classmethod
    def period_str(cls, period, fmt):
        # type: (t_Period,str) ->  str
        """
        参数:fmt:
        '%a':区域设置的缩写的工作日名称.
        '%A':区域设置的完整工作日名称.
        '%b':地区设置的缩写月份名称.
        '%B':区域设置的全月名.
        '%c':区域设置的适当日期和时间表示.
        '%d':当月的日期,作为十进制数字[01,31].
        '%f':财政年度,没有世纪,作为十进制数字[00,99] (1)
        '%F':财政年度,以世纪为小数(2)

        '%H':24小时十进制数[00,23]
        '%I':12小时十进制数字[01,12]
        '%j':作为十进制数字[001,366]
        '%m':月份作为十进制数[01,12]
        '%M':分钟,作为十进制数[00,59]
        '%p':区域设置对应的AM或PM.(3)

        '%q':季度[01,04]
        '%S':第二个,表示为十进制数[00,61].(4)
        '%U':一年周数(星期日为一周的第一天)[00,53]
        新年第一个星期天之前的所有日子都被认为是在第0周.(5)
        '%w':工作日作为十进制数字[0(星期日),6].
        '%W':一年中的周数(星期一为一周的第一天),作为十进制数[00,53]
        在新年的第一个星期一之前的所有日子都被认为是在第0周.(5)
        '%x':区域设置的适当日期表示.
        '%X':区域设置的适当时间表示.
        '%y':没有世纪的年份作为小数[00,99].
        '%Y':以世纪为小数的年份.
        '%Z':时区名称(如果没有时区存在,则没有字符).
        '%%':文字'%'字符.

        说明:
        (1)如频率不是按季显示一次,则'%f'指令与'%y'指令相同
        否则,它对应于'财政'年度,由:attr:`qyear`属性定义.
        (2)如频率不是每季度显示一次,则'%F'指令与'%Y'指令相同.
        否则,它对应于'财政'年度,由:attr:`qyear`属性定义.
        (3)如果使用'%I'指令来解析小时,则'%p'指令只影响输出小时字段.
        (4)这个范围实际上是'0'到'61';这就解释了闰秒和(非常罕见的)双闰秒.
        (5)'%U'和'%W'指令仅在指定了一周和年度的日期时用于计算.

        实例:
        a = Period(freq='Q-JUL', year=2006, quarter=1)
        a.strftime('%F-Q%q')#'2006-Q1'
        a.strftime('%b-%Y')# 'Oct-2005'本季度最后一个月date

        a = Period(freq='D', year=2001, month=1, day=1)
        a.strftime('%d-%b-%Y')#'01-Jan-2001'
        a.strftime('%b. %d, %Y was a %A')#'Jan. 01, 2001 was a Monday'
        """
        return period.strftime(fmt)

    # ===================================================
    # ===================================================
    # 其他类型转str
    @classmethod
    def to_str(cls, dt, fmt=None, **kwargs):
        # type:(t_dtall|arrow.Arrow,str,Any)->str
        """
        用途:其他数据类中转str
        用法:
        _to_str(ts:str,fmt:str,*,tz=None)
        _to_str(ts:int,fmt:str)
        _to_str(ts:int,None,*,tz=None,unit='ns')             iso格式
        _to_str(ts:float,fmt:str)
        _to_str(ts:float,None,*,tz=None,unit='s')             iso格式
        _to_str(ts:struct_time,fmt:str)
        _to_str(ts:struct_time,None,*,tz=tz.tzwinlocal())  iso格式
        _to_str(ts:arraw.Arraw,fmt:str)
        _to_str(ts:arraw.Arraw)                   iso格式
        _to_str(ts:datetime,fmt:str)
        _to_str(ts:datetime)                        iso格式
        _to_str(ts:date,fmt:str)
        _to_str(ts:date)                               iso格式
        _to_str(ts:dt.time,fmt:str)
        _to_str(ts:dt.time)                           iso格式
        _to_str(ts:pd.Timestamp,fmt:str)
         _to_str(ts:pd.Timestamp)              iso格式
        _to_str(ts:pd.Period,fmt:str)
        _to_str(ts:pd.Period,None,*,tz=None,freq='D',how='S')  iso格式
        _to_str(ts:np.datetime64,fmt:str)
        _to_str(ts:np.datetime64)               iso格式

        说明:fmt=None返回iso格式str,否则返回指定格式str
        参数:
        ts : str,datetime-like(dt.datetime,dt.date,dt.time), time(int or float ,sttime_t)
           arraw.Arraw,pd.Timestamp,pd.Period,np.datetime64
         fmt:str输出datetime字符串格式(为空有默认值)默认输出iso格式str

        **kwargs:[tz,unit,freq,how]

        备注:
            datetime.strftime(fmt=fmt)
            date.strftime(fmt=fmt)
            time.strftime(format, time.localtime(float, int))
            time.strftime(format, sttime_t)
            pd.Timestamp.strftime(fmt=fmt)
            pd.Period.strftime(fmt=fmt)
            np.datetime64.item().strftime(fmt=fmt)#datetime or date
        """
        return _to_str(dt, fmt, kwargs)

    @classmethod
    def isostr(cls, dt, sep='T', timespec='auto', **kwargs):
        # type:(t_dtall,str,str,Any)->str
        """
        用途:其他日期转iso字符串
        用法:
        _isostr(ts:str,sep:str='T', timespec:str='auto',*,tz=None)
        _isostr(ts:int,sep:str='T', timespec:str='auto',*,tz=None,unit='ns')
        _isostr(ts:float,sep:str='T', timespec:str='auto',*,tz=None,unit='s')
        _isostr(ts:struct_time,sep:str='T', timespec:str='auto')
        _isostr(ts:datetime,sep:str='T', timespec:str='auto')
        _isostr(ts:date)
        _isostr(ts:dt.time,None, timespec:str='auto')
        _isostr(ts:pd.Timestamp,sep:str='T', timespec:str='auto')
        _isostr(ts:pd.Period,sep:str='T', timespec:str='auto',*,tz=None,freq='D',how='S')
        _isostr(ts:np.datetime64,sep:str='T', timespec:str='auto')
        _isostr(ts:arraw.Arraw,sep:str='T', timespec:str='auto')

        返回: string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM].
        参数:
        dt:str,datetime-like time-like,pd.Timestamp,pd.Period,np.datetime64,arrow.Arrow
        sep :str日期时间之间分隔符
        timespec:str 输出元素 'auto', 'hours', 'minutes', 'seconds', 'milliseconds' and 'microseconds'.
        **kwargs:freq='D', how='S' pd.Period.to_timestamp参数

        说明:date无sep,timespec参数;dt.time无sep参数
        说明:参考_gen_ts

        实例1:
        dt=datetime(2022, 8, 27, 0, 27, 52, 855008)
        dt.isoformat()#'2022-08-27T00:27:52.855008'
        dt.isoformat(timespec='hours')#'2022-08-27T00'
        dt.isoformat(timespec='minutes')#'2022-08-27T00:27'

        实例2:
        dt=datetime.now()
        dt.isoformat()# '2022-08-30T11:33:59.398735'
        dt.time().isoformat()#'11:33:59.398735'
        dt.date().isoformat()#'2022-08-30'

        pd.Timestamp(dt).isoformat()# '2022-08-30T11:33:59.398735'
        p=pd.Period('2022-08-30T11:33:59.398735',freq='ns')
        #Period('2022-08-30 11:33:59.398735000', 'N')

        p._gen_ts().isoformat()#'2022-08-30T11:33:59.398735'
        pd.Timestamp(np.datetime64(dt)).isoformat()
        #'2022-08-30T11:33:59.398735'
        """
        return _isostr(dt, sep, timespec, kwargs)

    @classmethod
    def f_timestamp(cls, ts=None, **kwargs):
        # type:(t_dtall|NoneType,Any)->float|int
        """
        用途:转时间戳float or int
        用法:
        _f_timestamp(ts:str,*,tz=None)
        _f_timestamp(ts:int)
        _f_timestamp(ts:float)
        _f_timestamp(None,*,isfloat:bool=None)->int ns
        _f_timestamp(ts:struct_time,*,tz=None)
        _f_timestamp(ts:arraw.Arraw)
        _f_timestamp(ts:datetime)
        _f_timestamp(ts:date)
        _f_timestamp(ts:dt.time,*,start_day:str|date|None)不提供kwargs用现在日期替代
        _f_timestamp(ts:pd.Timestamp)
        _f_timestamp(ts:pd.Period,*,tz=None,freq='D',how='S')
        _f_timestamp(ts:np.datetime64)

        参数:
        ts : str,datetime-like(dt.datetime,dt.date,dt.time), time(int or float ,sttime_t)
        arraw.Arraw,pd.Timestamp,pd.Period,np.datetime64

        **kwargs:
        start_day=None str or date仅用于ts类型是datetime.time
        tz=None 仅用于ts类型是str or Period
        unit=None float or None仅用于ts类型是float 默认's' 注1
        freq=None str or None仅用于ts类型是pd.Period 默认'D'
        how=None str or None仅用于ts类型是pd.Period 默认'S'

        说明:相等的时间时间戳相同;dt.time类型的开始日期默认现在
        注意:datetime(1970,1,1)转时间戳报错,pd.Timestamp不报错

        实例1:
        tz1,tz2='Asia/Shanghai','America/New_York'
        tz3,tz4= 'US/Alaska','Europe/London'

        dt = datetime(1970, 1, 1, 0, 0)
        dt_utc = datetime(1970, 1, 1, tzinfo=pytz.utc)
        dt1=datetime(1970, 1, 1, 0, 0, tzinfo=pytz.timezone(tz1))
        dt2=datetime(1970, 1, 1, 0, 0, tzinfo=pytz.timezone(tz2))
        dt3 = datetime(1970, 1, 1, 0, 0, tzinfo=pytz.timezone(tz3))
        dt4 = datetime(1970, 1, 1, 0, 0, tzinfo=pytz.timezone(tz4))

        #dt.timestamp()  # error
        #datetime(1970, 1, 1).astimezone(pytz.utc)#err

        dt_utc.timestamp()#0.0
        pd.Timestamp(dt).timestamp()  # 0.0
        pd.Timestamp(dt1).timestamp()#-29160.0
        pd.Timestamp(dt2).timestamp()#17760.0
        pd.Timestamp(dt3).timestamp()# -50400.0
        pd.Timestamp(dt4).timestamp()#60.0

        #实例2:
        ts1=pd.Timestamp(dt)#Timestamp('1970-01-01 00:00:00')
        ts2=pd.Timestamp(dt_utc)#Timestamp('1970-01-01 00:00:00+0000', tz='UTC')

        ts1.timestamp()#0.0
        ts2.timestamp()#0.0

        p1=pd.Period(dt, freq='ms')#Period('1970-01-01 00:00:00.000', 'L')
        p2=pd.Period(dt_utc, freq='ms')#Period('1970-01-01 00:00:00.000', 'L')
        assert(p1==p2)
        p1._gen_ts()#Timestamp('1970-01-01 00:00:00')
        p1._gen_ts().timestamp()#0.0

        dt64= np.datetime64(dt)#numpy.datetime64('1970-01-01T00:00:00.000000')

        """
        return _f_timestamp(ts, kwargs)

    # ===================================================
    # ===================================================
    # other:

    @classmethod
    def get_df_tail_time(cls, df, datecol, outfmt=None, **kwargs):
        # type: (pd.DataFrame,str,str,Any) ->  Union[str,datetime]
        """
        用途:返回df指定最后一个工作日
        注意:df日期列必须按升序排列好
        返回:str or datetime
        参数:
        df:DataFrame 包含日期列
        datecol:str list需要解析的日期的列名
            df[datecol]数据类型为int,float(需指定kwargs['unit']:str),sttime_t,str,t_datetime
        fmt:str 按指定格式返回str;空返回datetime
        **kwargs:
            'tz':str时区
            'unit':str int or float 时间单位
            'start_date':str datetime.time类型的开始日期
        """

        def get_str_dt(ts: str):
            if ts.isdigit():
                ts = float(ts)
                unit = kwargs.get('unit', 's')
                ts = pd.Timestamp(ts, unit=unit)
            else:
                ts = pd.Timestamp(ts)
            return ts.tz_localize(tz).to_pydatetime(False)

        ts = df[datecol].iloc[-1]
        tz = kwargs.get('tz', None)
        if isinstance(ts, str):
            ts = get_str_dt(ts)
        elif isinstance(ts, datetime):
            pass
        elif isinstance(ts, pd.Timestamp):
            ts = ts.to_pydatetime(False)
        else:
            raise ValueError

        if not outfmt:
            return ts
        return ts.strftime(outfmt)

    @classmethod
    def get_df_tail_next_bustime(cls, df, datecol, outfmt='', **kwargs):
        # type: (pd.DataFrame,str,str,Any) ->  Union[str,date]
        """
        用途:返回df指定最后一个工作日的下一个工作日
        注意:df日期列必须按升序排列好
        返回:指定格式str(isostr默认) or date
        参数:
            df:DataFrame 包含日期列
            datecol:str list需要解析的日期的列名
                 df[datecol]数据类型为int,float(ns or s),sttime_t,str,t_datetime
            outfmt:按指定格式返回str;''返回iso str,None返回datetime
            **kwargs:
                'tz':str时区;'start_date':str or date,datetime.time类型的开始日期
        """
        def next_bustime(df, datecol, outfmt='', **kwargs):
            ts = df[datecol].iloc[-1]
            tz = kwargs.get('tz', None)
            dt = _gen_ts(ts, tz).to_pydatetime(False)
            bs = cn_calendar.find_workday(1, dt)
            dt = dt.replace(year=bs.year, month=bs.month, day=bs.day)
            if pds.helpers.is_pytz_zone(dt.tzinfo):
                return dt.tzinfo.normalize(dt)
            return dt

        dt = next_bustime(df, datecol, outfmt, **kwargs)
        if outfmt is None:
            return dt
        return dt.strftime(outfmt) if outfmt else dt.isoformat()

    @classmethod
    def is_update_file_time(cls, file, close_market_time='15:30:00'):
        # type: (str,str) ->  bool
        """
        用途:判断文件是否被更新,主要用于更新文件
        返回:bool 文件是否要更新
        参数:
        file:str期货数据保存的文件路径
        close_market_time:str期货闭市时间None默认15:30:00
        说明:
        1) 当距离今天最近的工作日的日期>file修改的时间(若不是工作日则file修改时间的前一个工作日)
            说明文件需要修改,返回True
        2) 若二者工作日相同,若文件修改时间>=15:30:00说明文件已经更新,返回False,否则返回True
        """
        timestamp = os.path.getmtime(file)
        file_dt = arrow.get(timestamp, tzinfo=tzlocal_t()).datetime
        now = datetime.now()

        f_workday = cn_calendar.get_workdays(file_dt, now)
        if not f_workday:
            return False
        if len(f_workday) > 1:
            return True
        mdate = f_workday[0]
        if mdate > file_dt.date():
            return True
        elif mdate < file_dt.date():
            return False
        else:
            close_time = arrow.get(close_market_time, 'HH:mm:ss').time()
            return file_dt.time() < close_time


# ====================================================

def _test_create_data(dt_tup=(1970, 1, 2, 8, 1, 2, 3)):
    s1, s2, s3 = ('China Standard Time', 'Asia/Shanghai', 'US/Alaska')
    utc = tz_.tzutc()
    tzloc = tz_.tzlocal()
    tzlocw = tz_.tzwinlocal()
    tz1 = tz_.tzwin('China Standard Time')
    tz2 = tz_.gettz('Asia/Shanghai')
    tz3 = tz_.gettz('US/Alaska')

    dt = datetime(*dt_tup)
    dt1 = datetime(*dt_tup, tzinfo=tz1)
    ar, ar1 = arrow.get(dt), arrow.get(dt1)
    d = dt.date()
    d1 = dt.date()
    t = dt.timetz()
    t1 = dt1.timetz()

    ts = pd.Timestamp(dt)
    ts1 = pd.Timestamp(dt1)
    s = '1970-01-02T00:01:02.000003'
    dt64 = np.datetime64(s)
    p = pd.Period(s).to_timestamp('us', 'S')

    f = 86462.000003
    i = int(86462.000003 * 1000 ** 3)
    st = time.localtime()
    s1 = '1970-01-02T00:01:02.000003'
    s2 = '1970-01-02T00:01:02.000003+0800'
    si = [s1, s2, f, i, st]

    tzstr = [s1, s2, s3]
    tzs = [None, utc, tzloc, tzlocw, tz1, tz2, tz3]

    data0 = [s1, s2, i, f, st]
    data1 = [dt, dt1, d, d1, t, t1, ts, ts1, dt64, p]
    dts = [datetime(*dt_tup, tzinfo=tz) for tz in tzs]
    tss = [pd.Timestamp(dt) for dt in dts]
    vd = {
        'dt': dts, 'ts': tss, 'date': [
            d, d1], 'dttime': [
            t, t1], 'time': [
                i, f, st], 'np_': [
                    dt64, p], 'str': [
                        s1, s2], 'arrow': [ar, ar1]}

    return tzstr, tzs, data0, data1, tzs, dts, tss, vd


def test_str(a=Date):
    values = _test_create_data(dt_tup=(1970, 1, 2, 8, 1, 2, 3))
    tzstr, tzs, data0, data1, tzs, dts, tss, vd = values
    tzs1, tzs2, tzs3 = tzstr
    utc, tzloc, tzlocw, tz1, tz2, tz3 = tzs[1:]
    s1, s2, i, f, st = data0
    dt, dt1, d, d1, t, t1, ts, ts1, dt64, p = data1

    def test_iso():
        for k, values in vd.items():
            print('\n test iso_str (%s) start...' % k)
            for vt in values:
                print(' (%s) iso_str=' % k, a.isostr(vt))

    def test_f_timestamp():
        for k, values in vd.items():
            print('\n test f_timestamp (%s) start...' % k)
            for vt in values:
                print('(%s) f_timestamp=' % k, a.f_timestamp(vt))

    def test_to_str():
        for k, values in vd.items():
            print('\n test  to_str (%s) start...' % k)
            for vt in values:
                print('(%s) to_str=' % k, a.to_str(vt))

        for k, values in vd.items():
            print('\n test to_str(%s) start...' % k)
            for vt in values:
                print('(%s) to_str=' % k, a.to_str(vt, fmt=''))

    test_iso()
    test_f_timestamp()
    test_to_str()


def test_gen_ts(a=Date):
    values = _test_create_data(dt_tup=(1970, 1, 2, 8, 1, 2, 3))
    tzstr, tzs, data0, data1, tzs, dts, tss, vd = values
    tzs1, tzs2, tzs3 = tzstr
    utc, tzloc, tzlocw, tz1, tz2, tz3 = tzs[1:]
    s1, s2, i, f, st = data0
    dt, dt1, d, d1, t, t1, ts, ts1, dt64, p = data1

    def test1():
        for k, values in vd.items():
            print('\n test gen_ts(%s) start...' % k)
            for vt in values:
                print('(%s) gen_ts=' % k, a.gen_ts(vt))

    def test_other(func, *args, **kwargs):
        print(func(*args, **kwargs))

    def test2():
        test_other(a.gen_ts, s1)
        test_other(a.gen_ts, s2)
        test_other(a.gen_ts, s2, tz=None)
        test_other(a.gen_ts, s2, tz=tz1)
        test_other(a.gen_ts, s2, tz=tz2)
        test_other(a.gen_ts, s2, tz=tz3)

    def test3():
        test_other(a.gen_ts, dt)
        test_other(a.gen_ts, dt1)
        test_other(a.gen_ts, dt1, tz=None)
        test_other(a.gen_ts, dt1, tz=tz1)
        test_other(a.gen_ts, dt1, tz=tz2)
        test_other(a.gen_ts, dt1, tz=tz3)

    def test4():
        test_other(a.gen_ts, d)
        test_other(a.gen_ts, d1)
        test_other(a.gen_ts, d1, tz=None)
        test_other(a.gen_ts, d1, tz=tz1)
        test_other(a.gen_ts, d1, tz=tz2)
        test_other(a.gen_ts, d1, tz=tz3)

    def test5():
        test_other(a.gen_ts, t)
        test_other(a.gen_ts, t1)
        test_other(a.gen_ts, t1, tz=None)
        test_other(a.gen_ts, t1, tz=tz1)
        test_other(a.gen_ts, t1, tz=tz2)
        test_other(a.gen_ts, t1, tz=tz3)

    def test6():
        test_other(a.gen_ts, t)
        test_other(a.gen_ts, t1)
        test_other(a.gen_ts, dt, tz=None)
        test_other(a.gen_ts, dt1, tz=tz1)
        test_other(a.gen_ts, d, tz=tz2)
        test_other(a.gen_ts, d1, tz=tz3)

    def test7():
        test_other(a.gen_ts, f)
        test_other(a.gen_ts, i)
        test_other(a.gen_ts, f, unit='s', tz=None)
        test_other(a.gen_ts, i, unit='ns', tz=tz1)
        test_other(a.gen_ts, st, tz=tz2)

        test_other(a.gen_ts, dt64)

    def test12():
        test_other(a.gen_dt, s1)
        test_other(a.gen_dt, s2)
        test_other(a.gen_dt, s2, tz=None)
        test_other(a.gen_dt, s2, tz=tz1)
        test_other(a.gen_dt, s2, tz=tz2)
        test_other(a.gen_dt, s2, tz=tz3)

    def test14():
        test_other(a.gen_dt, d)
        test_other(a.gen_dt, d1)
        test_other(a.gen_dt, d1, tz=None)
        test_other(a.gen_dt, d1, tz=tz1)
        test_other(a.gen_dt, d1, tz=tz2)
        test_other(a.gen_dt, d1, tz=tz3)

    def test15():
        test_other(a.gen_dt, t)
        test_other(a.gen_dt, t1)
        test_other(a.gen_dt, t1, tz=None)
        test_other(a.gen_dt, t1, tz=tz1)
        test_other(a.gen_dt, t1, tz=tz2)
        test_other(a.gen_dt, t1, tz=tz3)

    def test16():
        test_other(a.gen_dt, t)
        test_other(a.gen_dt, t1)
        test_other(a.gen_dt, dt, tz=None)
        test_other(a.gen_dt, dt1, tz=tz1)
        test_other(a.gen_dt, d, tz=tz2)
        test_other(a.gen_dt, d1, tz=tz3)

    def test17():
        test_other(a.gen_dt, dt64)
        test_other(a.gen_dt, ts1)

    test1()
    test2()
    test3()
    test4()
    test5()
    test6()
    test7()

    test12()
    test14()
    test15()
    test16()
    test17()
    print(
        'gen_ts(**kwargs)=',
        a.gen_ts(
            year=2022,
            month=9,
            day=20,
            hour=16,
            minute=58))


def test_gen_dt(a=Date):
    values = _test_create_data(dt_tup=(1970, 1, 2, 8, 1, 2, 3))
    tzstr, tzs, data0, data1, tzs, dts, tss, vd = values
    tzs1, tzs2, tzs3 = tzstr
    utc, tzloc, tzlocw, tz1, tz2, tz3 = tzs[1:]
    s1, s2, i, f, st = data0
    dt, dt1, d, d1, t, t1, ts, ts1, dt64, p = data1

    def test1():
        for k, values in vd.items():
            print('\n test gen_dt(%s) start...' % k)
            for vt in values:
                print('(%s) gen_dt=' % k, a.gen_dt(vt))

    def test_other(func, *args, **kwargs):
        print(func(*args, **kwargs))

    def test2():
        test_other(a.gen_dt, s1)
        test_other(a.gen_dt, s2)
        test_other(a.gen_dt, s2, tz=None)
        test_other(a.gen_dt, s2, tz=tz1)
        test_other(a.gen_dt, s2, tz=tz2)
        test_other(a.gen_dt, s2, tz=tz3)

    def test3():
        test_other(a.gen_dt, dt)
        test_other(a.gen_dt, dt1)
        test_other(a.gen_dt, dt1, tz=None)
        test_other(a.gen_dt, dt1, tz=tz1)
        test_other(a.gen_dt, dt1, tz=tz2)
        test_other(a.gen_dt, dt1, tz=tz3)

    def test4():
        test_other(a.gen_dt, d)
        test_other(a.gen_dt, d1)
        test_other(a.gen_dt, d1, tz=None)
        test_other(a.gen_dt, d1, tz=tz1)
        test_other(a.gen_dt, d1, tz=tz2)
        test_other(a.gen_dt, d1, tz=tz3)

    def test5():
        test_other(a.gen_dt, t)
        test_other(a.gen_dt, t1)
        test_other(a.gen_dt, t1, tz=None)
        test_other(a.gen_dt, t1, tz=tz1)
        test_other(a.gen_dt, t1, tz=tz2)
        test_other(a.gen_dt, t1, tz=tz3)

    def test6():
        test_other(a.gen_dt, t)
        test_other(a.gen_dt, t1)
        test_other(a.gen_dt, dt, tz=None)
        test_other(a.gen_dt, dt1, tz=tz1)
        test_other(a.gen_dt, d, tz=tz2)
        test_other(a.gen_dt, d1, tz=tz3)

    def test7():
        test_other(a.gen_dt, f)
        test_other(a.gen_dt, i)
        test_other(a.gen_dt, f, unit='s', tz=None)
        test_other(a.gen_dt, i, unit='ns', tz=tz1)
        test_other(a.gen_dt, st, tz=tz2)

        test_other(a.gen_dt64, dt64)

    def test12():
        test_other(a.gen_dt, s1)
        test_other(a.gen_dt, s2)
        test_other(a.gen_dt, s2, tz=None)
        test_other(a.gen_dt, s2, tz=tz1)
        test_other(a.gen_dt, s2, tz=tz2)
        test_other(a.gen_dt, s2, tz=tz3)

    def test14():
        test_other(a.gen_dt, d)
        test_other(a.gen_dt, d1)
        test_other(a.gen_dt, d1, tz=None)
        test_other(a.gen_dt, d1, tz=tz1)
        test_other(a.gen_dt, d1, tz=tz2)
        test_other(a.gen_dt, d1, tz=tz3)

    def test15():
        test_other(a.gen_dt, t)
        test_other(a.gen_dt, t1)
        test_other(a.gen_dt, t1, tz=None)
        test_other(a.gen_dt, t1, tz=tz1)
        test_other(a.gen_dt, t1, tz=tz2)
        test_other(a.gen_dt, t1, tz=tz3)

    def test16():
        test_other(a.gen_dt, t)
        test_other(a.gen_dt, t1)
        test_other(a.gen_dt, dt, tz=None)
        test_other(a.gen_dt, dt1, tz=tz1)
        test_other(a.gen_dt, d, tz=tz2)
        test_other(a.gen_dt, d1, tz=tz3)

    def test17():
        test_other(a.gen_dt64, dt64)
        test_other(a.gen_dt, dt1)

    test1()
    test2()
    test3()
    test4()
    test5()
    test6()
    test7()

    test12()
    test14()
    test15()
    test16()
    test17()
    print(
        'gen_dt(**kwargs)=',
        a.gen_ts(
            year=2022,
            month=9,
            day=20,
            hour=16,
            minute=58))


def test_gen_dt64(a=Date):
    values = _test_create_data(dt_tup=(1970, 1, 2, 8, 1, 2, 3))
    tzstr, tzs, data0, data1, tzs, dts, tss, vd = values
    tzs1, tzs2, tzs3 = tzstr
    utc, tzloc, tzlocw, tz1, tz2, tz3 = tzs[1:]
    s1, s2, i, f, st = data0
    dt, dt1, d, d1, t, t1, ts, ts1, dt64, p = data1

    def test1():
        for k, values in vd.items():
            print('\n test gen_dt64(%s) start...' % k)
            for vt in values:
                print('(%s) gen_dt64=' % k, a.gen_dt64(vt))

    test1()


def test_gen_period(a=Date):
    values = _test_create_data(dt_tup=(1970, 1, 2, 8, 1, 2, 3))
    tzstr, tzs, data0, data1, tzs, dts, tss, vd = values
    tzs1, tzs2, tzs3 = tzstr
    utc, tzloc, tzlocw, tz1, tz2, tz3 = tzs[1:]
    s1, s2, i, f, st = data0
    dt, dt1, d, d1, t, t1, ts, ts1, dt64, p = data1

    print('\n test gen_period  start...')

    def test1():
        for k, values in vd.items():
            print('\n gen_period(%s) start...' % k)
            for vt in values:
                print('(%s) gen_period=' % k, a.gen_period(vt))

    test1()
    print(
        'gen_period(**kwargs)=',
        a.gen_period(
            year=2022,
            month=9,
            day=20,
            hour=16,
            minute=58,
            freq='s'))


def test_gen_structtime(a=Date):
    print()
    print('gen_structtime=', a.gen_structtime())
    print('gen_structtime=', a.gen_structtime(islocal=True))
    print('gen_structtime=', a.gen_structtime(time.time(), islocal=True))
    # print('gen_structtime=', a.gen_structtime(time.time_ns(), islocal=True))
    # err


def test_convert(a=Date):
    print('\nstart test convert...')
    tup = time.localtime()
    f1, f2 = time.time(), time.time_ns()
    tzsh = tz_.gettz('Asia/Shanghai')
    print('timetuple_to_f=', a.timetuple_to_f(tup))
    print('f_to_timetuple=', a.f_to_timetuple(f1, is_utc=False))
    print('f_to_timetuple=', a.f_to_timetuple(f2, is_utc=True))

    print()
    print('f_timestamp_to_dt=', a.f_timestamp_to_dt(f1))
    print('f_timestamp_to_dt=', a.f_timestamp_to_dt(f2))
    print('f_timestamp_to_dt=', a.f_timestamp_to_dt(f2, tz=tzsh))
    print('f_timestamp_to_dt=', a.f_timestamp_to_dt(f2, tz_struct_time=tzsh))

    print(
        'str_to_timetuple=',
        a.str_to_timetuple(
            '2020-03-14 15:32:52',
            fmt=None))


def test_arrow_get(a=Date):
    # Date.arrow_from_str('2022-01-05 15:58:59')
    # # <Arrow [2022-01-05T15:58:59+00:00]>
    #
    # Date.arrow_from_str('2022-01-5 15:58:59', 'YYYY-MM-D HH:mm:ss')
    # # <Arrow [2022-01-05T15:58:59+00:00]>
    #
    # fmt = Date.m_fmt['arrow_no_seperator']
    # Date.arrow_from_str('20220105155859', fmt)
    # # <Arrow [2022-01-05T15:58:59+00:00]>
    print('\n test arrow_get start...')

    tup = time.localtime()
    f1, f2 = time.time(), time.time_ns()
    tzus = tz_.gettz('US/Central')
    tzsh = tz_.gettz('Asia/Shanghai')
    dt = datetime.now()
    dt1 = datetime.now(tzsh)
    dt2 = datetime.now(tzus)
    d = dt.date()
    d1 = dt1.date()
    d2 = dt2.date()
    dtts = dt.timetz()
    dtts1 = dt1.date()
    dtts2 = dt2.date()
    ts = pd.Timestamp.now()
    ts1 = pd.Timestamp.now(tzsh)
    ts2 = pd.Timestamp.now(tzus)

    ar = arrow.get(dt)
    ar1 = arrow.get(dt1)
    ar1 = arrow.get(dt2)

    lst = [f1, f2, tup, dt, dt1, dt2, d, d1, d2, dtts,
           dtts1, dtts2, ts, ts1, ts2, (2022, 18, 7)]

    for v in lst:
        print()
        print('ar1=', a.arrow_get(v, tz=None), type(v))
        print('ar2=', a.arrow_get(v, tz=tzsh), type(v))
        print('ar3=', a.arrow_get(v, tz=tzus), type(v))

    print()

    lst2 = [
        '2013-09-29T01:26:43.830580', '20160413T133656.456289',
        '2013-05-05 12:30:45 America/Chicago', '2013-05-05 12:30:45'
    ]
    for v in lst2:
        try:
            print()
            print('ar1=', a.arrow_get(v, tz=None), type(v))
            print('ar2=', a.arrow_get(v, tz=tzsh), type(v))
            print('ar3=', a.arrow_get(v, tz=tzus), type(v))
        except BaseException:
            print('err:s = ', v)

    print()
    print(
        'arrow_get(**kwargs)=',
        a.arrow_get(
            year=2022,
            month=9,
            day=20,
            hour=16,
            minute=58))

    '''

        #2个参数:both 'str',根据第二个节点的格式来解析第一个节点:
        arrow.get('2013-05-05 12:30:45 America/Chicago', 'YYYY-MM-DD HH:mm:ss ZZZ')
        #<Arrow [2013-05-05T12:30:45-05:00]>

        #2个参数:first a 'str' to parse and second a 'list' of formats to try::
        arrow.get('2013-05-05 12:30:45', ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss'])
        #<Arrow [2013-05-05T12:30:45+00:00]>

        #Three or more** arguments, as for the direct constructor of an 'Arrow' object::
        arrow.get(2013, 5, 5, 12, 30, 45)#<Arrow [2013-05-05T12:30:45+00:00]>


    '''


def test_parse_dt(a=Date):
    tzus = tz_.gettz('US/Central')
    tzsh = tz_.gettz('Asia/Shanghai')
    print('\ntest parse_dt start...')

    lst1 = [
        '2017-08-28 06:08:20-06:00',
        '2022-10-21',
        '20221021',
        '2022/10/21',
        '10-21',
        '10/21',
        '3/8',
        '3-8',
        '3/8/2022',
        '2022/3/8',
        '8/3',
        '8-3',
        '8/3/2022',
        '3/15/2022',
        '15/3/2022',
        'Mar 15',
        'Mar15',
        '15 Mar',
        'Mar 15 2022',
        '2022 Mar15',
        '15,3',
        '3,15,2022',
        '2003 10:20:30 Thu Sep 25',
        '2020-09-09T10:20:20.5-8:00',
        '2020-09-09T10:20',
        '2020-09-09T10',
        '2020-09-09',
        '20200909T102020-8:00',
        '20200909T102020',
        '20200909T1020',
        '20200909T1020',
        '20200909',
        '20200909102020',
        '10-09-2020',
        '10-09-2020',
        '10-09-20',
        '10-09-20',
        'today is 25 of Oct 2020 exactly at 10:20:20 with timezone +08:00',
        '今天是2020年10月20日 10点23分49秒',
        '今天是2020年10月20日,明天有个预约']

    lst2 = ['2017-08-28 06:08:20 CDT',
            'Thu Sep 25 10:20:30 BRST 2003',
            '2003 Thu Sep 25 10:20:30 BRST',
            '2003 10:20:30 Thu Sep 25 BRST',
            'Thu Sep 25 10:20:30 BRST 2003', ]

    def test1():
        for s in lst1:
            try:
                dt = a.parse_dt(s, tz=None)
                dt1 = a.parse_dt(s, tz=tzsh)
                dt2 = a.parse_dt(s, tz=tzus)
                print('s  = %s' % s)
                print('dt1=%s, tz=%s' % (dt, dt.tzinfo))
                print('dt2=%s, tz=%s' % (dt1, dt1.tzinfo))
                print('dt3=%s, tz=%s' % (dt2, dt2.tzinfo))
                print()
            except Exception:
                print('err: tz = ', ',s=', s)

                dt = a.parse_dt(s, tz=None, fuzzy=True)
                dt1 = a.parse_dt(s, tz=tzsh, fuzzy=True)
                dt2 = a.parse_dt(s, tz=tzus, fuzzy=True)
                print('dt1=%s, tz=%s' % (dt, dt.tzinfo))
                print('dt2=%s, tz=%s' % (dt1, dt1.tzinfo))
                print('dt3=%s, tz=%s' % (dt2, dt2.tzinfo))
                print()

    def test2():
        for s in lst2:
            try:
                dt = a.parse_dt(s, tz=None)
                dt1 = a.parse_dt(s, tz=tzsh)
                dt2 = a.parse_dt(s, tz=tzus)
                print('s  = %s' % s)
                print('dt1=%s, tz=%s' % (dt, dt.tzinfo))
                print('dt2=%s, tz=%s' % (dt1, dt1.tzinfo))
                print('dt3=%s, tz=%s' % (dt2, dt2.tzinfo))
                print()
            except Exception:
                print('err: tz = ', ',s=', s)

                dt = a.parse_dt(s, tz=None, fuzzy=True)
                dt1 = a.parse_dt(s, tz=tzsh, fuzzy=True)
                dt2 = a.parse_dt(s, tz=tzus, fuzzy=True)
                print('dt1=%s, tz=%s' % (dt, dt.tzinfo))
                print('dt2=%s, tz=%s' % (dt1, dt1.tzinfo))
                print('dt3=%s, tz=%s' % (dt2, dt2.tzinfo))
                print()

        # print(get_tznames())

    test1()
    test2()


def test_other(a=Date):

    values = _test_create_data(dt_tup=(1970, 1, 2, 8, 1, 2, 3))
    tzstr, tzs, data0, data1, tzs, dts, tss, vd = values
    tzs1, tzs2, tzs3 = tzstr
    utc, tzloc, tzlocw, tz1, tz2, tz3 = tzs[1:]
    s1, s2, i, f, st = data0
    dt, dt1, d, d1, t, t1, ts, ts1, dt64, p = data1

    def test_now():
        print('\n test now start...')
        print(a.now())
        print(a.now(tz1))
        print(a.now(tz1, fmt=''))
        print(a.today())
        print(a.today(fmt=''))

    def test_join():
        print('\n test join start...')
        print(a.join(dt.date(), dt.time()))
        print(a.join(dt.date(), dt.time(), True))

        print(a.join(dt1.date(), '22:30:59.123456', True))
        print(a.join('2022-09-15', dt1.timetz(), True))
        print(a.join(dt1.date(), dt1.timetz(), True))
        print(a.join('2022-9-15', '22:30:59.123456', True))

    """
    def get_delta(cls, **kwargs) -> relativedelta:
    def sub(cls, dt, delta=None, **kwargs):
    def sub_month(cls, dt, delta=None, monthend=None, **kwargs):
    def replace()
    """

    def test_file_time():
        file = r'C:\Users\Administrator\Desktop\m2205.csv'
        assert(a.is_update_file_time(file))

        d = {
            'date': [
                '2022-7-11 13:15:35',
                '2022-7-12 14:45:58'],
            'value': [
                1,
                2]}
        df = pd.DataFrame(d)
        dt1 = datetime(2022, 7, 12, 14, 45, 58)
        dt2 = datetime(2022, 7, 13, 14, 45, 58)
        assert (a.get_df_tail_time(df, 'date') == dt1)
        assert (a.get_df_tail_next_bustime(df, 'date') == dt2.isoformat())

    test_now()
    test_join()
    test_file_time()


# 节假日函数
# Date.busday_offset = busday_offset  # 查找距离{delta_days} 天后的日期
# Date.ge_busday = ge_busday  # 取当前或后一个工作日(> =dt)
# Date.ge_busday_today = ge_busday_today  # 取当前或后一工作日(>=dt)
# Date.get_workday_range = get_workday_range  # 取开始结束日期间工作日
# Date.gt_busday = gt_busday  # 取后一工作日(> dt)
# Date.gt_busday_today = gt_busday_today  # 取后一工作日
# Date.is_holiday = is_holiday  # 是否是节假日
# Date.is_workday = is_workday  # 是否是工作日
# Date.isleap = isleap  # 是闰年
# Date.le_busday = le_busday  # 取当前或前一工作日(<= dt)
# Date.le_busday_today = le_busday_today  # 取当前或前一工作日(<=dt)
# Date.leap_n = leap_n  # 闰年总数
# Date.lt_busday = lt_busday  # 取前一工作日(< dt)
# Date.lt_busday_today = lt_busday_today  # 取前一工作日
# Date.month_days = month_days  # 一个月有多少天
# Date.nearest_busday = nearest_busday  # 取当前工作日-距离dt日期最近工作日
# Date.nearest_busday_today = nearest_busday_today  # 取距离今天最近工作日
# Date.next_busday = next_busday  # 取后一个工作日(> dt)
# Date.pre_busday = pre_busday  # 取前一工作日(< dt)
# Date.week_day = week_day  # 返回给定日期的周一到日 0-6


# 时区函数
Date.exists_datetime = exists_datetime  # 检查日期时间是否存在
Date.get_naive = get_naive  # 返回无时区且原值保持不变日期(或有时区)
Date.get_tz = get_tz  # 获取时区对象
Date.get_tz_offset = get_tz_offset  # 获取时区偏移量
Date.get_win_localzone = get_win_localzone  # 获取win本地时区
Date.get_win_timeones = get_win_timeones  # 获取窗口系统全部时区名称
Date.is_ambiguous = is_ambiguous  # 检查日期时间是否不明确
Date.is_aware = is_aware
Date.is_dst = is_dst  # 是否是夏令时
Date.is_naive = is_naive
Date.is_tz = is_tz  # 是否是时区对象
Date.is_tz_dateutil = is_tz_dateutil  # 是否是dateutil时区
Date.is_tz_pytz = is_tz_pytz
Date.is_tz_tzlocal = is_tz_tzlocal  # 是否是tzlocal.PytzShimTimezone时区
Date.is_tz_zoneinfo = is_tz_zoneinfo  # 是否是zoneinfo.ZoneInfo时区
Date.m_win_tz = m_win_tz
Date.m_win_tzname = m_win_tzname
Date.M_WIN_TZNAMES = M_WIN_TZNAMES
Date.tz_as = tz_as  # 转换为新时区的本地时间
Date.tz_del = tz_del  # 删除时区
Date.tz_dst = tz_dst  # 获取夏令时偏移
Date.tz_fromutc = tz_fromutc  # 将dt的时间作为UTC时间转为本地时间
Date.tz_name = tz_name  # 获取时区名称
Date.tz_str = tz_str
Date.tz_toutc = tz_toutc  # 将dt,ts转为utc格式日期
Date.tz_transitions = tz_transitions  # 获取夏令时的开始结束时间或None
Date.tz_upgrade_tzinfo = tz_upgrade_tzinfo  # 将pytz时区提升为zoneinfo时区
Date.tz_utcoffset = tz_utcoffset  # 显示与UTC的偏移量
Date.tzwin_normalize = tzwin_normalize


if __name__ == '__main__':
    test_str()
    test_gen_ts()
    test_gen_dt()
    test_gen_dt64()
    test_gen_period()
    test_gen_structtime()
    test_convert()
    test_other()
    test_parse_dt()
    test_arrow_get()

    """

     test iso_str (dt) start...
     (dt) iso_str= 1970-01-02T08:01:02.000003
     (dt) iso_str= 1970-01-02T08:01:02.000003+00:00
     (dt) iso_str= 1970-01-02T08:01:02.000003+08:00
     (dt) iso_str= 1970-01-02T08:01:02.000003+08:00
     (dt) iso_str= 1970-01-02T08:01:02.000003+08:00
     (dt) iso_str= 1970-01-02T08:01:02.000003+08:00
     (dt) iso_str= 1970-01-02T08:01:02.000003-10:00

     test iso_str (ts) start...
     (ts) iso_str= 1970-01-02T08:01:02.000003
     (ts) iso_str= 1970-01-02T08:01:02.000003+00:00
     (ts) iso_str= 1970-01-02T08:01:02.000003+08:00
     (ts) iso_str= 1970-01-02T08:01:02.000003+08:00
     (ts) iso_str= 1970-01-02T08:01:02.000003+08:00
     (ts) iso_str= 1970-01-02T08:01:02.000003+08:00
     (ts) iso_str= 1970-01-02T08:01:02.000003-10:00

     test iso_str (date) start...
     (date) iso_str= 1970-01-02
     (date) iso_str= 1970-01-02

     test iso_str (dttime) start...
     (dttime) iso_str= 08:01:02.000003
     (dttime) iso_str= 08:01:02.000003+08:00

     test iso_str (time) start...
     (time) iso_str= 1970-01-02T00:01:02.000003
     (time) iso_str= 1970-01-02T00:01:02.000003
     (time) iso_str= 2022-09-20T20:06:06+08:00

     test iso_str (np_) start...
     (np_) iso_str= 1970-01-02T00:01:02.000003
     (np_) iso_str= 1970-01-02T00:01:02.000003

     test iso_str (str) start...
     (str) iso_str= 1970-01-02T00:01:02.000003
     (str) iso_str= 1970-01-02T00:01:02.000003+08:00

     test iso_str (arrow) start...
     (arrow) iso_str= 1970-01-02T08:01:02.000003+00:00
     (arrow) iso_str= 1970-01-02T08:01:02.000003+08:00

     test f_timestamp (dt) start...
    (dt) f_timestamp= 86462.000003
    (dt) f_timestamp= 115262.000003
    (dt) f_timestamp= 86462.000003
    (dt) f_timestamp= 86462.000003
    (dt) f_timestamp= 86462.000003
    (dt) f_timestamp= 86462.000003
    (dt) f_timestamp= 151262.000003

     test f_timestamp (ts) start...
    (ts) f_timestamp= 115262.000003
    (ts) f_timestamp= 115262.000003
    (ts) f_timestamp= 86462.000003
    (ts) f_timestamp= 86462.000003
    (ts) f_timestamp= 86462.000003
    (ts) f_timestamp= 86462.000003
    (ts) f_timestamp= 151262.000003

     test f_timestamp (date) start...
    (date) f_timestamp= 86400.0
    (date) f_timestamp= 86400.0

     test f_timestamp (dttime) start...
    (dttime) f_timestamp= 1663660862.000003
    (dttime) f_timestamp= 1663632062.000003

     test f_timestamp (time) start...
    (time) f_timestamp= 86462000003000
    (time) f_timestamp= 86462.000003
    (time) f_timestamp= 1663675566.0

     test f_timestamp (np_) start...
    (np_) f_timestamp= 86462.000003
    (np_) f_timestamp= 86462.000003

     test f_timestamp (str) start...
    (str) f_timestamp= 86462.000003
    (str) f_timestamp= 57662.000003

     test f_timestamp (arrow) start...
    (arrow) f_timestamp= 115262.000003
    (arrow) f_timestamp= 86462.000003

     test  to_str (dt) start...
    (dt) to_str= 1970-01-02T08:01:02.000003
    (dt) to_str= 1970-01-02T08:01:02.000003+00:00
    (dt) to_str= 1970-01-02T08:01:02.000003+08:00
    (dt) to_str= 1970-01-02T08:01:02.000003+08:00
    (dt) to_str= 1970-01-02T08:01:02.000003+08:00
    (dt) to_str= 1970-01-02T08:01:02.000003+08:00
    (dt) to_str= 1970-01-02T08:01:02.000003-10:00

     test  to_str (ts) start...
    (ts) to_str= 1970-01-02T08:01:02.000003
    (ts) to_str= 1970-01-02T08:01:02.000003+00:00
    (ts) to_str= 1970-01-02T08:01:02.000003+08:00
    (ts) to_str= 1970-01-02T08:01:02.000003+08:00
    (ts) to_str= 1970-01-02T08:01:02.000003+08:00
    (ts) to_str= 1970-01-02T08:01:02.000003+08:00
    (ts) to_str= 1970-01-02T08:01:02.000003-10:00

     test  to_str (date) start...
    (date) to_str= 1970-01-02
    (date) to_str= 1970-01-02

     test  to_str (dttime) start...
    (dttime) to_str= 08:01:02.000003
    (dttime) to_str= 08:01:02.000003+08:00

     test  to_str (time) start...
    (time) to_str= 1970-01-02T00:01:02.000003
    (time) to_str= 1970-01-02T00:01:02.000003
    (time) to_str= 2022-09-20T20:06:06+08:00

     test  to_str (np_) start...
    (np_) to_str= 1970-01-02T00:01:02.000003
    (np_) to_str= 1970-01-02T00:01:02.000003

     test  to_str (str) start...
    (str) to_str= 1970-01-02T00:01:02.000003
    (str) to_str= 1970-01-02T00:01:02.000003+08:00

     test  to_str (arrow) start...
    (arrow) to_str= 1970-01-02T08:01:02.000003+00:00
    (arrow) to_str= 1970-01-02T08:01:02.000003+08:00

     test to_str(dt) start...
    (dt) to_str= 1970-01-02 08:01:02.000003
    (dt) to_str= 1970-01-02 08:01:02.000003
    (dt) to_str= 1970-01-02 08:01:02.000003
    (dt) to_str= 1970-01-02 08:01:02.000003
    (dt) to_str= 1970-01-02 08:01:02.000003
    (dt) to_str= 1970-01-02 08:01:02.000003
    (dt) to_str= 1970-01-02 08:01:02.000003

     test to_str(ts) start...
    (ts) to_str= 1970-01-02 08:01:02.000003
    (ts) to_str= 1970-01-02 08:01:02.000003
    (ts) to_str= 1970-01-02 08:01:02.000003
    (ts) to_str= 1970-01-02 08:01:02.000003
    (ts) to_str= 1970-01-02 08:01:02.000003
    (ts) to_str= 1970-01-02 08:01:02.000003
    (ts) to_str= 1970-01-02 08:01:02.000003

     test to_str(date) start...
    (date) to_str= 1970-01-02
    (date) to_str= 1970-01-02

     test to_str(dttime) start...
    (dttime) to_str= 08:01:02.000003
    (dttime) to_str= 08:01:02.000003

     test to_str(time) start...
    (time) to_str= 1970-01-02 00:01:02.000003
    (time) to_str= 1970-01-02 00:01:02.000003
    (time) to_str= 2022-09-20 20:06:06

     test to_str(np_) start...
    (np_) to_str= 1970-01-02 00:01:02.000003
    (np_) to_str= 1970-01-02 00:01:02.000003

     test to_str(str) start...
    (str) to_str= 1970-01-02 00:01:02.000003
    (str) to_str= 1970-01-02 00:01:02.000003

     test to_str(arrow) start...
    (arrow) to_str= 1970-01-02 08:01:02.000003
    (arrow) to_str= 1970-01-02 08:01:02.000003

     test gen_ts(dt) start...
    (dt) gen_ts= 1970-01-02 08:01:02.000003
    (dt) gen_ts= 1970-01-02 08:01:02.000003+00:00
    (dt) gen_ts= 1970-01-02 08:01:02.000003+08:00
    (dt) gen_ts= 1970-01-02 08:01:02.000003+08:00
    (dt) gen_ts= 1970-01-02 08:01:02.000003+08:00
    (dt) gen_ts= 1970-01-02 08:01:02.000003+08:00
    (dt) gen_ts= 1970-01-02 08:01:02.000003-10:00

     test gen_ts(ts) start...
    (ts) gen_ts= 1970-01-02 08:01:02.000003
    (ts) gen_ts= 1970-01-02 08:01:02.000003+00:00
    (ts) gen_ts= 1970-01-02 08:01:02.000003+08:00
    (ts) gen_ts= 1970-01-02 08:01:02.000003+08:00
    (ts) gen_ts= 1970-01-02 08:01:02.000003+08:00
    (ts) gen_ts= 1970-01-02 08:01:02.000003+08:00
    (ts) gen_ts= 1970-01-02 08:01:02.000003-10:00

     test gen_ts(date) start...
    (date) gen_ts= 1970-01-02 00:00:00
    (date) gen_ts= 1970-01-02 00:00:00

     test gen_ts(dttime) start...
    (dttime) gen_ts= 2022-09-20 08:01:02.000003
    (dttime) gen_ts= 2022-09-20 08:01:02.000003+08:00

     test gen_ts(time) start...
    (time) gen_ts= 1970-01-02 00:01:02.000003
    (time) gen_ts= 1970-01-02 00:01:02.000003
    (time) gen_ts= 2022-09-20 20:06:06+08:00

     test gen_ts(np_) start...
    (np_) gen_ts= 1970-01-02 00:01:02.000003
    (np_) gen_ts= 1970-01-02 00:01:02.000003

     test gen_ts(str) start...
    (str) gen_ts= 1970-01-02 00:01:02.000003
    (str) gen_ts= 1970-01-02 00:01:02.000003+08:00

     test gen_ts(arrow) start...
    (arrow) gen_ts= 1970-01-02 08:01:02.000003+00:00
    (arrow) gen_ts= 1970-01-02 08:01:02.000003+08:00
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-01 06:01:02.000003-10:00
    1970-01-02 08:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003-10:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00-10:00
    2022-09-20 08:01:02.000003
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003-08:00
    2022-09-20 08:01:02.000003
    2022-09-20 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00-10:00
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    2022-09-20 20:06:06+08:00
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-01 06:01:02.000003-10:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00-10:00
    2022-09-20 08:01:02.000003
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003-08:00
    2022-09-20 08:01:02.000003
    2022-09-20 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00-10:00
    1970-01-02 00:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    gen_ts(**kwargs)= 2022-09-20 16:58:00

     test gen_dt(dt) start...
    (dt) gen_dt= 1970-01-02 08:01:02.000003
    (dt) gen_dt= 1970-01-02 08:01:02.000003+00:00
    (dt) gen_dt= 1970-01-02 08:01:02.000003+08:00
    (dt) gen_dt= 1970-01-02 08:01:02.000003+08:00
    (dt) gen_dt= 1970-01-02 08:01:02.000003+08:00
    (dt) gen_dt= 1970-01-02 08:01:02.000003+08:00
    (dt) gen_dt= 1970-01-02 08:01:02.000003-10:00

     test gen_dt(ts) start...
    (ts) gen_dt= 1970-01-02 08:01:02.000003
    (ts) gen_dt= 1970-01-02 08:01:02.000003+00:00
    (ts) gen_dt= 1970-01-02 08:01:02.000003+08:00
    (ts) gen_dt= 1970-01-02 08:01:02.000003+08:00
    (ts) gen_dt= 1970-01-02 08:01:02.000003+08:00
    (ts) gen_dt= 1970-01-02 08:01:02.000003+08:00
    (ts) gen_dt= 1970-01-02 08:01:02.000003-10:00

     test gen_dt(date) start...
    (date) gen_dt= 1970-01-02 00:00:00
    (date) gen_dt= 1970-01-02 00:00:00

     test gen_dt(dttime) start...
    (dttime) gen_dt= 2022-09-20 08:01:02.000003
    (dttime) gen_dt= 2022-09-20 08:01:02.000003+08:00

     test gen_dt(time) start...
    (time) gen_dt= 1970-01-02 00:01:02.000003
    (time) gen_dt= 1970-01-02 00:01:02.000003
    (time) gen_dt= 2022-09-20 20:06:06+08:00

     test gen_dt(np_) start...
    (np_) gen_dt= 1970-01-02 00:01:02.000003
    (np_) gen_dt= 1970-01-02 00:01:02.000003

     test gen_dt(str) start...
    (str) gen_dt= 1970-01-02 00:01:02.000003
    (str) gen_dt= 1970-01-02 00:01:02.000003+08:00

     test gen_dt(arrow) start...
    (arrow) gen_dt= 1970-01-02 08:01:02.000003+00:00
    (arrow) gen_dt= 1970-01-02 08:01:02.000003+08:00
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-01 06:01:02.000003-10:00
    1970-01-02 08:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003-10:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00-10:00
    2022-09-20 08:01:02.000003
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003-08:00
    2022-09-20 08:01:02.000003
    2022-09-20 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00-10:00
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    2022-09-20 20:06:06+08:00
    1970-01-02T00:01:02.000003
    1970-01-02 00:01:02.000003
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-02 00:01:02.000003+08:00
    1970-01-01 06:01:02.000003-10:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00-10:00
    2022-09-20 08:01:02.000003
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003+08:00
    2022-09-20 08:01:02.000003-08:00
    2022-09-20 08:01:02.000003
    2022-09-20 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    1970-01-02 00:00:00+08:00
    1970-01-02 00:00:00-10:00
    1970-01-02T00:01:02.000003
    1970-01-02 08:01:02.000003+08:00
    gen_dt(**kwargs)= 2022-09-20 16:58:00

     test gen_dt64(dt) start...
    (dt) gen_dt64= 1970-01-02T08:01:02.000003000
    (dt) gen_dt64= 1970-01-02T08:01:02.000003000
    (dt) gen_dt64= 1970-01-02T00:01:02.000003000
    (dt) gen_dt64= 1970-01-02T00:01:02.000003000
    (dt) gen_dt64= 1970-01-02T00:01:02.000003000
    (dt) gen_dt64= 1970-01-02T00:01:02.000003000
    (dt) gen_dt64= 1970-01-02T18:01:02.000003000

     test gen_dt64(ts) start...
    (ts) gen_dt64= 1970-01-02T08:01:02.000003000
    (ts) gen_dt64= 1970-01-02T08:01:02.000003000
    (ts) gen_dt64= 1970-01-02T00:01:02.000003000
    (ts) gen_dt64= 1970-01-02T00:01:02.000003000
    (ts) gen_dt64= 1970-01-02T00:01:02.000003000
    (ts) gen_dt64= 1970-01-02T00:01:02.000003000
    (ts) gen_dt64= 1970-01-02T18:01:02.000003000

     test gen_dt64(date) start...
    (date) gen_dt64= 1970-01-02T00:00:00.000000000
    (date) gen_dt64= 1970-01-02T00:00:00.000000000

     test gen_dt64(dttime) start...
    (dttime) gen_dt64= 2022-09-20T08:01:02.000003000
    (dttime) gen_dt64= 2022-09-20T00:01:02.000003000

     test gen_dt64(time) start...
    (time) gen_dt64= 1970-01-02T00:01:02.000003000
    (time) gen_dt64= 1970-01-02T00:01:02.000003000
    (time) gen_dt64= 2022-09-20T12:06:06.000000000

     test gen_dt64(np_) start...
    (np_) gen_dt64= 1970-01-02T00:01:02.000003
    (np_) gen_dt64= 1970-01-02T00:01:02.000003000

     test gen_dt64(str) start...
    (str) gen_dt64= 1970-01-02T00:01:02.000003000
    (str) gen_dt64= 1970-01-01T16:01:02.000003000

     test gen_dt64(arrow) start...
    (arrow) gen_dt64= 1970-01-02T08:01:02.000003000
    (arrow) gen_dt64= 1970-01-02T00:01:02.000003000

     test gen_period  start...

     gen_period(dt) start...
    (dt) gen_period= 1970-01-02 08:01:02.000003
    (dt) gen_period= 1970-01-02 08:01:02.000003
    (dt) gen_period= 1970-01-02 08:01:02.000003
    (dt) gen_period= 1970-01-02 08:01:02.000003
    (dt) gen_period= 1970-01-02 08:01:02.000003
    (dt) gen_period= 1970-01-02 08:01:02.000003
    (dt) gen_period= 1970-01-02 08:01:02.000003

     gen_period(ts) start...
    (ts) gen_period= 1970-01-02 08:01:02.000003000
    (ts) gen_period= 1970-01-02 08:01:02.000003000
    (ts) gen_period= 1970-01-02 08:01:02.000003000
    (ts) gen_period= 1970-01-02 08:01:02.000003000
    (ts) gen_period= 1970-01-02 08:01:02.000003000
    (ts) gen_period= 1970-01-02 08:01:02.000003000
    (ts) gen_period= 1970-01-02 08:01:02.000003000

     gen_period(date) start...
    (date) gen_period= 1970-01-02
    (date) gen_period= 1970-01-02

     gen_period(dttime) start...
    (dttime) gen_period= 2022-09-20 08:01:02.000003
    (dttime) gen_period= 2022-09-20 08:01:02.000003

     gen_period(time) start...
    (time) gen_period= 1970-01-02 00:01:02.000003000
    (time) gen_period= 1970-01-02 00:01:02.000003000
    (time) gen_period= 2022-09-20 20:06:06.000000

     gen_period(np_) start...
    (np_) gen_period= 1970-01-02 00:01:02.000003
    (np_) gen_period= 1970-01-02 00:01:02.000003000

     gen_period(str) start...
    (str) gen_period= 1970-01-02 00:01:02.000003
    (str) gen_period= 1970-01-02 00:01:02.000003

     gen_period(arrow) start...
    (arrow) gen_period= 1970-01-02 08:01:02.000003
    (arrow) gen_period= 1970-01-02 08:01:02.000003
    gen_period(**kwargs)= 2022-09-20 16:58:00

    gen_structtime= time.struct_time(tm_year=2022, tm_mon=9, tm_mday=20, tm_hour=20, tm_min=6, tm_sec=6, tm_wday=1, tm_yday=263, tm_isdst=0)
    gen_structtime= time.struct_time(tm_year=2022, tm_mon=9, tm_mday=20, tm_hour=20, tm_min=6, tm_sec=6, tm_wday=1, tm_yday=263, tm_isdst=0)
    gen_structtime= time.struct_time(tm_year=2022, tm_mon=9, tm_mday=20, tm_hour=20, tm_min=6, tm_sec=6, tm_wday=1, tm_yday=263, tm_isdst=0)

    start test convert...
    timetuple_to_f= 1663675566.0
    f_to_timetuple= time.struct_time(tm_year=2022, tm_mon=9, tm_mday=20, tm_hour=20, tm_min=6, tm_sec=6, tm_wday=1, tm_yday=263, tm_isdst=0)
    f_to_timetuple= time.struct_time(tm_year=2022, tm_mon=9, tm_mday=20, tm_hour=12, tm_min=6, tm_sec=6, tm_wday=1, tm_yday=263, tm_isdst=0)

    f_timestamp_to_dt= 2022-09-20 20:06:06.774414+08:00
    f_timestamp_to_dt= 2022-09-20 20:06:06.774414+08:00
    f_timestamp_to_dt= 2022-09-20 20:06:06.774414+08:00
    f_timestamp_to_dt= 2022-09-20 20:06:06.774414+08:00
    str_to_timetuple= time.struct_time(tm_year=2020, tm_mon=3, tm_mday=14, tm_hour=15, tm_min=32, tm_sec=52, tm_wday=5, tm_yday=74, tm_isdst=-1)

     test now start...
    2022-09-20 20:06:06.785414
    2022-09-20 20:06:06.785414+08:00
    2022-09-20T20:06:06.785414+08:00
    2022-09-20
    2022-09-20

     test join start...
    1970-01-02 08:01:02.000003
    1970-01-02 08:01:02.000003
    1970-01-02 22:30:59.123456
    2022-09-15 08:01:02.000003+08:00
    1970-01-02 08:01:02.000003+08:00
    2022-09-15 22:30:59.123456

    test parse_dt start...
    s  = 2017-08-28 06:08:20-06:00
    dt1=2017-08-28 06:08:20-06:00, tz=tzoffset(None, -21600)
    dt2=2017-08-28 06:08:20-06:00, tz=tzoffset(None, -21600)
    dt3=2017-08-28 06:08:20-06:00, tz=tzoffset(None, -21600)

    s  = 2022-10-21
    dt1=2022-10-21 00:00:00, tz=None
    dt2=2022-10-21 00:00:00, tz=None
    dt3=2022-10-21 00:00:00, tz=None

    s  = 20221021
    dt1=2022-10-21 00:00:00, tz=None
    dt2=2022-10-21 00:00:00, tz=None
    dt3=2022-10-21 00:00:00, tz=None

    s  = 2022/10/21
    dt1=2022-10-21 00:00:00, tz=None
    dt2=2022-10-21 00:00:00, tz=None
    dt3=2022-10-21 00:00:00, tz=None

    s  = 10-21
    dt1=2022-10-21 00:00:00, tz=None
    dt2=2022-10-21 00:00:00, tz=None
    dt3=2022-10-21 00:00:00, tz=None

    s  = 10/21
    dt1=2022-10-21 00:00:00, tz=None
    dt2=2022-10-21 00:00:00, tz=None
    dt3=2022-10-21 00:00:00, tz=None

    s  = 3/8
    dt1=2022-03-08 00:00:00, tz=None
    dt2=2022-03-08 00:00:00, tz=None
    dt3=2022-03-08 00:00:00, tz=None

    s  = 3-8
    dt1=2022-03-08 00:00:00, tz=None
    dt2=2022-03-08 00:00:00, tz=None
    dt3=2022-03-08 00:00:00, tz=None

    s  = 3/8/2022
    dt1=2022-03-08 00:00:00, tz=None
    dt2=2022-03-08 00:00:00, tz=None
    dt3=2022-03-08 00:00:00, tz=None

    s  = 2022/3/8
    dt1=2022-03-08 00:00:00, tz=None
    dt2=2022-03-08 00:00:00, tz=None
    dt3=2022-03-08 00:00:00, tz=None

    s  = 8/3
    dt1=2022-08-03 00:00:00, tz=None
    dt2=2022-08-03 00:00:00, tz=None
    dt3=2022-08-03 00:00:00, tz=None

    s  = 8-3
    dt1=2022-08-03 00:00:00, tz=None
    dt2=2022-08-03 00:00:00, tz=None
    dt3=2022-08-03 00:00:00, tz=None

    s  = 8/3/2022
    dt1=2022-08-03 00:00:00, tz=None
    dt2=2022-08-03 00:00:00, tz=None
    dt3=2022-08-03 00:00:00, tz=None

    s  = 3/15/2022
    dt1=2022-03-15 00:00:00, tz=None
    dt2=2022-03-15 00:00:00, tz=None
    dt3=2022-03-15 00:00:00, tz=None

    s  = 15/3/2022
    dt1=2022-03-15 00:00:00, tz=None
    dt2=2022-03-15 00:00:00, tz=None
    dt3=2022-03-15 00:00:00, tz=None

    s  = Mar 15
    dt1=2022-03-15 00:00:00, tz=None
    dt2=2022-03-15 00:00:00, tz=None
    dt3=2022-03-15 00:00:00, tz=None

    s  = Mar15
    dt1=2022-03-15 00:00:00, tz=None
    dt2=2022-03-15 00:00:00, tz=None
    dt3=2022-03-15 00:00:00, tz=None

    s  = 15 Mar
    dt1=2022-03-15 00:00:00, tz=None
    dt2=2022-03-15 00:00:00, tz=None
    dt3=2022-03-15 00:00:00, tz=None

    s  = Mar 15 2022
    dt1=2022-03-15 00:00:00, tz=None
    dt2=2022-03-15 00:00:00, tz=None
    dt3=2022-03-15 00:00:00, tz=None

    s  = 2022 Mar15
    dt1=2022-03-15 00:00:00, tz=None
    dt2=2022-03-15 00:00:00, tz=None
    dt3=2022-03-15 00:00:00, tz=None

    s  = 15,3
    dt1=2022-09-15 00:00:00, tz=None
    dt2=2022-09-15 00:00:00, tz=None
    dt3=2022-09-15 00:00:00, tz=None

    s  = 3,15,2022
    dt1=2022-03-15 00:00:00, tz=None
    dt2=2022-03-15 00:00:00, tz=None
    dt3=2022-03-15 00:00:00, tz=None

    s  = 2003 10:20:30 Thu Sep 25
    dt1=2003-09-25 10:20:30, tz=None
    dt2=2003-09-25 10:20:30, tz=None
    dt3=2003-09-25 10:20:30, tz=None

    s  = 2020-09-09T10:20:20.5-8:00
    dt1=2020-09-09 10:20:20.500000-08:00, tz=tzoffset(None, -28800)
    dt2=2020-09-09 10:20:20.500000-08:00, tz=tzoffset(None, -28800)
    dt3=2020-09-09 10:20:20.500000-08:00, tz=tzoffset(None, -28800)

    s  = 2020-09-09T10:20
    dt1=2020-09-09 10:20:00, tz=None
    dt2=2020-09-09 10:20:00, tz=None
    dt3=2020-09-09 10:20:00, tz=None

    s  = 2020-09-09T10
    dt1=2020-09-09 10:00:00, tz=None
    dt2=2020-09-09 10:00:00, tz=None
    dt3=2020-09-09 10:00:00, tz=None

    s  = 2020-09-09
    dt1=2020-09-09 00:00:00, tz=None
    dt2=2020-09-09 00:00:00, tz=None
    dt3=2020-09-09 00:00:00, tz=None

    s  = 20200909T102020-8:00
    dt1=2020-09-09 10:20:20-08:00, tz=tzoffset(None, -28800)
    dt2=2020-09-09 10:20:20-08:00, tz=tzoffset(None, -28800)
    dt3=2020-09-09 10:20:20-08:00, tz=tzoffset(None, -28800)

    s  = 20200909T102020
    dt1=2020-09-09 10:20:20, tz=None
    dt2=2020-09-09 10:20:20, tz=None
    dt3=2020-09-09 10:20:20, tz=None

    s  = 20200909T1020
    dt1=2020-09-09 10:20:00, tz=None
    dt2=2020-09-09 10:20:00, tz=None
    dt3=2020-09-09 10:20:00, tz=None

    s  = 20200909T1020
    dt1=2020-09-09 10:20:00, tz=None
    dt2=2020-09-09 10:20:00, tz=None
    dt3=2020-09-09 10:20:00, tz=None

    s  = 20200909
    dt1=2020-09-09 00:00:00, tz=None
    dt2=2020-09-09 00:00:00, tz=None
    dt3=2020-09-09 00:00:00, tz=None

    s  = 20200909102020
    dt1=2020-09-09 10:20:20, tz=None
    dt2=2020-09-09 10:20:20, tz=None
    dt3=2020-09-09 10:20:20, tz=None

    s  = 10-09-2020
    dt1=2020-10-09 00:00:00, tz=None
    dt2=2020-10-09 00:00:00, tz=None
    dt3=2020-10-09 00:00:00, tz=None

    s  = 10-09-2020
    dt1=2020-10-09 00:00:00, tz=None
    dt2=2020-10-09 00:00:00, tz=None
    dt3=2020-10-09 00:00:00, tz=None

    s  = 10-09-20
    dt1=2020-10-09 00:00:00, tz=None
    dt2=2020-10-09 00:00:00, tz=None
    dt3=2020-10-09 00:00:00, tz=None

    s  = 10-09-20
    dt1=2020-10-09 00:00:00, tz=None
    dt2=2020-10-09 00:00:00, tz=None
    dt3=2020-10-09 00:00:00, tz=None

    err: tz =  ,s= today is 25 of Oct 2020 exactly at 10:20:20 with timezone +08:00
    dt1=2020-10-25 10:20:20+08:00, tz=tzoffset(None, 28800)
    dt2=2020-10-25 10:20:20+08:00, tz=tzoffset(None, 28800)
    dt3=2020-10-25 10:20:20+08:00, tz=tzoffset(None, 28800)

    err: tz =  ,s= 今天是2020年10月20日 10点23分49秒
    dt1=2010-10-20 23:00:00, tz=None
    dt2=2010-10-20 23:00:00, tz=None
    dt3=2010-10-20 23:00:00, tz=None

    err: tz =  ,s= 今天是2020年10月20日,明天有个预约
    dt1=2022-10-20 00:00:00, tz=None
    dt2=2022-10-20 00:00:00, tz=None
    dt3=2022-10-20 00:00:00, tz=None

    s  = 2017-08-28 06:08:20 CDT
    dt1=2017-08-28 06:08:20, tz=None
    dt2=2017-08-28 06:08:20+08:00, tz=tzfile('PRC')
    dt3=2017-08-28 06:08:20-05:00, tz=tzfile('US/Central')

    s  = Thu Sep 25 10:20:30 BRST 2003
    dt1=2003-09-25 10:20:30, tz=None
    dt2=2003-09-25 10:20:30+08:00, tz=tzfile('PRC')
    dt3=2003-09-25 10:20:30-05:00, tz=tzfile('US/Central')

    s  = 2003 Thu Sep 25 10:20:30 BRST
    dt1=2003-09-25 10:20:30, tz=None
    dt2=2003-09-25 10:20:30+08:00, tz=tzfile('PRC')
    dt3=2003-09-25 10:20:30-05:00, tz=tzfile('US/Central')

    s  = 2003 10:20:30 Thu Sep 25 BRST
    dt1=2003-09-25 10:20:30, tz=None
    dt2=2003-09-25 10:20:30+08:00, tz=tzfile('PRC')
    dt3=2003-09-25 10:20:30-05:00, tz=tzfile('US/Central')

    s  = Thu Sep 25 10:20:30 BRST 2003
    dt1=2003-09-25 10:20:30, tz=None
    dt2=2003-09-25 10:20:30+08:00, tz=tzfile('PRC')
    dt3=2003-09-25 10:20:30-05:00, tz=tzfile('US/Central')


     test arrow_get start...

    ar1= 2022-09-20T12:06:06.850414+00:00 <class 'float'>
    ar2= 2022-09-20T20:06:06.850414+08:00 <class 'float'>
    ar3= 2022-09-20T07:06:06.850414-05:00 <class 'float'>

    ar1= 2022-09-20T12:06:06.850414+00:00 <class 'int'>
    ar2= 2022-09-20T20:06:06.850414+08:00 <class 'int'>
    ar3= 2022-09-20T07:06:06.850414-05:00 <class 'int'>

    ar1= 2022-09-20T20:06:06+00:00 <class 'time.struct_time'>
    ar2= 2022-09-21T04:06:06+08:00 <class 'time.struct_time'>
    ar3= 2022-09-20T15:06:06-05:00 <class 'time.struct_time'>

    ar1= 2022-09-20T20:06:06.850413+00:00 <class 'datetime.datetime'>
    ar2= 2022-09-20T20:06:06.850413+08:00 <class 'datetime.datetime'>
    ar3= 2022-09-20T20:06:06.850413-05:00 <class 'datetime.datetime'>

    ar1= 2022-09-20T20:06:06.850413+08:00 <class 'datetime.datetime'>
    ar2= 2022-09-20T20:06:06.850413+08:00 <class 'datetime.datetime'>
    ar3= 2022-09-20T20:06:06.850413-05:00 <class 'datetime.datetime'>

    ar1= 2022-09-20T07:06:06.850413-05:00 <class 'datetime.datetime'>
    ar2= 2022-09-20T07:06:06.850413+08:00 <class 'datetime.datetime'>
    ar3= 2022-09-20T07:06:06.850413-05:00 <class 'datetime.datetime'>

    ar1= 2022-09-20T00:00:00+00:00 <class 'datetime.date'>
    ar2= 2022-09-20T00:00:00+08:00 <class 'datetime.date'>
    ar3= 2022-09-20T00:00:00-05:00 <class 'datetime.date'>

    ar1= 2022-09-20T00:00:00+00:00 <class 'datetime.date'>
    ar2= 2022-09-20T00:00:00+08:00 <class 'datetime.date'>
    ar3= 2022-09-20T00:00:00-05:00 <class 'datetime.date'>

    ar1= 2022-09-20T00:00:00+00:00 <class 'datetime.date'>
    ar2= 2022-09-20T00:00:00+08:00 <class 'datetime.date'>
    ar3= 2022-09-20T00:00:00-05:00 <class 'datetime.date'>

    ar1= 2022-09-20T20:06:06.850413+00:00 <class 'datetime.time'>
    ar2= 2022-09-20T20:06:06.850413+08:00 <class 'datetime.time'>
    ar3= 2022-09-20T20:06:06.850413-05:00 <class 'datetime.time'>

    ar1= 2022-09-20T00:00:00+00:00 <class 'datetime.date'>
    ar2= 2022-09-20T00:00:00+08:00 <class 'datetime.date'>
    ar3= 2022-09-20T00:00:00-05:00 <class 'datetime.date'>

    ar1= 2022-09-20T00:00:00+00:00 <class 'datetime.date'>
    ar2= 2022-09-20T00:00:00+08:00 <class 'datetime.date'>
    ar3= 2022-09-20T00:00:00-05:00 <class 'datetime.date'>

    ar1= 2022-09-20T20:06:06.850413+00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
    ar2= 2022-09-20T20:06:06.850413+08:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
    ar3= 2022-09-20T20:06:06.850413-05:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>

    ar1= 2022-09-20T20:06:06.850413+08:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
    ar2= 2022-09-20T20:06:06.850413+08:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
    ar3= 2022-09-20T20:06:06.850413-05:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>

    ar1= 2022-09-20T07:06:06.851414-05:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
    ar2= 2022-09-20T07:06:06.851414+08:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
    ar3= 2022-09-20T07:06:06.851414-05:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>

    ar1= 2022-05-08T00:00:00+00:00 <class 'tuple'>
    ar2= 2022-05-08T08:00:00+08:00 <class 'tuple'>
    ar3= 2022-05-07T19:00:00-05:00 <class 'tuple'>


    ar1= 2013-09-29T01:26:43.830580+00:00 <class 'str'>
    ar2= 2013-09-29T09:26:43.830580+08:00 <class 'str'>
    ar3= 2013-09-28T20:26:43.830580-05:00 <class 'str'>

    ar1= 2016-04-13T13:36:56.456289+00:00 <class 'str'>
    ar2= 2016-04-13T21:36:56.456289+08:00 <class 'str'>
    ar3= 2016-04-13T08:36:56.456289-05:00 <class 'str'>

    ar1= 2013-05-05T12:30:45-05:00 <class 'str'>
    ar2= 2013-05-05T12:30:45-05:00 <class 'str'>
    ar3= 2013-05-05T12:30:45-05:00 <class 'str'>

    ar1= 2013-05-05T12:30:45+00:00 <class 'str'>
    ar2= 2013-05-05T20:30:45+08:00 <class 'str'>
    ar3= 2013-05-05T07:30:45-05:00 <class 'str'>

    arrow_get(**kwargs)= 2022-09-20T16:58:00+00:00

    """

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值