楔子

dateutil是一个处理时间的库,可以非常智能的将字符串解析成时间类型,并且这也是pandas所依赖的库。下面来看一下用法:

parser

dateutil下面有一个parser模块,它是专门用来将字符串解析为时间类型的。

from dateutil import parser

直接导入即可,然后调用内部的parse方法。

>>> parser.parse("2018-3-25")
datetime.datetime(2018, 3, 25, 0, 0)
>>> parser.parse("2018-03-25")
datetime.datetime(2018, 3, 25, 0, 0)
>>> parser.parse("2018/3/25")
datetime.datetime(2018, 3, 25, 0, 0)
>>> parser.parse("2018/03/25")
datetime.datetime(2018, 3, 25, 0, 0)

我们看到还是很智能的,可以使用/或者-进行分隔。

# 即使没有分隔符也是可以的,但是必须是yyyymmdd这种形式,比如月份,如果是3月,那么要写03
# 因为在没有分隔符的情况,写成2018325的话,那么会把2018325都当成年来解析
>>> parser.parse("20180325")
datetime.datetime(2018, 3, 25, 0, 0) # 如果只有两部分,那么会自动把前面的当成月、后面当成日(但是有特例,后面说)
# 没指定的部分,默认为当前日期对应的部分
>>> parser.parse("03-25")
datetime.datetime(2020, 3, 25, 0, 0)
# 我们看到月份超过了12,所以报错了
>>> parser.parse("13-5")
ValueError: month must be in 1..12
# 但是如果月份超过了31,那么就不再是月份了,而是会被当成年来解析,那么同理后面的就会变成月。
# 也就是前面的当成是年、后面的当成是月
# 当前是两千多年,所以解释成2032年,而5则解释成5月。而"日"则是25,因为它没有指定,所以和当前日期保持一致
>>> parser.parse("32-5")
datetime.datetime(2032, 5, 25, 0, 0)
# 但是超过70,那么会被解释成1970年,这与unix诞生时间有关
>>> parser.parse("70-5")
datetime.datetime(1970, 5, 25, 0, 0)
# 如果超过了100,那么就是其本身
>>> parser.parse("100-5")
datetime.datetime(100, 5, 25, 0, 0) >>> parser.parse("1225")
# 由于没有分割符那么1225整体会被当成是年来解释,然后月和日则和当前日期保持一致,3月25日
# 所以如果只有两部分,最好指定分隔符。
# 不指定分隔符的情况最好只用于yyyymmdd这种形式,如果只有两部分、还不指定分隔符的话,范围太广了
datetime.datetime(1225, 3, 25, 0, 0) # 213一样会被解释成年,其它部分和当前日期保持一致
>>> parser.parse("213")
datetime.datetime(213, 3, 25, 0, 0) # 但如果小于31,那么会被解释成日
>>> parser.parse("32")
datetime.datetime(2032, 3, 25, 0, 0)
# 这里被解释成"日"了,当然前提是当前月份有31天
>>> parser.parse("31")
datetime.datetime(2020, 3, 31, 0, 0)
>>> parser.parse("06")
datetime.datetime(2020, 3, 6, 0, 0) # 所以dateutil给我们做了很多的处理,但老实说我们基本不会处理像"31"、"1225"这种格式的字符串,因为它太模糊了,所以即便是这种数据,起码也要有分隔符
# 如果有分隔符,那么处理起来会非常简单
# 还是那句话,如果没有分隔符,还要dateutil来处理的话,那么最好是yyyymmdd的格式,其它情况要有分隔符:/或者-都可以

如果不是年月日的顺序呢?

# 首先看一下这种情况,如果都只有两位,那么会自动把最后一部分当成年来解析
# 前面的两部分则是月和日,也就是"月日年"
>>> parser.parse("12/25/18")
datetime.datetime(2018, 12, 25, 0, 0)
>>> parser.parse("12/10/11")
datetime.datetime(2011, 12, 10, 0, 0) # 但18显然超过最大月份12,那么之前的月日年、则会变成日月年
>>> parser.parse("18/10/11")
datetime.datetime(2011, 10, 18, 0, 0)
# 同理年份写全也是一样的
>>> parser.parse("12/10/2011")
datetime.datetime(2011, 12, 10, 0, 0)
>>> parser.parse("18/10/2011")
datetime.datetime(2011, 10, 18, 0, 0)
# 但是年份只能位于开头或者结尾,不能在中间
# 如果年在结尾,那么会按照月日年来解析,但是月份大于12,那么会按照日月年来解析
# 如果年在开头,那么只会按照年月日来解析,不存在年日月这一说

