python模块之时间模块

[TOC]

参考

time模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> print(time.__doc__)

Functions:

time() -- return current time in seconds since the Epoch as a float
clock() -- return CPU time since process start as a float
sleep() -- delay for a number of seconds given as a float
gmtime() -- convert seconds since Epoch to UTC tuple
localtime() -- convert seconds since Epoch to local time tuple
asctime() -- convert time tuple to string
ctime() -- convert time in seconds to string
mktime() -- convert local time tuple to seconds since Epoch
strftime() -- convert time tuple to string according to format specification
strptime() -- parse string to time tuple according to format specification
tzset() -- change the local timezone

获取时间戳

1
2
3
4
>>> time.time()
1552827527.447389
>>> int(time.time())
1552827538

停留x秒

1
2
3
4
5
>>> print time.sleep.__doc__
sleep(seconds)

Delay execution for a given number of seconds. The argument may be
a floating point number for subsecond precision.

time与字符串互转

1
2
3
4
5
6
>>> print time.strftime.__doc__
strftime(format[, tuple]) -> string

Convert a time tuple to a string according to a format specification.
See the library reference manual for formatting codes. When the time tuple
is not present, current time as returned by localtime() is used.
1
2
3
4
5
>>> print time.strptime.__doc__
strptime(string, format) -> struct_time

Parse a string to a time tuple according to a format specification.
See the library reference manual for formatting codes (same as strftime()).
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python3

import time

# 格式化成2016-03-20 11:45:39形式
print (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))

# 格式化成Sat Mar 28 22:24:24 2016形式
print (time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()))

# 将格式字符串转换为时间戳
a = "Sat Mar 28 22:24:24 2016"
print (time.mktime(time.strptime(a,"%a %b %d %H:%M:%S %Y")))

datetime模块

获取当前日期和时间

1
2
3
4
5
6
>>> from datetime import datetime
>>> now = datetime.now() # 获取当前datetime
>>> print(now)
2015-05-18 16:28:07.198690
>>> print(type(now))
<class 'datetime.datetime'>

注意到datetime是模块,datetime模块还包含一个datetime类,通过from datetime import datetime导入的才是datetime这个类。

如果仅导入import datetime,则必须引用全名datetime.datetime

datetime.now()返回当前日期和时间,其类型是datetime

获取指定日期和时间

要指定某个日期和时间,我们直接用参数构造一个datetime

1
2
3
4
>>> from datetime import datetime
>>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
>>> print(dt)
2015-04-19 12:20:00

datetime转换为timestamp

在计算机中,时间实际上是用数字表示的。我们把1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time,记为0(1970年以前的时间timestamp为负数),当前时间就是相对于epoch time的秒数,称为timestamp。

你可以认为:

1
timestamp = 0 = 1970-1-1 00:00:00 UTC+0:00

对应的北京时间是:

1
timestamp = 0 = 1970-1-1 08:00:00 UTC+8:00

可见timestamp的值与时区毫无关系,因为timestamp一旦确定,其UTC时间就确定了,转换到任意时区的时间也是完全确定的,这就是为什么计算机存储的当前时间是以timestamp表示的,因为全球各地的计算机在任意时刻的timestamp都是完全相同的(假定时间已校准)。

把一个datetime类型转换为timestamp只需要简单调用timestamp()方法:

1
2
3
4
>>> from datetime import datetime
>>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
>>> dt.timestamp() # 把datetime转换为timestamp
1429417200.0

注意Python的timestamp是一个浮点数。如果有小数位,小数位表示毫秒数。

某些编程语言(如Java和JavaScript)的timestamp使用整数表示毫秒数,这种情况下只需要把timestamp除以1000就得到Python的浮点表示方法。

timestamp转换为datetime

要把timestamp转换为datetime,使用datetime提供的fromtimestamp()方法:

1
2
3
4
>>> from datetime import datetime
>>> t = 1429417200.0
>>> print(datetime.fromtimestamp(t))
2015-04-19 12:20:00

注意到timestamp是一个浮点数,它没有时区的概念,而datetime是有时区的。上述转换是在timestamp和本地时间做转换。

本地时间是指当前操作系统设定的时区。例如北京时区是东8区,则本地时间:

