Python关于时区与时间相关的操作汇总

  日常的开发过程中总会与到日期与时间的处理,尤其是最近的2个项目都还会遇到时区问题,个人在开发的过程中也遇到过不同的问题,这里总结一下Python操作日期与时间相关的方案。

《PythonCookBook》中关于日期时间的操作

  这本书中关于日期时间的操作非常值得参考:第三章:数字日期和时间

个人之前总结的博客

      time模块

      datetime模块 

      分割处理存放连续时间字符串的列表

      datetime模块格式化以及去掉前导0的操作说明

      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  

~~~

posted on 2020-09-28 16:53  江湖乄夜雨  阅读(1906)  评论(0编辑  收藏  举报