此外dateutil还可以识别英文模式的字符串

>>> parser.parse("Mar 15 2018")
datetime.datetime(2018, 3, 15, 0, 0)

rrule

rrule是用于生成多个连续的日期,如果你知道pandas的data_range,那么这个很好理解。

>>> from dateutil import rrule
>>> list(rrule.rrule(freq=rrule.DAILY, dtstart=parser.parse("2018-1-1"), until=parser.parse("2018-1-5")))
[datetime.datetime(2018, 1, 1, 0, 0),
datetime.datetime(2018, 1, 2, 0, 0),
datetime.datetime(2018, 1, 3, 0, 0),
datetime.datetime(2018, 1, 4, 0, 0),
datetime.datetime(2018, 1, 5, 0, 0)]
  • freq:YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY,年、月、星期、天、小时、分、秒,即间隔,我们上面的例子是DAILY,每一天生成一个
  • dtstart:起始时间
  • until:结束时间
>>> list(rrule.rrule(freq=rrule.DAILY, count=3, dtstart=parser.parse("2018-1-1"), until=parser.parse("2018-1-5")))
[datetime.datetime(2018, 1, 1, 0, 0),
datetime.datetime(2018, 1, 2, 0, 0),
datetime.datetime(2018, 1, 3, 0, 0),
]
  • count:只生成多少个
>>> list(rrule.rrule(freq=rrule.DAILY, interval=2, dtstart=parser.parse("2018-1-1"), until=parser.parse("2018-1-5")))
[datetime.datetime(2018, 1, 1, 0, 0),
datetime.datetime(2018, 1, 3, 0, 0),
datetime.datetime(2018, 1, 5, 0, 0)]
  • interval:间隔,freq指的是天,加上interval=2,所以就是每隔两天
>>> list(rrule.rrule(freq=rrule.DAILY, byweekday=(rrule.MO, rrule.SU), dtstart=parser.parse("2018-1-1"), until=parser.parse("2018-1-5")))
[datetime.datetime(2018, 1, 1, 0, 0)]
  • byweekday=(rrule.MO, rrule.SU),表示只保留周一和周日

rrule还可以计算两个日期之间查了多少天、多少月、多少年等等,默认的timedelta则最大只能计算到天。

>>> rrule.rrule(freq=rrule.DAILY, dtstart=parser.parse("2018-1-1"), until=parser.parse("2019-1-5")).count()
370
>>> rrule.rrule(freq=rrule.MONTHLY, dtstart=parser.parse("2018-1-1"), until=parser.parse("2019-1-5")).count()
13
>>> rrule.rrule(freq=rrule.YEARLY, dtstart=parser.parse("2018-1-1"), until=parser.parse("2019-1-5")).count()
2

我们看到还是使用rrule.rrule,如果调用count方法就会计算差值,至于计算什么差值,则取决于freq。

但是它的计算方式要注意,比如计算年,就先按照年来减,然后看月,如果until的"月和日"组合起来大于等于dtstart的"月和日",那么年会加1

>>> rrule.rrule(freq=rrule.YEARLY, dtstart=parser.parse("2018-1-1"), until=parser.parse("2019-1-1")).count()
2

明明都是1月1号,但是两者差了两年。对于月也是同理,先减去月,然后看日,如果until的日大于等于dtstart的日,那么月也会加1

>>> rrule.rrule(freq=rrule.MONTHLY, dtstart=parser.parse("2018-1-1"), until=parser.parse("2018-3-1")).count()
3
>>> rrule.rrule(freq=rrule.MONTHLY, dtstart=parser.parse("2018-1-2"), until=parser.parse("2018-3-1")).count()
2

所以不单单是减完就结束了,还会进行一次判断(减的是年就判断"月和日"、减的是月就判断日),如果until不小于dtstart,那么上一步减完的结果会加1。

但是说实话,这种做法个人觉得很不友好,所以我们计算两个日期之间的差值一般不会使用rrule,rrule主要还是用于生成多个连续日期。

relativedelta

个人非常推荐的一个方法,我们来看一下用法。

from dateutil.relativedelta import relativedelta
from datetime import datetime # 可以自动计算两个日期之间的差值
print(
relativedelta(datetime(2018, 1, 5), datetime(2018, 1, 1))
) # relativedelta(days=+4) print(
relativedelta(datetime(2018, 2, 5), datetime(2018, 1, 9))
) # relativedelta(days=+27) print(
relativedelta(datetime(2018, 2, 5), datetime(2018, 1, 1))
) # relativedelta(months=+1, days=+4) print(
relativedelta(datetime(2019, 2, 5), datetime(2018, 1, 1))
) # relativedelta(years=+1, months=+1, days=+4)