1
2015-04-19 12:20:00

实际上就是UTC+8:00时区的时间:

1
2015-04-19 12:20:00 UTC+8:00

而此刻的格林威治标准时间与北京时间差了8小时,也就是UTC+0:00时区的时间应该是:

1
2015-04-19 04:20:00 UTC+0:00

timestamp也可以直接被转换到UTC标准时区的时间:

1
2
3
4
5
6
>>> from datetime import datetime
>>> t = 1429417200.0
>>> print(datetime.fromtimestamp(t)) # 本地时间
2015-04-19 12:20:00
>>> print(datetime.utcfromtimestamp(t)) # UTC时间
2015-04-19 04:20:00

str转换为datetime

很多时候,用户输入的日期和时间是字符串,要处理日期和时间,首先必须把str转换为datetime。转换方法是通过datetime.strptime()实现,需要一个日期和时间的格式化字符串:

1
2
3
4
>>> from datetime import datetime
>>> cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
>>> print(cday)
2015-06-01 18:19:59

字符串'%Y-%m-%d %H:%M:%S'规定了日期和时间部分的格式。详细的说明请参考Python文档

注意转换后的datetime是没有时区信息的。

datetime转换为str

如果已经有了datetime对象,要把它格式化为字符串显示给用户,就需要转换为str,转换方法是通过strftime()实现的,同样需要一个日期和时间的格式化字符串:

1
2
3
4
>>> from datetime import datetime
>>> now = datetime.now()
>>> print(now.strftime('%a, %b %d %H:%M'))
Mon, May 05 16:28

datetime加减

对日期和时间进行加减实际上就是把datetime往后或往前计算,得到新的datetime。加减可以直接用+-运算符,不过需要导入timedelta这个类:

1
2
3
4
5
6
7
8
9
10
>>> from datetime import datetime, timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 5, 18, 16, 57, 3, 540997)
>>> now + timedelta(hours=10)
datetime.datetime(2015, 5, 19, 2, 57, 3, 540997)
>>> now - timedelta(days=1)
datetime.datetime(2015, 5, 17, 16, 57, 3, 540997)
>>> now + timedelta(days=2, hours=12)
datetime.datetime(2015, 5, 21, 4, 57, 3, 540997)

可见,使用timedelta你可以很容易地算出前几天和后几天的时刻。

本地时间转换为UTC时间

本地时间是指系统设定时区的时间,例如北京时间是UTC+8:00时区的时间,而UTC时间指UTC+0:00时区的时间。

一个datetime类型有一个时区属性tzinfo,但是默认为None,所以无法区分这个datetime到底是哪个时区,除非强行给datetime设置一个时区:

1
2
3
4
5
6
7
8
>>> from datetime import datetime, timedelta, timezone
>>> tz_utc_8 = timezone(timedelta(hours=8)) # 创建时区UTC+8:00
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 5, 18, 17, 2, 10, 871012)
>>> dt = now.replace(tzinfo=tz_utc_8) # 强制设置为UTC+8:00
>>> dt
datetime.datetime(2015, 5, 18, 17, 2, 10, 871012, tzinfo=datetime.timezone(datetime.timedelta(0, 28800)))

如果系统时区恰好是UTC+8:00,那么上述代码就是正确的,否则,不能强制设置为UTC+8:00时区。

时区转换

我们可以先通过utcnow()拿到当前的UTC时间,再转换为任意时区的时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 拿到UTC时间,并强制设置时区为UTC+0:00:
>>> utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
>>> print(utc_dt)
2015-05-18 09:05:12.377316+00:00
# astimezone()将转换时区为北京时间:
>>> bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
>>> print(bj_dt)
2015-05-18 17:05:12.377316+08:00
# astimezone()将转换时区为东京时间:
>>> tokyo_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt)
2015-05-18 18:05:12.377316+09:00
# astimezone()将bj_dt转换时区为东京时间:
>>> tokyo_dt2 = bj_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt2)
2015-05-18 18:05:12.377316+09:00

时区转换的关键在于,拿到一个datetime时,要获知其正确的时区,然后强制设置时区,作为基准时间。

利用带时区的datetime,通过astimezone()方法,可以转换到任意时区。

