Loading

Python的time和datetime模块详解

基础内容

总览

全文阅读预计需要 25~30 分钟

本文将会带你彻底掌握 Python 中时间的用法,从字符串到时间戳到各种好用的内置数据类型,从 time 模块一路讲到 datetime,带你一起讲穿整个时间模块,让您也能成为时间操控大师

在 python 中时间的转换大概遵循下面的方法,如果您已经是一个老手了可以直接把这张图拷贝走,下面的内容都不用看了

image-20230717140323475

基础知识

时差,时区与标准

以下内容来自:https://zhuanlan.zhihu.com/p/435892095 (略有删改)

如果各位稍微了解一点世界上的事情就会发现,为什么NBA的直播总是在大晚上,其实是因为地球的另一边的时间和我们是不一样的,于是就有一个时区的概念,当前中国处于东八区上,全国统一使用北京时间

img

UTC,GMT,Unix 时间戳 全球统一时间

UTC(世界标准时间),GMT(格林威治时间),Unix 时间戳这几个时间虽然计数上不一样,但是这些时间都是全球统一的,使用这些时间方便计算,也能防止程序出现错误,但是对于展示来说不友好

本地时间

因为标准时间没有办法满足需求,所以自然就有本地时间,本地时间就可以通过设置时区的方式自动的将时间转换到当前的时区,比如我们现在的时区通常在英文里面是 Asia/Shanghai

1949年以前,中国一共分了5个时区,以哈尔滨 ( Asia/Harbin)、上海(Asia/Shanghai)、重庆(Asia/Chongqing)、乌鲁木齐(Asia/Urumqi)、喀什(Asia/Kashgar)

从国际标准本身的角度来看,北京和上海处于同一时区,只能保留一个。而作为时区代表上海已经足够具有代表性,因此其维护者没有足够的动力做出改变。

内容来自:https://zhuanlan.zhihu.com/p/450867597 (内容略有删改)

夏令时

目前中国没有夏令时,如果在做国内程序可直接忽略

DST(Daylight Saving Time),夏令时又称夏季时间,或者夏时制

在施行夏令时的国家,一年里面有一天只有23小时(夏令时开始那一天),有一天有25小时(夏令时结束那一天),其他时间每天都是24小时。(绿色部分为2019年统计的在全球施行冬夏令时的国家和地区)

img

感知型对象和简单型对象

感知型就是他多了一个 tzinfo 的属性,这个属性可以自动感知当前的地区,而简单型数据没有这个属性,他就是一个时间对象,可能对现实的操作会存在一点缺陷

另外在python中datetime会切换到当前的地区的时间,这个在 pandas 中就不会,pandas 中需要你手动切换

datetime 和 time 模块

datetime 模块是 time 的再封装,是高级版的 time,其中两个库之间的数据类型能相互转换,在一般的情况下,如果 time 模块能自己解决就不需要再次引入 datetime

  • 我什么时候用 time?
    • 简单的字符串时间比大小
    • 程序里面需要输出当前的时间
    • 对程序计时或者暂停程序
    • 当前是今年的第几天,是星期几
  • 我什么时候用 datetime?
    • 需要对时间类型进行加减(比如增加一天,减少一个小时)
    • 需要计算一个时间有多少秒,或者5900多秒对应多少小时多少分钟
    • 计算当天到某一日的倒计时(比如高考倒计时)

可见 datetime 模块在时间的计算上用的比较多,而想要对时间进行比较和查看使用 time 模块足以

time 模块

time 模块简介

time 模块无疑是用的最多的,其中最常用的便是两个方法

  • time.time() 获取当前的时间戳,这样就可以计算程序的运行时间
    • 其实使用time.perf_counter() 更加准确
  • time.sleep(int) 通过这个方法可以睡眠

现在一个新的需求被提出了,我们需要让程序在字符串类型和 time 提供的类型里面进行转换

首先需要了解在 time 里面的数据结构,在 python 的 time 模块里面,一共有三种可用的数据结构可供解析

  • timestamp 时间戳
  • struct_time
  • 字符串