输入两个日期,然后会计算两个日期之间的差值,然后可以获取如下属性,:

  • years: 计算差了多少年
  • months:计算差了多少月
  • days:计算差了多少天
  • hours:计算差了多少小时
  • minutes:计算差了多少分钟
  • seconds:计算差了多少秒
  • microseconds:计算差了多少毫秒

注意:上面的指的是两个日期对应部分的差值,比如:2018-3-1和2017-1-1,之间差了两个月,并不是12+2=14,它获取的是对应部分的差值

from dateutil.relativedelta import relativedelta
from datetime import datetime dt1 = datetime(2018, 12, 11, 19, 15, 25)
dt2 = datetime(2017, 8, 3, 17, 24, 51) diff = relativedelta(dt1, dt2)
print(diff) # relativedelta(years=+1, months=+4, days=+8, hours=+1, minutes=+50, seconds=+34) print(diff.years) # 1
print(diff.months) # 4
print(diff.days) # 8
print(diff.hours) # 1
print(diff.minutes) # 50
print(diff.seconds) # 34

我们看到上面计算的结果不是我们期待的,我们希望在计算差了多少个月的时候,是希望把年算进去的,那么怎么办呢?使用pandas

import pandas as pd

for freq in ("Y", "M", "W", "D", "H", "T", "S"):
"""
Y: 年
M: 月
W: 星期
D: 天
H: 时
T: 分
S: 秒
"""
dt1 = pd.Period("2018-11-12 12:11:10", freq)
dt2 = pd.Period("2017-11-12 11:18:35", freq)
print((dt1 - dt2).n)
"""
1
12
53
365
8761
525653
31539155
"""

这一般是我们期望的结果,计算月的时候,会将差的年份乘上12再和差的月份相加,同理计算天的时候,会将年和月算进去。计算小时,则是将年、月、日都算进去。

因此计算两个日期之间的差值的时候,如果是精确到天,那么我们可以直接将日期相减,得到timedelta。但是精确到年和月,那么我们知道可以使用rrule,但是它涉及到一个问题,就是我们说过的: 2018-2-2和2018-1-1应该差了一个月零一天,但是得到结果是两个月,而relativedelta则是计算每个单独的部分之间的差值,所以我个人推荐使用pandas

那relativedelta都用在什么地方呢?如下:

from dateutil.relativedelta import relativedelta
from datetime import datetime, timedelta dt1 = datetime(2018, 12, 11, 19, 15, 25) # 给dt1加上5个月,变成了19年5五月
diff = relativedelta(months=5)
print(diff + dt1) # 2019-05-11 19:15:25 # 给dt1加上2年15天
diff = relativedelta(years=2, days=15)
print(diff + dt1) # 2020-12-26 19:15:25 # 给dt1加上14个小时、38分、12秒
diff = relativedelta(hours=14, minutes=38, seconds=12)
print(dt1 + diff) # 2018-12-12 09:53:37 # 给dt1加上3星期
diff = relativedelta(weeks=3)
print(dt1 + diff) # 2019-01-01 19:15:25
"""
所以我们可以给指定部分加上或减去任意的时间间隔
当然datetime和timedelta也可以相加减,但是timedelta无法指定月和年
""" # 另外我们指定间隔的时候是可以无视范围的,比如一个月最多有31天,但是我们指定45也是可以的
# 比如:dt1是2018年12月11,那么加上45天。会先拿出21天变成2019年1月1号,因为12月有31天
# 然后剩余24天,2019-1-1再加上24天,所以是2019年1月25号
diff = relativedelta(days=45)
print(dt1 + diff) # 2019-01-25 19:15:25

所以relativedelta最大的用处就是能够给一个日期加上指定的时间间隔。