注:不是必须从UTC+0:00时区转换到其他时区,任何带时区的datetime都可以正确转换,例如上述bj_dttokyo_dt的转换。

小结

datetime表示的时间需要时区信息才能确定一个特定的时间,否则只能视为本地时间。

如果要存储datetime,最佳方法是将其转换为timestamp再存储,因为timestamp的值与时区完全无关。

dateutil模块

安装

1
pip install python-dateutil

parser

parser是根据字符串解析成datetime,字符串可以很随意,可以用时间日期的英文单词,可以用横线、逗号、空格等做分隔符,可以包含时区。
没指定时间默认是0点,没指定日期默认是今天,没指定年份默认是今年。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> from dateutil.parser import parse

>>> parse("2018-10-21")
datetime.datetime(2018, 10, 21, 0, 0)

>>> parse("20181021")
datetime.datetime(2018, 10, 21, 0, 0)

>>> parse("2018/10/21")
datetime.datetime(2018, 10, 21, 0, 0)

>>> parse("10-21")
datetime.datetime(2018, 10, 21, 0, 0)

>>> parse("10/21")
datetime.datetime(2018, 10, 21, 0, 0)

rrule

rrule(self, freq, dtstart=None, interval=1, wkst=None,count=None, until=None, bysetpos=None,bymonth=None, bymonthday=None, byyearday=None, byeaster=None,byweekno=None, byweekday=None, byhour=None, byminute=None, bysecond=None,cache=False)

  • freq:可以理解为单位。可以是 YEARLY, MONTHLY, WEEKLY,DAILY, HOURLY, MINUTELY, SECONDLY。即年月日周时分秒
  • dtstart,until:是开始和结束时间
  • wkst:周开始时间
  • interval:间隔
  • count:指定生成多少个
  • byxxx:指定匹配的周期。比如byweekday=(MO,TU)则只有周一周二的匹配。byweekday可以指定MO,TU,WE,TH,FR,SA,SU。即周一到周日。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
>>> from dateutil import rrule

生成一个连续的日期列表
>>> list(rrule.rrule(rrule.DAILY,dtstart=parse('2018-11-1'),until=parse('2018-11-5')))
[datetime.datetime(2018, 11, 1, 0, 0),
datetime.datetime(2018, 11, 2, 0, 0),
datetime.datetime(2018, 11, 3, 0, 0),
datetime.datetime(2018, 11, 4, 0, 0),
datetime.datetime(2018, 11, 5, 0, 0)]

间隔一天
>>> list(rrule.rrule(rrule.DAILY,interval=2,dtstart=parse('2018-11-1'),until=parse('2018-11-5')))
[datetime.datetime(2018, 11, 1, 0, 0),
datetime.datetime(2018, 11, 3, 0, 0),
datetime.datetime(2018, 11, 5, 0, 0)]

只保留前3个元素
>>> list(rrule.rrule(rrule.DAILY,count=3,dtstart=parse('2018-11-1'),until=parse('2018-11-5')))
[datetime.datetime(2018, 11, 1, 0, 0),
datetime.datetime(2018, 11, 2, 0, 0),
datetime.datetime(2018, 11, 3, 0, 0)]

只要周一的
>>> list(rrule.rrule(rrule.DAILY,byweekday=rrule.MO,dtstart=parse('2018-11-1'),until=parse('2018-11-5')))
[datetime.datetime(2018, 11, 5, 0, 0)]

只要周六和周日的
>>> list(rrule.rrule(rrule.DAILY,byweekday=(rrule.SA,rrule.SU),dtstart=parse('2018-11-1'),until=parse('2018-11-5')))
[datetime.datetime(2018, 11, 3, 0, 0),
datetime.datetime(2018, 11, 4, 0, 0)]

以月为间隔
>>> list(rrule.rrule(rrule.MONTHLY,dtstart=parse('2018-3-15'),until=parse('2018-7-30')))
[datetime.datetime(2018, 3, 15, 0, 0),
datetime.datetime(2018, 4, 15, 0, 0),
datetime.datetime(2018, 5, 15, 0, 0),
datetime.datetime(2018, 6, 15, 0, 0),
datetime.datetime(2018, 7, 15, 0, 0)]