其中可能大多数人很少会用到 struct_time,然后 timestamp 会用到的多一点,因为很多人都会用 time.time()

time 模块后期也可以和 datetime 进行沟通或者协作,不过他们之间需要使用字符串类型或者使用 timestamp 类型进行转换,不过更多的时候都是使用 timestamp,主要是因为字符串转换确实有点麻烦(相对而言)

timestamp 时间戳

时间戳是一个概念,它是指自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间的秒数。

这个时间是全球都一样的,不存在时区的问题

# 创建一个时间错
print(time.time()) # 输出 1689649979.1072056

这个时候如果有两个 time.time() 就可以用来对程序的运行时间计时,但是这里不讨论计时的问题,主要来讨论这些东西如何进行转换

struct_time 结构化时间

struct_time 可以更加清晰的对时间进行转换,让他成为程序能够轻易理解的形式,其中包含当前是今年的第几天,以及当前是星期几,同时也可以将当前的年月或者具体的时间转换成整数类型,方便后期进行计算

# 创建一个时间错
today = time.localtime(time.time()) # 可以默认省略,如果省略的话,就是当前的时间

print(today)
# 输出
# time.struct_time(tm_year=2023, tm_mon=7, tm_mday=18, tm_hour=11, tm_min=28, tm_sec=39, tm_wday=1, tm_yday=199, tm_isdst=0)

在Python中,struct_time是一个元组,包含了9个元素,分别表示年、月、日、时、分、秒、一周中的第几天、一年中的第几天和夏令时标志。struct_time没有方法,但是可以通过属性来访问元组中的元素。以下是struct_time中的属性:

  • tm_year:年份,如2021
  • tm_mon:月份,范围为1~12
  • tm_mday:日期,范围为1~31
  • tm_hour:小时,范围为0~23
  • tm_min:分钟,范围为0~59
  • tm_sec:秒数,范围为0~61(60和61分别用于闰秒)
  • tm_wday:一周中的第几天,范围为0~6,0表示周一,1表示周二,以此类推
  • tm_yday:一年中的第几天,范围为1~366
  • tm_isdst:夏令时标志,0表示不是夏令时,1表示是夏令时,-1表示未知

可以通过以下方式访问struct_time中的属性:

import time

# 获取当前时间的时间元组
today = time.localtime()

# 访问时间元组中的属性
print(today.tm_year)
print(today.tm_mon)
print(today.tm_mday)
print(today.tm_hour)
print(today.tm_min)
print(today.tm_sec)
print(today.tm_wday)
print(today.tm_yday)
print(today.tm_isdst)

输出结果

2021
10
14
15
14
47
3
287
0

str format time字符串类型

在 python 里面有两个方法

  • 一种 strptime 是 str prase time 的缩写,就是将字符串解析成时间
  • 还有一种是 strftime 是 str format time的缩写,是将时间格式化成字符串

相比于前面两种,字符串类型的数据是人类容易理解的数据类型,这个数据类型看起来像是这个样子

2023-7-18
2023/7/18
2023 7 18
2023 07 18
20230718

这样由字符显示在电脑里面的都是字符串类型,程序没有办法直接对字符串类型的数据进行操作,他们通常还有其他的干扰项目,我们需要对他进行格式化,从其中提取需要的数据

time 数据类型的相互转换

timestamp 转 struct_time

timestamp 没有办法很好的对日期进行显示,这个时候就需要使用到 struct_time,struct_time虽然没有办法对日期进行加减,但是可以更加方便的显示出当前的时间,然后可以让程序员在这个的基础上进行输出

# 创建一个时间错
today = time.localtime(time.time())

print(today)

# 获取当前是今年的第几天
print(today.tm_yday)

# 今天星期几
print(today.tm_wday)

输出结果

time.struct_time(tm_year=2023, tm_mon=7, tm_mday=18, tm_hour=11, tm_min=28, tm_sec=39, tm_wday=1, tm_yday=199, tm_isdst=0)
199
1