关于python3.8的一些新特性的解析与代码演示的更多相关文章

  1. Python3.x 常用的新特性

    Python3.x 常用的新特性 print() 是函数,不是一个语句 raw_input()输入函数,改为 input() Python 3 对文本和二进制数据做了更为清晰的区分. 文本由unico ...

  2. java8新特性全面解析

    在Java Code Geeks上有大量的关于Java 8 的教程了,像玩转Java 8--lambda与并发,Java 8 Date Time API 教程: LocalDateTime和在Java ...

  3. JAVA 7新特性——在单个catch代码块中捕获多个异常,以及用升级版的类型检查重新抛出异常

    在Java 7中,catch代码块得到了升级,用以在单个catch块中处理多个异常.如果你要捕获多个异常并且它们包含相似的代码,使用这一特性将会减少代码重复度.下面用一个例子来理解. Java 7之前 ...

  4. Java 18 新特性:使用Java代码启动jwebserver

    前几天分享了Java 18 新特性:简单Web服务器的jwebserver命令行功能. 今天换一种方式,使用Java代码来实现一个静态资源服务器. 详细步骤我录了个视频放到B站了,感兴趣的小伙伴可以点 ...

  5. 你应该使用Python3里的这些新特性

    概述 由于Python2的官方维护期即将结束,越来越多的Python项目从Python2切换到了Python3.可是,在实际的工作中,我发现好多人都是在用Python2的思维去写Python3的代码, ...

  6. Oracle DB 12.2(12cR2)的一个新特性:硬解析失败的SQL语句(需要符合一定条件)打印到alert_sid.log中.

    How to Identify Hard Parse Failures (Doc ID 1353015.1)Bug 16945190 - Diagnostic enhancement to dump ...

  7. Python3中的新特性(3)——代码迁移与2to3

    1.将代码移植到Python2.6 建议任何要将代码移植到Python3的用户首先将代码移植到Python2.6.Python2.6不仅与Python2.5向后兼容,而且支持Python3中的部分新特 ...

  8. c++新特性与boost

    <Boost程序库探秘——深度解析C++准标准库>之试读 前一阵子还看到一篇文章,说C#要重蹈C++的覆辙,这里说的C++的覆辙是什么呢?是指C++语言过于臃肿的功能特性,导致学习人员的流 ...

  9. 返璞归真 asp.net mvc (6) - asp.net mvc 2.0 新特性

    原文:返璞归真 asp.net mvc (6) - asp.net mvc 2.0 新特性 [索引页][源码下载] 返璞归真 asp.net mvc (6) - asp.net mvc 2.0 新特性 ...

随机推荐

  1. 七十七:flask.Restful之flask-Restful参数验证

    flask_restful插件提供了reqparse来做类似WTForms的验证功能来校验数据,add_argument可以指定这个字段的名字.数据类型等1.default:默认值,若没有传入此次参数 ...

  2. 使用命令行方式运行 JMeter 脚本

    For non-interactive testing, you may choose to run JMeter without the GUI. To do so, use the followi ...

  3. jenkins凭证与新建任务

    一.凭证介绍 有许多第三方网站和应用程序可以与 Jenkins 进行交互,例如程序代码仓库,云存储系统和服务等. 此类应用程序的系统管理员可以在应用程序中配置凭证以专供 Jenkins 使用.通常通过 ...

  4. Android测试之查看package和activity名称的方法

    方法一:使用aapt    //aapt是sdk自带的一个工具,在sdk\builds-tools\目录下 1.命令行中切换到aapt.exe目录执行:aapt dump badging + 路径  ...

  5. HNU_团队项目_数据库设计感想_个人感想

    数据库设计感想  个人的一点心得体会 最重要的放在最前面——讨论开会时的123经验 开会前对会议目的及方式要有所考虑: 不要随意无目的开会: 遵守时间,控制会议时间长度: 会议主持人要维持会议只需,有 ...

  6. 【HANA系列】【第四篇】SAP HANA XS使用服务器JavaScript Libraries详解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第四篇]SAP HANA XS ...

  7. 338.比特位计数( Counting Bits)leetcode

    附上:题目地址:https://leetcode-cn.com/problems/counting-bits/submissions/ 1:题目: 给定一个非负整数 num.对于 0 ≤ i ≤ nu ...

  8. DNS_主从搭建

    一.DNS简介 1.DNS DNS是域名系统(Domain Name System)的简称,它是一个将域名和IP相互映射的分布式数据库.有了DNS服务器,我们只需要记录一个网站的域名即可访问,而再也不 ...

  9. 应用安全 - JavaScript - 框架 - Jquery - 漏洞 - 汇总

    jQuery CVE-2019-11358 Date 类型 原型污染 影响范围 CVE-2015-9251  Date 类型跨站 影响范围<jQuery 3.0.0

  10. "a++" 与 "++a" 的区别-演示

    两种表示方法经常容易混淆, 在这里将利用演示程序来揭示两者之间的区别, 演示代码如下 int main() { ; cout << "a=1 " << &q ...