计算时间差

rrule可计算出两个datetime对象间相差的年月日等时间数量

1
2
3
4
5
6
7
两个日期相差10
>>> rrule.rrule(rrule.DAILY,dtstart=parse('2018-11-1'),until=parse('2018-11-10')).count()
10

某个日期到今天相差多少天
>>> rrule.rrule(rrule.DAILY,dtstart=parse('2018-11-1'),until=datetime.date.today()).count()
10

两个日期相差几个月
前一个月为m月,后一个月为n月,当日期不满整月时,差的月数按n-m算,当日期满整月后,差的月数按n-m+1算。
差的年数同月数的情况一样。
例子如下:

1
2
3
4
5
6
7
8
9
>>> rrule.rrule(rrule.MONTHLY,dtstart=parse('2018-3-15'),until=parse('2018-11-10')).count()
8
>>> rrule.rrule(rrule.MONTHLY,dtstart=parse('2018-3-15'),until=parse('2018-11-20')).count()
9

>>> rrule.rrule(rrule.YEARLY,dtstart=parse('2016-3-15'),until=parse('2018-2-10')).count()
2
>>> rrule.rrule(rrule.YEARLY,dtstart=parse('2016-3-15'),until=parse('2018-3-15')).count()
3

技巧

当前时间转文本

无论是timedatetime,哪个模块都可以,具体怎么输出,自行调整格式参数'%Y-%m-%d %H:%M:%S'

%字符 表意 数值范围
%y 年(2位) 00, 01, …, 99
%Y 年(4位) 0001, 0002, …, 2013, 2014, …, 9998, 9999
%m 01, 02, …, 12
%d 01, 02, …, 31
%H 时(24小时制) 00, 01, …, 23
%M 00, 01, …, 59
%S 00, 01, …, 59
%f 毫秒 000000, 000001, …, 999999
%z 时区 (empty), +0000, -0400, +1030, +063415, -030712.345216

这里只列一下我用到的,更多可以看官方文档:
https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior

【注意】:time、datetime两种时间模块虽然都有strftime,但是(格式,时间)参数位置正好相反。

time模块(格式在前,时间在后)

1
2
3
4
5
6
7
import time
t = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
print(type(t),t)

# 输出结果
<class 'str'> 2019-05-25 08:56:45
123456

datetime模块(格式在后,时间在前)

1
2
3
4
5
6
7
import datetime
dt = datetime.datetime.strftime(datetime.datetime.now(),'%Y-%m-%d %H:%M:%S')
print(type(dt),dt)

# 输出结果
<class 'str'> 2019-05-25 08:56:45
123456

文本转日期

常规方法strptime("日期时间文本","文本格式")

制定时间格式,进行解析

1
2
3
4
5
6
import datetime
dt = datetime.datetime.strptime('2019-05-25T07:46:45.743+0000','%Y-%m-%dT%H:%M:%S.%f%z')
print(type(dt),dt)

# 输出结果
<class 'datetime.datetime'> 2019-05-25 07:46:45.743000+00:00

万能方法parse("日期时间文本")

自动解析

1
2
3
4
5
6
from dateutil.parser import parse
dt = parse('2019-05-25 07:46:45')
print(type(dt),dt)

# 输出结果
<class 'datetime.datetime'> 2019-05-25 07:46:45

该方法适用于很多类型时间格式,建议使用前自行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dt = ["2001.07.04 AD at 12:08:56 PDT",
"Wed, 4 Jul 2001 12:08:56 -0700",
"190525","2018-08-06T10:00:00.000Z",
"Wed, Jul 4",
"12:08 PM",
"02001.July.04 AD 12:08 PM",
"20190525083855-0700",
"2001-07-04T12:08:56.235-0700",
"Thu Oct 16 07:13:48 GMT 2014"]

for i in dt:
print(parse(i))

# 输出结果
2001-07-04 12:08:56
2001-07-04 12:08:56-07:00
2025-05-19 00:00:00
2018-08-06 10:00:00+00:00
2019-07-04 00:00:00
2019-05-25 12:08:00
2001-07-04 12:08:00
2019-05-25 08:38:55-07:00
2001-07-04 12:08:56.235000-07:00
2014-10-16 07:13:48+00:00