可见当前的输出结果,把数值都可视化出来了,当前时间对应的日期,年份,小时,以及一些其他的东西(今天星期几),这样就没必要再去写程序或者写判断,当前是今年的第几天,或者今天是星期几

struct_time 转 timestamp

想要将 struct_time 转换成 timestamp 需要使用 time.mktime() 函数

# 将struct_time转换为时间戳
struct_time = time.struct_time((2023, 1, 1, 0, 0, 0, 0, 1, 0)) # 2023年1月1日
print(struct_time)
print(time.mktime(struct_time))

输出结果

time.struct_time(tm_year=2023, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=1, tm_isdst=0)
1672502400.0

struct_time 转 字符串

将结构化数据 struct_time 转换成字符串需要用到指令 time.asctime()

struct_time = time.struct_time((2023, 7, 18, 0, 0, 0, 0, 1, 0))
print(time.asctime(struct_time))

输出结果

Mon Jul 18 00:00:00 2023

字符串 转 struct_time

这个需要去记忆一张表格,下面的这张表格并不完整,是经过删减之后留下的常用的一些方法

格式 含义
%Y 四位数的年份表示,0000-9999
%m 月份,01-12
%d 日期,0-31
%H 24小时制小时数,0-23
%M 分钟,00-59
%S 秒,00-59
%% %符合,因为被当做转义字符

由此可见,如果有一个时间是这样的

2023 - 7 - 18 17:19:20
%Y - %m - %d %H:%M:%S

其中的空格和特殊符号要严格对应才能匹配上,比如下面这样

time_str1 = "2023-7-18"
time_str2 = "2023/7/18"
time_str3 = "2023 7 18"
time_str4 = "2023 07 18"
time_str5 = "20230718"
time_str6 = '2023年7月18日'

print(time.strptime(time_str1, "%Y-%m-%d"))
print(time.strptime(time_str2, "%Y/%m/%d"))
print(time.strptime(time_str3, "%Y %m %d"))
print(time.strptime(time_str4, "%Y %m %d"))
print(time.strptime(time_str5, "%Y%m%d"))
print(time.strptime(time_str6, "%Y年%m月%d日"))

输出结果

time.struct_time(tm_year=2023, tm_mon=7, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=199, tm_isdst=-1)
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=199, tm_isdst=-1)
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=199, tm_isdst=-1)
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=199, tm_isdst=-1)
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=199, tm_isdst=-1)
time.struct_time(tm_year=2023, tm_mon=7, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=199, tm_isdst=-1)

表格的更多内容

格式 含义
%y 两位数的年份表示,00-99
%Y 四位数的年份表示,0000-9999
%m 月份,01-12
%d 日期,0-31
%H 24小时制小时数,0-23
%I 12小时制小时数,01-12
%M 分钟,00-59
%S 秒,00-59
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地日期表示和时间表示
%j 第几天,001-366
%p 本地A.M\P.M
%U 第几周,00-53,星期天为星期的开始
%W 第几周,00-53,星期一为星期的开始
%w 星期几,0-6,星期天为星期的开始
%x 本地日期
%X 本地时间
%Z 当前时区的名称
%% %符合,因为被当做转义字符

datetime 模块

在 datetime 模块里面同样也别有洞天,刚刚我们在 time 模块里面想要对时间进行操作是一件十分困难的事情,比如我想让时间增加 1天6个小时3分钟,请问阁下应该如何应对,可能这个时候就有人跑去算秒数然后加到时间戳里面了,当然这样也是一种方法,但是可读性很差,而且也不方便修改,这个时候就要请出 datetime 模块了

result = datetime.datetime.now() + datetime.timedelta(days=1, hours=6, minutes=3)
print(datetime.datetime.now())
print(result)
print(type(result))

输出结果

2023-07-18 17:38:41.573526
2023-07-19 23:41:41.573526
<class 'datetime.datetime'>

datetime.time 记录时间

这个方法专门用来记录时间,通过输入3个数字就可以快速定义一个时间

print(datetime.time(12, 20, 30))
# 输出 12:20:30

