日常的开发过程中总会与到日期与时间的处理,尤其是最近的2个项目都还会遇到时区问题,个人在开发的过程中也遇到过不同的问题,这里总结一下Python操作日期与时间相关的方案。
《PythonCookBook》中关于日期时间的操作
这本书中关于日期时间的操作非常值得参考:第三章:数字日期和时间
个人之前总结的博客
pymysql往数据库中插入datetime类型的"空数据"与MySQL5.7sql_mode的一个问题
关于时区操作的pytz模块 ***
有的时候我们会看到类似"10/Sep/2015:06:47:35"或"2015-9-10T06:47:35"这样的时间字符串。首先要强调的是上面两种表达方式无论哪一种都是错误的表达方式,因为没有时区的时间字符串是没有意义的!!!
整个世界被分为12个时区,从-12~+12,负数代表西时区,而正数代表东时区,以英国的0时区为中心。比如中国的时区可以用+8来表示,更多的使用+08:00或+0800来表示,读作“东八区”。有些时候我们用CST时间表示中国标准时间,或者在某些操作系统中使用’Asia/Shanghai’代表东八区。
所以正确的时间字符串应当是"10/Sep/2015:06:47:35 +0800"或"2015-9-10T06:47:35+0800"的包含时区的格式,如果不是,那么请确认你得到这份包含时间数据的语境以确保知道其时区,因为毕竟不同时区的同一时间实际上是不同的时间。
获取0时区的操作
直接使用内置的datetime模块中的utcnow方法:
使用pytz模块转换时区
使用第三方模块 pytz:
注意1
时区的英文名称,比如这里使用的’Asia/Shanghai',我们如何才能知道其他地区的时区呢?可以通过pytz.all_timezones这个属性获取。
注意2
时间格式化函数strftime()及其参数’%Y-%m-%d %H:%M:%S%z’。
这个函数的意思是将datetime类型的时间格式化成其参数所描述的格式,在上面的程序中很明显地,Y代表年,m代表月,d代表日,H代表小时,M代表分钟,S代表秒,z代表时区。
字符串与时间戳相互转换的arrow模块 ***
将包含时区的时间字符串转换成时间戳 ***
相关的案例代码如下:
import time from datetime import datetime import pytz import arrow tz = pytz.timezone("Asia/Shanghai") dt = datetime.utcnow() print("dt>>>",dt) # dt>>> 2020-09-28 11:05:43.693912 time_now = time.time() print("time_now>>>",time_now) # time_now>>> 1601291143.693969 # 格式化方式1 cst_time1 = tz.fromutc(dt).strftime("%Y-%m-%d %H:%M:%S%z") print("sct_time1>>>",cst_time1) # sct_time1>>> 2020-09-28 19:05:43+0800 arrow1 = arrow.get(cst_time1,"YYYY-MM-DD HH:mm:ssZ").timestamp print("arrow1>>>",arrow1) # arrow1>>> 1601291143 # 格式化方式2 cst_time2 = tz.fromutc(dt).strftime("%d/%b/%Y:%H:%M:%S%z") print("cst_time2>>>",cst_time2) # cst_time2>>> 28/Sep/2020:19:05:43+0800 arrow2 = arrow.get(cst_time2,"DD/MMM/YYYY:HH:mm:ssZ").timestamp print("arrow2>>>",arrow2) # arrow2>>> 1601291143 # 格式化方式3 cst_time3 = tz.fromutc(dt).strftime("%Y-%m-%dT%H:%M:%S %z") print("cst_time3>>>",cst_time3) # cst_time3>>> 2020-09-28T19:05:43 +0800 arrow3 = arrow.get(cst_time3,"YYYY-MM-DDTHH:mm:ss Z").timestamp print("arrow3>>>",arrow3) # arrow3>>> 1601291143 # 格式化方式4 cst_time4 = tz.fromutc(dt).strftime("%c %z") print("cst_time4>>>",cst_time4) # cst_time4>>> Mon Sep 28 19:05:43 2020 +0800 arrow4 = arrow.get(cst_time4,"ddd MMM DD HH:mm:ss YYYY Z").timestamp print("arrow4>>>",arrow4) # arrow4>>> 1601291143
有一点稍稍让人感到困扰,arrow需要的字符串格式化参数与Python标准库datetime的不同,虽然看起来大同小异,不过还是需要稍微熟悉一下。
将时间戳转换成时间字符串 ***
下面的代码分别使用Python标准库和arrow两种方法进行举例:
import time from datetime import datetime import pytz import arrow tz = pytz.timezone("Asia/Shanghai") dt = datetime.utcnow() print("dt>>>",dt) # dt>>> 2020-09-28 11:51:32.814224 time_now = time.time() print("time_now>>>",time_now) # time_now>>> 1601293892.814267 ### python标准库方案 ret1 = tz.fromutc(datetime.utcfromtimestamp(time_now)).strftime("%Y-%m-%d %H:%M:%S %z") print("标准库方案>>>",ret1,type(ret1)) # 标准库方案>>> 2020-09-28 19:51:32 +0800 <class 'str'> ### arrow方案 ret2 = arrow.get(time_now).to(tz).format("YYYY-MM-DD HH:mm:ss Z") print("arrow方案>>>",ret2,type(ret2)) # arrow方案>>> 2020-09-28 19:51:32 +0800 <class 'str'>
~~~
开始的时间是当天的0点的获取方式
# 开始时间是当天的0点 until = datetime.now() now_date_str = until.strftime("%Y-%m-%d") year, month, day = now_date_str.split("-") since = datetime(int(year), int(month), int(day), 00, 00, 00)
使用字符串格式化进行时区转换
实现的方式比较low(0-0),当作是笔记把!!!
注意这种方式是在特定的业务场景下使用的,下面我会详细介绍。
在实际的业务中,我使用SDK获取到的日期的格式是这样的:
"created_time": "2020-09-11T18:42:32+0800"
后面的 +0800 就是“东八区”的意思。我需要将获取到的数据经过结构的构建与逻辑的处理最后存入到MySQL数据库中。
但是数据的日期在数据库中必须按照 UTC0时区 的格式存储(历史原因...),所以我还需要额外的处理一下时区。网上找了很多这方面的资料,但是感觉还是跟自己业务逻辑相差比较远,根据自己的认真观察,发现存入数据库的日期数据其实就比当前获取的时间晚8小时——所以干脆进行timedelta操作就可以了...
下面是实现的demo:
# 业务代码略 ret2 = cam.api_get(fields=fields) updated_time = ret2["updated_time"] print("updated_time>>>>>",updated_time) # updated_time>>>>> 2020-09-23T00:02:02+0800 ### 如果是东8区时间 往后推8小时!!! if "0800" in updated_time: # 按照东8区的格式格式化 print(updated_time, type(updated_time)) # 2020-09-23T00:02:02+0800 <class 'str'> publish_time = datetime.strptime(updated_time, "%Y-%m-%dT%H:%M:%S+0800") # 往后推8小时就是UTC时间 publish_time = publish_time - timedelta(hours=8) # 如果是UTC时间直接处理并写入数据库 elif "0000" in updated_time: # 按照UTC的格式格式化 publish_time = datetime.strptime(updated_time, "%Y-%m-%dT%H:%M:%S+0000") else: raise Exception("时间格式不规范") ### 需要转换成str类型才能写入数据库!!! publish_time = str(publish_time) print("<<<<<",publish_time) # <<<<< 2020-09-22 16:02:02
~~~