但是注意,国外日期时间的常用格式和国内不一样。单写19-05-25会被解析成2025年5月19日

1
2
3
4
5
print(parse("19-05-25"))

# 输出结果
2025-05-19 00:00:00
1234

如果是2019-05-25就不会错了

1
2
3
4
print(parse("2019-05-25"))

# 输出结果
2019-05-25 00:00:00

时间戳相关

生成10或13位时间戳(做一些网页爬虫或构造提交时候可能用到)

1
2
3
4
5
6
7
8
9
10
11
import time
t = time.time()
print(type(t),t)
print('10位时间戳:',str(int(t)))
print('13位时间戳:',str(int(t*1000)))

# 输出结果
<class 'float'> 1558750679.0872486
10位时间戳: 1558750679
13位时间戳: 1558750679087
12345678910

时间戳转日期时间(10位或13位通用)

1
2
3
4
5
6
7
import datetime
ts = '1517302458364' # 该值为int或string类型均可
dt1 = datetime.datetime.fromtimestamp(float(ts)/10**(len(str(ts))-10))
print(type(dt1),dt1)

# 输出结果
<class 'datetime.datetime'> 2018-01-30 16:54:18.364000

时间差计算

如果是两个datatime格式的日期,直接计算一下差值即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import datetime
dt1 = datetime.datetime.fromtimestamp(1517302458)
dt2 = datetime.datetime.now()
#print(type(dt1),dt1)
#print(type(dt2),dt2)
td = dt2-dt1
#print(type(td),td)
output = '相差%d天%.1f小时'%(td.days,td.seconds/60/60)
print(output)

# 输出结果
<class 'datetime.datetime'> 2018-01-30 16:54:18
<class 'datetime.datetime'> 2019-12-16 16:18:37.443000
<class 'datetime.timedelta'> 684 days, 23:24:19.443000
相差68423.4小时
123456789101112131415

如果是两个文本格式的日期,用parse()转换成datetime,同样计算差值即可

1
2
3
4
5
6
7
8
import datetime
from dateutil.parser import parse
arr = ['2018-01-30 16:54:18','2019-12-16 16:18:37']
td = parse(arr[1])-parse(arr[0])
print('相差%d天%.1f小时'%(td.days,td.seconds/60/60))

# 输出结果
相差68423.4小时

时区转换

直接获取0时区的datetime,并转化为东8区datetime

1
2
3
4
5
6
7
8
9
from datetime import datetime,timezone,timedelta
dt0 = datetime.utcnow().replace(tzinfo=timezone.utc)
dt8 = dt0.astimezone(timezone(timedelta(hours=8))) # 转换时区到东八区
print('UTC协调世界时 \t%s\nUTC+8北京时间\t%s'%(dt0,dt8))

# 输出结果
UTC协调世界时 2019-05-25 02:48:54.281741+00:00
UTC+8北京时间 2019-05-25 10:48:54.281741+08:00
12345678

如果是想将文本格式的2019-05-25T02:48:54.281741+00:00转化到东8区datetime

1
2
3
4
5
6
7
8
9
10
11
from datetime import datetime,timezone,timedelta
from dateutil.parser import parse
dt0_str = '2019-05-25T02:48:54.281741+00:00'
dt0 = parse(dt0_str)
dt8 = dt0.astimezone(timezone(timedelta(hours=8)))
print(type(dt0),dt0)
print(type(dt8),dt8)

# 输出结果
<class 'datetime.datetime'> 2019-05-25 02:48:54.281741+00:00
<class 'datetime.datetime'> 2019-05-25 10:48:54.281741+08:00

获取常用时间

1
2
3
4
5
6
7
8
9
10
11
from datetime import datetime, timedelta

t1 = datetime.now()

# 获取本月第一天
t2 = datetime(t1.year, t1.month, 1)
print(t2.strftime("%Y-%m-%d"))

# 本月最后一天
t3 = datetime(year=t1.year, month=t1.month+1, day=1) - timedelta(days=1)
print(t3.strftime("%Y-%m-%d"))
--------------------本文结束,感谢您的阅读--------------------