python dateutil 日期处理模块 详解

一. 参考文件

1.1 官方文档

官方文档(英文),不想看英文的,那就算了

1.2 安装方法

pip install python-dateutil

二. 代码样例(examples)

2.1 relativedelta (相对增量)样例

Python 3.8.13 (default, Mar 28 2022, 06:16:26) 
[Clang 12.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import datetime
>>> from dateutil.relativedelta import *
>>> import calendar
>>> NOW = datetime.now()
>>> NOW
datetime.datetime(2022, 6, 10, 10, 33, 39, 421646)
>>> TODAY = date.today()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'date' is not defined
>>> from datetime import date
>>> TODAY = date.today()
>>> TODAY
datetime.date(2022, 6, 10)
## 相对增量计算
>>> NOW + relativedelta(months=+1)
datetime.datetime(2022, 7, 10, 10, 33, 39, 421646)
>>> NOW + relativedelta(year=+1, months=-1, days=+2)
datetime.datetime(1, 5, 12, 10, 36, 34, 869162)
## 时间间隔计算
>>> marryday = datetime(2009, 2, 5)
>>> relativedelta(marryday, TODAY)
relativedelta(years=-13, months=-4, days=-5)
>>> relativedelta(marryday, TODAY).years
-13
## 最近的weekday,
>>> TODAY + relativedelta(weekday=FR)  # 最近的周五
datetime.date(2022, 6, 10)
>>> TODAY + relativedelta(weekday=SU)  # 最近的周日
datetime.date(2022, 6, 12)
>>> TODAY + relativedelta(day=31, weekday=SU)  # 当月内的最后个周日,此处的当月并非自然月内的, 而是日历格式上的,按周排好的那个周日, 这里理解起来有点费劲, 多试几次.
datetime.date(2022, 7, 3)
>>> TODAY + relativedelta(months=+1, weekday=SU)  # 下个月的第一个周日
datetime.date(2022, 7, 10)
>>> TODAY + relativedelta(weekday=FR(+1))  最近的下个周五
datetime.date(2022, 6, 10)
>>> TODAY + relativedelta(weekday=FR(-1))  最近的上个周五
datetime.date(2022, 6, 10)
>>> TODAY + relativedelta(days=+2, weekday=FR(+1))  # 因为2022.6.10是周五, 所以days+2后,即是周日, 周日后的第一个周日,即是2022.6.17
datetime.date(2022, 6, 17)
>>> 

2.2 rrule 样例

Python 3.8.13 (default, Mar 28 2022, 06:16:26) 
[Clang 12.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from dateutil.rrule import *
>>> from dateutil.parser import *
>>> from datetime import *
>>> import pprint
>>> import sys
>>> sys.displayhook = pprint.pprint

# 从2022.6.10开始,按天显示,显示10次, 返回列表
>>> list(rrule(DAILY,count=10, dtstart=date(2022,6,10)))
[datetime.datetime(2022, 6, 10, 0, 0),
 datetime.datetime(2022, 6, 11, 0, 0),
 datetime.datetime(2022, 6, 12, 0, 0),
 datetime.datetime(2022, 6, 13, 0, 0),
 datetime.datetime(2022, 6, 14, 0, 0),
 datetime.datetime(2022, 6, 15, 0, 0),
 datetime.datetime(2022, 6, 16, 0, 0),
 datetime.datetime(2022, 6, 17, 0, 0),
 datetime.datetime(2022, 6, 18, 0, 0),
 datetime.datetime(2022, 6, 19, 0, 0)]
>>> list(rrule(DAILY,count=10, dtstart=parse("20220610")))
[datetime.datetime(2022, 6, 10, 0, 0),
 datetime.datetime(2022, 6, 11, 0, 0),
 datetime.datetime(2022, 6, 12, 0, 0),
 datetime.datetime(2022, 6, 13, 0, 0),
 datetime.datetime(2022, 6, 14, 0, 0),
 datetime.datetime(2022, 6, 15, 0, 0),
 datetime.datetime(2022, 6, 16, 0, 0),
 datetime.datetime(2022, 6, 17, 0, 0),
 datetime.datetime(2022, 6, 18, 0, 0),
 datetime.datetime(2022, 6, 19, 0, 0)]
 # parse,不支持解析7位数字
>>> list(rrule(DAILY,count=10, dtstart=parse("2022610")))
Traceback (most recent call last):
  File "/Users/mac/opt/anaconda3/envs/sse38/lib/python3.8/site-packages/dateutil/parser/_parser.py", line 649, in parse
    ret = self._build_naive(res, default)
  File "/Users/mac/opt/anaconda3/envs/sse38/lib/python3.8/site-packages/dateutil/parser/_parser.py", line 1235, in _build_naive
    naive = default.replace(**repl)
ValueError: year 2022610 is out of range

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mac/opt/anaconda3/envs/sse38/lib/python3.8/site-packages/dateutil/parser/_parser.py", line 1368, in parse
    return DEFAULTPARSER.parse(timestr, **kwargs)
  File "/Users/mac/opt/anaconda3/envs/sse38/lib/python3.8/site-packages/dateutil/parser/_parser.py", line 651, in parse
    six.raise_from(ParserError(str(e) + ": %s", timestr), e)
  File "<string>", line 3, in raise_from
dateutil.parser._parser.ParserError: year 2022610 is out of range: 2022610
>>> list(rrule(DAILY,count=10, dtstart=parse("2022.6.10")))
[datetime.datetime(2022, 6, 10, 0, 0),
 datetime.datetime(2022, 6, 11, 0, 0),
 datetime.datetime(2022, 6, 12, 0, 0),
 datetime.datetime(2022, 6, 13, 0, 0),
 datetime.datetime(2022, 6, 14, 0, 0),
 datetime.datetime(2022, 6, 15, 0, 0),
 datetime.datetime(2022, 6, 16, 0, 0),
 datetime.datetime(2022, 6, 17, 0, 0),
 datetime.datetime(2022, 6, 18, 0, 0),
 datetime.datetime(2022, 6, 19, 0, 0)]
 # parse支持解析到时间
>>> list(rrule(DAILY,count=10, dtstart=parse("20220610T091223")))
[datetime.datetime(2022, 6, 10, 9, 12, 23),
 datetime.datetime(2022, 6, 11, 9, 12, 23),
 datetime.datetime(2022, 6, 12, 9, 12, 23),
 datetime.datetime(2022, 6, 13, 9, 12, 23),
 datetime.datetime(2022, 6, 14, 9, 12, 23),
 datetime.datetime(2022, 6, 15, 9, 12, 23),
 datetime.datetime(2022, 6, 16, 9, 12, 23),
 datetime.datetime(2022, 6, 17, 9, 12, 23),
 datetime.datetime(2022, 6, 18, 9, 12, 23),
 datetime.datetime(2022, 6, 19, 9, 12, 23)]
# rrule 支持展示"截止到什么时间", 但不支持24:00:00的格式 
>>> list(rrule(DAILY, dtstart=parse("20220610T091223"), until=datetime(2022,6,19,24,0,0)))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: hour must be in 0..23
# rrule支持展示"截止到什么时间"
>>> list(rrule(DAILY, dtstart=parse("20220610T091223"), until=datetime(2022,6,19,23,59,59)))
[datetime.datetime(2022, 6, 10, 9, 12, 23),
 datetime.datetime(2022, 6, 11, 9, 12, 23),
 datetime.datetime(2022, 6, 12, 9, 12, 23),
 datetime.datetime(2022, 6, 13, 9, 12, 23),
 datetime.datetime(2022, 6, 14, 9, 12, 23),
 datetime.datetime(2022, 6, 15, 9, 12, 23),
 datetime.datetime(2022, 6, 16, 9, 12, 23),
 datetime.datetime(2022, 6, 17, 9, 12, 23),
 datetime.datetime(2022, 6, 18, 9, 12, 23),
 datetime.datetime(2022, 6, 19, 9, 12, 23)]
 # 支持步长显示
>>> list(rrule(DAILY, dtstart=parse("20220610T091223"),interval=3, count=10))
[datetime.datetime(2022, 6, 10, 9, 12, 23),
 datetime.datetime(2022, 6, 13, 9, 12, 23),
 datetime.datetime(2022, 6, 16, 9, 12, 23),
 datetime.datetime(2022, 6, 19, 9, 12, 23),
 datetime.datetime(2022, 6, 22, 9, 12, 23),
 datetime.datetime(2022, 6, 25, 9, 12, 23),
 datetime.datetime(2022, 6, 28, 9, 12, 23),
 datetime.datetime(2022, 7, 1, 9, 12, 23),
 datetime.datetime(2022, 7, 4, 9, 12, 23),
 datetime.datetime(2022, 7, 7, 9, 12, 23)]
>>> list(rrule(DAILY, dtstart=parse("20220610T091223"),interval=10, count=3))
[datetime.datetime(2022, 6, 10, 9, 12, 23),
 datetime.datetime(2022, 6, 20, 9, 12, 23),
 datetime.datetime(2022, 6, 30, 9, 12, 23)]
##  bymonth是指一月份, byweekday是周几, range(7)是指所有,   总体意思是:输出dtstart到until之几年间,每年1月份中的所有日期.
 >>> list(rrule(YEARLY,bymonth=1, byweekday=range(7), dtstart=parse('20200101'), until=parse('20220610')))
<内容太多,不粘贴了>
# 这意思是显示这几年1月份中所有的周二.
>>>list(rrule(YEARLY,bymonth=1, byweekday=1, dtstart=parse('20200101'), until=parse('20220610')))
# 来个实例, 父亲节, 每年6月份的第3个周日
>>> list(rrule(YEARLY, count=5,bymonth=6, byweekday=(SU(3)),dtstart=parse('20220610')))
[datetime.datetime(2022, 6, 19, 0, 0),
 datetime.datetime(2023, 6, 18, 0, 0),
 datetime.datetime(2024, 6, 16, 0, 0),
 datetime.datetime(2025, 6, 15, 0, 0),
 datetime.datetime(2026, 6, 21, 0, 0)]
[datetime.datetime(2022, 6, 19, 0, 0),
 datetime.datetime(2023, 6, 18, 0, 0),
 datetime.datetime(2024, 6, 16, 0, 0),
 datetime.datetime(2025, 6, 15, 0, 0),
 datetime.datetime(2026, 6, 21, 0, 0)]

# 从dtstart开始,按周输出, 输出10次,
>>> list(rrule(WEEKLY, count=10, dtstart=parse('20220110')))
[datetime.datetime(2022, 1, 10, 0, 0),
 datetime.datetime(2022, 1, 17, 0, 0),
 datetime.datetime(2022, 1, 24, 0, 0),
 datetime.datetime(2022, 1, 31, 0, 0),
 datetime.datetime(2022, 2, 7, 0, 0),
 datetime.datetime(2022, 2, 14, 0, 0),
 datetime.datetime(2022, 2, 21, 0, 0),
 datetime.datetime(2022, 2, 28, 0, 0),
 datetime.datetime(2022, 3, 7, 0, 0),
 datetime.datetime(2022, 3, 14, 0, 0)]
# 按周输出,步长为2,输出5次
>>> list(rrule(WEEKLY, interval=2, count=5, dtstart=parse('20220110')))
[datetime.datetime(2022, 1, 10, 0, 0),
 datetime.datetime(2022, 1, 24, 0, 0),
 datetime.datetime(2022, 2, 7, 0, 0),
 datetime.datetime(2022, 2, 21, 0, 0),
 datetime.datetime(2022, 3, 7, 0, 0)]
 # 这个好玩点,从dtstart开始, 按周输出5次,wkst是每周的开始是SUNDAY, 只输出(TU,TH)两天
>>> list(rrule(WEEKLY, count=5, wkst=SU, byweekday=(TU,TH),dtstart=parse('20220610')))
[datetime.datetime(2022, 6, 14, 0, 0),
 datetime.datetime(2022, 6, 16, 0, 0),
 datetime.datetime(2022, 6, 21, 0, 0),
 datetime.datetime(2022, 6, 23, 0, 0),
 datetime.datetime(2022, 6, 28, 0, 0)]
>>> 

2.3 rruleset(规则集)接上

>>> s = rruleset()
# 向规则集中添加一个正向规则(即需要的内容)
>>> s.rrule(rrule(DAILY, count=7, dtstart=parse('20220610')))
None
# 向规则集中添加一个反向规则(即不需要的内容,此内容有可能已经包含在正向规则中,要把它排除掉)
>>> s.exrule(rrule(DAILY, byweekday=(SA,SU),dtstart=parse("20220610")))
None
>>> list(s)
[datetime.datetime(2022, 6, 10, 0, 0),
 datetime.datetime(2022, 6, 13, 0, 0),
 datetime.datetime(2022, 6, 14, 0, 0),
 datetime.datetime(2022, 6, 15, 0, 0),
 datetime.datetime(2022, 6, 16, 0, 0)]
# 向规则中添加一个正向规则之外特殊规则(不包在正向规则之内)
>>> s.rdate(datetime(2022,7,10,23,23))
None
>>> list(s)
[datetime.datetime(2022, 6, 10, 0, 0),
 datetime.datetime(2022, 6, 13, 0, 0),
 datetime.datetime(2022, 6, 14, 0, 0),
 datetime.datetime(2022, 6, 15, 0, 0),
 datetime.datetime(2022, 6, 16, 0, 0),
 datetime.datetime(2022, 7, 10, 23, 23)]

这个事儿可以这么理解: 比如一家公司想要本科以上学历的人, 这就是正向规则, 不想要本科以下学历的人, 这是反向规则, 但有一个人非常牛逼, 还是让他入职了, 此时就是特殊规则.

2.4 parse(解析日期)(重要)

Python 3.8.13 (default, Mar 28 2022, 06:16:26) 
[Clang 12.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 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.datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))
>>> parse("2003 Thu Sep 25 10:20:30 BRST", tzinfos=TZOFFSETS)
datetime.datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))
>>> parse("2003 10:20:30 Thu Sep 25 BRST", tzinfos=TZOFFSETS)
datetime.datetime(2003, 9, 25, 10, 20, 30, tzinfo=tzoffset('BRST', -10800))

