• python生成时间序列(date_range)


    介绍

    自己写了一个用python内置模块实现的生成时间序列的函数

    支持自动推断字符串到datetime的转换, 但对格式有一定要求, 其它格式可手动指定格式化方式, 格式化方式与python内置格式化格式完全一致

    支持输出格式化

    以下是方法的源代码(python环境3.5以上, 没做更多版本的支持, 需要的自己稍作调整即可)

    源代码

    import re
    import calendar
    import datetime
    
    
    class FormatError(ValueError):
        pass
    
    
    class Date(object):
        @classmethod
        def date_range(cls, start=None, end=None, periods=None, freq=None, input_format=None, out_format=None):
            """
            生成时间序列
            :param start: 序列开始时间
            :param end: 序列结束时间, 给定start时, 结束时间不包含end
            :param periods: int, 生成的时间序列长度
            :param freq: 要生成时间序列的时间间隔
            :param out_format: 是否输出格式化后的字符串, 若要输出可指定输出格式. "%Y-%m-%d %H:%M:%S"
            :param input_format: 若start或end是字符串且无法自动推断时间格式则需指定格式
            :return: [date or date_str]
            """
            start = cls.str_to_date(start, input_format)
            end = cls.str_to_date(end, input_format)
            out = []
            if start is None and end and periods:
                for i in range(periods-1):
                    old, end = cls.date_replace(end, cls._freq(freq), mod="-")
                    if i == 0:
                        out.append(old)
                    out = [end] + out
            elif end is None and start and periods:
                for i in range(periods-1):
                    old, start = cls.date_replace(start, cls._freq(freq), mod="+")
                    if i == 0:
                        out.append(old)
                    out.append(start)
            elif periods is None and start and end:
                i = 0
                while True:
                    old, start = cls.date_replace(start, cls._freq(freq), mod="+")
                    if i == 0:
                        out.append(old)
                        i += 1
                    if start < end:
                        out.append(start)
                    else:
                        break
            else:
                raise ValueError("start, end, periods 须且只能指定其中两个")
            if out_format is True:
                out = [str(i) for i in out]
            elif out_format is not None:
                out = [i.strftime(out_format) for i in out]
            return out
    
        @staticmethod
        def date_replace(date, freq=(0, )*6, mod="+"):
            timedelta = datetime.timedelta(days=freq[2], hours=freq[3], minutes=freq[4], seconds=freq[5])
            if mod == "+":
                if sum(freq[:2]) == 0:
                    old = date
                    date = date + timedelta
                elif sum(freq[2:]) == 0:
                    y = date.year + freq[0] + (date.month + freq[1] - 1) // 12
                    m = (date.month + freq[1] - 1) % 12 + 1
                    old = date.replace(day=calendar.monthrange(date.year, date.month)[1])
                    date = date.replace(year=y, month=m, day=calendar.monthrange(y, m)[1])
                else:
                    raise ValueError(" '年月' 不能同时和 '日时分秒' 作为间隔")
            elif mod == "-":
                if sum(freq[:2]) == 0:
                    old = date
                    date = date - timedelta
                elif sum(freq[2:]) == 0:
                    y = date.year - freq[0] + (date.month - freq[1] - 1) // 12
                    m = (date.month - freq[1] - 1) % 12 + 1
                    old = date.replace(day=calendar.monthrange(date.year, date.month)[1])
                    date = date.replace(year=y, month=m, day=calendar.monthrange(y, m)[1])
                else:
                    raise ValueError(" '年月' 不能同时和 '日时分秒' 作为间隔")
            else:
                raise ValueError("mod值只能是 '+' 或 '-' ")
            return old, date
    
        @staticmethod
        def _freq(freq=None):
            """
            设置时间间隔
            :param freq: "Y2m3d4H5M6S" 表示间隔 1年2月3日4时5分6秒
            :return: [年, 月, 日, 时, 分, 秒]
            """
            freq_ = [0] * 6
            if freq is None:
                freq_[2] = 1
                return freq_
            for n, i in enumerate(["Y", "m", "d", "H", "M", "S"]):
                r = f'((d*){i})'
                s = re.search(r, freq)
                if s:
                    freq_[n] = int(s.group(2)) if s.group(2) else 1
            return freq_
    
        @staticmethod
        def str_to_date(string, format_=None):
            """
            字符串转时间, 默认自动推断格式
            :param string: 时间字符串
            :param format_: 格式
            :return: 对应的时间类型, 输入非字符串则原值输出
            """
            if not isinstance(string, str):
                return string
            if format_:
                return datetime.datetime.strptime(string, format_)
            s = re.match(r'(d{4})D+(d{1,2})D+(d{1,2})(?:D+(d{1,2}))?(?:D+(d{1,2}))?(?:D+(d{1,2}))?D*$', string)
            if s:
                result = [int(i) for i in s.groups() if i]
                return datetime.datetime(*result)
            s = re.match(r'(d{4})D*(d{2})D*(d{2})D*(d{2})?D*(d{2})?D*(d{2})?D*$', string)
            if s:
                result = [int(i) for i in s.groups() if i]
                return datetime.datetime(*result)
            else:
                raise FormatError("自动推断失败, 请指定format_")

    使用方式

    print(Date.date_range(datetime.datetime(2018, 9, 18), periods=10))
    print()
    print(Date.date_range('20180918', '2018-09-28'))
    print()
    print(Date.date_range(end='20180927', periods=10))
    print()
    print(Date.date_range('20180918', '2018-09-28', out_format=True))
    print()
    print(Date.date_range('2018/09/18', '2018-09-28', out_format="%Y-%m-%d"))
    print()
    print(Date.date_range('2018年9月18日', '2019-09-28', freq="m", out_format="%Y-%m-%d"))
    print()
    print(Date.date_range('2018/9/18', '2018-9-19', freq="3H", out_format=True))

    对应结果

    [datetime.datetime(2018, 9, 18, 0, 0), datetime.datetime(2018, 9, 19, 0, 0), datetime.datetime(2018, 9, 20, 0, 0), datetime.datetime(2018, 9, 21, 0, 0), datetime.datetime(2018, 9, 22, 0, 0), datetime.datetime(2018, 9, 23, 0, 0), datetime.datetime(2018, 9, 24, 0, 0), datetime.datetime(2018, 9, 25, 0, 0), datetime.datetime(2018, 9, 26, 0, 0), datetime.datetime(2018, 9, 27, 0, 0)]
    
    [datetime.datetime(2018, 9, 18, 0, 0), datetime.datetime(2018, 9, 19, 0, 0), datetime.datetime(2018, 9, 20, 0, 0), datetime.datetime(2018, 9, 21, 0, 0), datetime.datetime(2018, 9, 22, 0, 0), datetime.datetime(2018, 9, 23, 0, 0), datetime.datetime(2018, 9, 24, 0, 0), datetime.datetime(2018, 9, 25, 0, 0), datetime.datetime(2018, 9, 26, 0, 0), datetime.datetime(2018, 9, 27, 0, 0)]
    
    [datetime.datetime(2018, 9, 18, 0, 0), datetime.datetime(2018, 9, 19, 0, 0), datetime.datetime(2018, 9, 20, 0, 0), datetime.datetime(2018, 9, 21, 0, 0), datetime.datetime(2018, 9, 22, 0, 0), datetime.datetime(2018, 9, 23, 0, 0), datetime.datetime(2018, 9, 24, 0, 0), datetime.datetime(2018, 9, 25, 0, 0), datetime.datetime(2018, 9, 26, 0, 0), datetime.datetime(2018, 9, 27, 0, 0)]
    
    ['2018-09-18 00:00:00', '2018-09-19 00:00:00', '2018-09-20 00:00:00', '2018-09-21 00:00:00', '2018-09-22 00:00:00', '2018-09-23 00:00:00', '2018-09-24 00:00:00', '2018-09-25 00:00:00', '2018-09-26 00:00:00', '2018-09-27 00:00:00']
    
    ['2018-09-18', '2018-09-19', '2018-09-20', '2018-09-21', '2018-09-22', '2018-09-23', '2018-09-24', '2018-09-25', '2018-09-26', '2018-09-27']
    
    ['2018-09-30', '2018-10-31', '2018-11-30', '2018-12-31', '2019-01-31', '2019-02-28', '2019-03-31', '2019-04-30', '2019-05-31', '2019-06-30', '2019-07-31', '2019-08-31']
    
    ['2018-09-18 00:00:00', '2018-09-18 03:00:00', '2018-09-18 06:00:00', '2018-09-18 09:00:00', '2018-09-18 12:00:00', '2018-09-18 15:00:00', '2018-09-18 18:00:00', '2018-09-18 21:00:00']
  • 相关阅读:
    VMware虚拟机12安装linux系统
    PHP 底层的运行机制与原理
    分享10条PHP性能优化的小技巧,帮助你更好的用PHP开发
    MySQL wamp密码修改
    分布式之抉择分布式锁
    面试总结——Java高级工程师(三)
    面试总结——Java高级工程师(二)
    面试题总结——走向JAVA高级工程师
    浅谈Nginx之反向代理与负载均衡
    修复TortoiseGit文件夹和文件图标不显示
  • 原文地址:https://www.cnblogs.com/P--K/p/9723050.html
Copyright © 2020-2023  润新知