该方法可以使用比较运算符对时间进行比较

print(datetime.time(12, 20, 30) > datetime.time(12, 20, 29))
# 输出结果 True

但是没有办法相加或者相减,如果想要修改其中的值可以通过 replace 的方法对其中的值进行替换

# 仅替换小时
print(datetime.time(12, 20, 30).replace(hour=8))

# 全部替换
print(datetime.time(12, 20, 30).replace(hour=8, minute=10, second=20))

输出结果

08:20:30
08:10:20

datetime.date 记录日期

创建一个 datetime.date

有记录时间的自然也有记录日期的

print(datetime.date(2023, 7, 18))
# 输出 2023-07-18

# 使用 today 来创建(该方法默认隐藏时间)
today = datetime.date.today()
# 输出 2023-07-18

date 的加减法

时间datetime.time不一样的地方在这个方法他是可以使用加减法的,相比起datetime.time 来说,date自由了很多,对于数值的计算

纯datetime.date只能用减法,想要加法得使用datetime.timedelta

print(datetime.date(2023, 7, 18) - datetime.date(2023, 1, 1))
# 输出 198 days, 0:00:00

除了能用减法之外,还可以使用 datetime.timedelta() 来进行加减和其他操作,这个 datetime.timedelta() 之后会说

print(datetime.date(2023, 7, 18) + datetime.timedelta(days=1)) # 这里加一天时间
# 输出结果 2023-07-19

更多方法

点进 date 这个类里面就会发现他还有很多有意思的方法

# 通过时间戳创建一个 date
timestamp = time.time()
today = datetime.date.fromtimestamp(timestamp)

# 获取当前日期是星期几
print(f'今天是:星期{today.weekday()}')

# 格式化说出
print(f'今天是:{today.strftime("%Y年%m月%d日")}')

输出结果

今天是:星期1
今天是:2023年07月18日

datetime.datetime 记录具体的时间

datetime.datetime.now() 应该是新手朋友接触这个库第一个用到的方法,这个方法会返回当前的时间,不过用的是 datetime 格式,至于为什么放在最后讲,是因为这个方法其实就是上面两个方法的总和,在源代码里赫然写着下面的代码,所以我先讲上面那两个

__slots__ = date.__slots__ + time.__slots__

创建一个 datetime.datetime

# 使用 now 来创建当前的时间
print(datetime.datetime.now())

# 使用时间戳来创建
print(datetime.datetime.fromtimestamp(time.time()))

# 指定时间来创建
print(datetime.datetime(2008, 1, 1, 12, 0, 0))

# 使用字符串来直接创建 datetime(该方法本质上是调用 time.strptime())
print(datetime.datetime.strptime('2023-7-18', '%Y-%m-%d'))

# 用 datetime.date 和 datetime.time 来创建(前面放日期,后面放时间)
print(datetime.datetime.combine(datetime.date(2008, 1, 1), datetime.time(12, 0, 0)))

输出结果

2023-07-18 22:34:12.606550
2023-07-18 22:34:12.606550
2008-01-01 12:00:00
2023-07-18 00:00:00
2008-01-01 12:00:00

拆分一个 datetime.datetime

有合并自然也有拆分,通过直接获取他的属性的方式来拆分一个 datetime.datetime

my_datetime = datetime.datetime(2008, 1, 1, 12, 0, 0)
print(my_datetime)

# 获取他的日期
print(my_datetime.date())

# 获取他的时间
print(my_datetime.time())

# 获取他的小时数
print(my_datetime.hour)

输出结果

2008-01-01 12:00:00
2008-01-01
12:00:00
12

datetime.timedelta 时间计算

对于时间的加减官方提供了一种非常便捷的方式,就是使用 datetime.timedelta,这种方法可以让我们创建一个时间的概念,比如一天,让某一个日期加上一整天,而不需要我们手动去算一天有多少秒,然后自己慢慢加

# 创建一个 timedelta 对象
one_day = datetime.timedelta(days=1)
print(one_day)