# 正常情况下是这样的
>>> parse("2003 10:20:30 Thu Sep 25")
datetime.datetime(2003, 9, 25, 10, 20, 30)
>>> parse("Thu Sep 25 10:20:30 BRST 2003", ignoretz=True)
datetime.datetime(2003, 9, 25, 10, 20, 30)
# 缺省必要的数据时,可以采用default值为补全
>>> parse("10:20:30 Thu Sep 25", default=DEFAULT)
datetime.datetime(2003, 9, 25, 10, 20, 30)
>>> parse("Thu Sep 25", default=DEFAULT)
datetime.datetime(2003, 9, 25, 0, 0)

# 支持解析标准时间格式 
>>> parse("2020-09-09T10:20:20.5-8:00")
datetime.datetime(2020, 9, 9, 10, 20, 20, 500000, tzinfo=tzoffset(None, -28800))
>>> parse("2020-09-09T10:20")
datetime.datetime(2020, 9, 9, 10, 20)
>>> parse("2020-09-09T10")
datetime.datetime(2020, 9, 9, 10, 0)
>>> parse("2020-09-09")
datetime.datetime(2020, 9, 9, 0, 0)
# 支持解析不带分隔符的时间格式
>>> parse("20200909T102020-8:00")
datetime.datetime(2020, 9, 9, 10, 20, 20, tzinfo=tzoffset(None, -28800))
>>> parse("20200909T102020")
datetime.datetime(2020, 9, 9, 10, 20, 20)
>>> parse("20200909T1020")
datetime.datetime(2020, 9, 9, 10, 20)
>>> parse("20200909T1020")
datetime.datetime(2020, 9, 9, 10, 20)
>>> parse("20200909")
datetime.datetime(2020, 9, 9, 0, 0)
>>> parse("20200909102020")
datetime.datetime(2020, 9, 9, 10, 20, 20)
# 支持有歧义的解析
>>> parse("10-09-2020")
datetime.datetime(2020, 10, 9, 0, 0)
>>> parse("10-09-2020", dayfirst=True)
datetime.datetime(2020, 9, 10, 0, 0)
>>> parse("10-09-20", dayfirst=True)
datetime.datetime(2020, 9, 10, 0, 0)
>>> parse("10-09-20", yearfirst=True)
datetime.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.datetime(2020, 10, 25, 10, 20, 20, tzinfo=tzoffset(None, 28800))
>>> s = "今天是2020年10月20日 10点23分49秒"
>>> parse(s, fuzzy=True)
datetime.datetime(2010, 10, 20, 23, 49)
>>> s = "今天是2020年10月20日,明天有个预约"
>>> parse(s, fuzzy=True)
datetime.datetime(2022, 10, 20, 0, 0)
>>> 