# 明天
tomorrow = datetime.datetime.now() + one_day
print(f'明天是:{tomorrow.strftime("%Y年%m月%d日")}')

输出结果

1 day, 0:00:00
明天是:2023年07月19日

秒与时间的转换

在许多的需求中可能会出现下面的一些要求,比如我需要休眠30分钟,或者可能是需要休眠1分30秒,这些当然可以直接自己算,然后丢进 time.sleep 里面,但是同样的也有更加简便的方法

# 将时间长度转换为秒(总秒数 total_seconds())
print(datetime.timedelta(seconds=9530).total_seconds())

# 将秒转换为时间长度
print(datetime.timedelta(seconds=9530))

# 将时间长度转换为天
print(datetime.timedelta(seconds=9530).days)

输出结果

9530.0
0
2:38:50

四则运算

在 timedelta 不仅仅可以计算加减,甚至还能计算乘法和除法

# 创建一个 timedelta 对象
long_time = datetime.timedelta(days=1, hours=6) * 5
print(long_time)

long_time = long_time / 3
print(long_time)

long_time = long_time + datetime.timedelta(weeks=1)
print(long_time)

输出结果

6 days, 6:00:00
2 days, 2:00:00
9 days, 2:00:00

更多运算

其中的 t 代表datetime.timedelta

运算 结果
t1 = t2 + t3 t2t3 的和。 运算后 t1-t2 == t3 and t1-t3 == t2 必为真值。(1)
t1 = t2 - t3 t2t3 的差。 运算后 t1 == t2 - t3 and t2 == t1 + t3 必为真值。 (1)(6)
t1 = t2 * i or t1 = i * t2 乘以一个整数。运算后假如 i != 0t1 // i == t2 必为真值。
In general, t1 * i == t1 * (i-1) + t1 is true. (1)
t1 = t2 * f or t1 = f * t2 乘以一个浮点数,结果会被舍入到 timedelta 最接近的整数倍。 精度使用四舍五偶入奇不入规则。
f = t2 / t3 总时间 t2 除以间隔单位 t3 (3)。 返回一个 float 对象。
t1 = t2 / f or t1 = t2 / i 除以一个浮点数或整数。 结果会被舍入到 timedelta 最接近的整数倍。 精度使用四舍五偶入奇不入规则。
t1 = t2 // i or t1 = t2 // t3 计算底数,其余部分(如果有)将被丢弃。在第二种情况下,将返回整数。 (3)
t1 = t2 % t3 余数为一个 timedelta 对象。(3)
q, r = divmod(t1, t2) 通过 : q = t1 // t2 (3) and r = t1 % t2 计算出商和余数。q是一个整数,r是一个 timedelta 对象。
+t1 返回一个相同数值的 timedelta 对象。
-t1 等价于 timedelta(-t1.days, -t1.seconds, -t1.microseconds), 和 t1* -1. (1)(4)
abs(t) t.days >= 0``时等于 +\ *t*, 当 ``t.days < 0 时 -t 。 (2)
str(t) 返回一个形如 [D day[s], ][H]H:MM:SS[.UUUUUU] 的字符串,当 t 为负数的时候, D 也为负数。 (5)
repr(t) 返回一个 timedelta 对象的字符串表示形式,作为附带正规属性值的构造器调用。

timedelta 和 datetime

在 datetime.datetime 里面可以直接使用减法将两个 datetime.datetime 相减,或者可以用一个 datetime.datetime 减去一个 datetime.datetime

# 创建一个 timedelta 对象
today = datetime.datetime(2023, 7, 18, 12, 30, 0)
print(today)

# 如果没有传入时间, 默认是 0 点
print(today - datetime.datetime(2023, 6, 7)) # 高考的日子

输出结果

2023-07-18 12:30:00
41 days, 12:30:00

不过更多的时候都是 datetime.datetime 加减一个 datetime.timedelta,因为 timedelta 相比起 datetime.datetime 更加方便

posted @ 2023-07-20 00:52  271374667  阅读(235)  评论(0编辑  收藏  举报