三. 练习题(Exercies)

3.1 MLK DAY(马丁.路德.金 日)

马丁.路德.金 日 是美国的一个节日, 始于1986.1.20, 此后,每年1月份的第三个周一,即为该节日.


from dateutil import rrule
from datetime import datetime
# 关键部分
MLK_DAY = rrule.rrule(rrule.YEARLY, 
	bymonth=1, 
	byweekday=(rrule.MO(3)), 
	dtstart=datetime(1986, 1,20)
	)
print(MLK_DAY)

from datetime import datetime
MLK_TEST_CASES = [
    ((datetime(1970, 1, 1), datetime(1980, 1, 1)),
     []),
    ((datetime(1980, 1, 1), datetime(1989, 1, 1)),
     [datetime(1986, 1, 20),
      datetime(1987, 1, 19),
      datetime(1988, 1, 18)]),
    ((datetime(2017, 2, 1), datetime(2022, 2, 1)),
     [datetime(2018, 1, 15, 0, 0),
      datetime(2019, 1, 21, 0, 0),
      datetime(2020, 1, 20, 0, 0),
      datetime(2021, 1, 18, 0, 0),
      datetime(2022, 1, 17, 0, 0)]
     ),
]

def test_mlk_day():
    for (between_args, expected) in MLK_TEST_CASES:
        assert MLK_DAY.between(*between_args) == expected

test_mlk_day()

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值