python| python中日期时间处理

随着得物越做越大越做越强, 国际化的脚步也越来越快. 国际化给程序带来2大类需要处理的问题: **语言** 和 **日期时间**. 这篇主要聊 **日期时间处理**.


## 词汇 abbr


> 很多问题往往只是语言的迷雾, 穿透语言表达的概念和定义, 就能解决很多问题.


- area.地区 i18n.国际化.`zh_CN`.语言代码(iso639).国家代码(iso3166) i10n.本地化

- dt.datetime.日期时间 date.日期 time.时间

- `tz`.timezone.时区 UTC.协调世界时=GMT.格林尼治标准时.`gmt` `loc`.localtime.localWallClockTime DST.夏令时.人为

 - CST.中国标准时间=PRC=`Asia/Shanghai`=`+0800`=东八区

 - EST.美国东部标准时间 EDT.美国东部夏令时

- `ts`.timestamp.时间戳

- `fmt`.fromat.日期时间的显示格式


后文遇到相关词汇可以直接对照来看.


## 最佳实践 BestPractice


> The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans. -- https://pypi.org/project/pytz


- 简单概括: 在处理时间问题时, 一直使用 UTC, 只在需要展示时按照对应地区进行处理. gmt✅  loc❌

 - 只使用包含时区的时期时间数据

 - `ts.时间戳` 即 UTC, 需要增加 tz 信息进行 fmt

 - 处理时保持一致性: gmt 归 gmt, loc 归 loc


## py 中相关包与函数


在进入代码之前, 约定 **本地地区** 为 `CST.中国标准时间`.


- 包: time datetime pytz calendar

 - strptime: str-parse-time

 - strftime: str-format-time

- 相关日期时间表示: ts.时间戳 `class time.struct_time`(后面简写为 `st`) `class datetime.datetime`(后面简写为 `dt`) str.fmt

 - ts

   - st>ts: `calendar.timegm(gmt_st) time.mktime(loc_st)`

   - dt>ts: `dt.timestamp()`

 - st

   - ts>st: `gmt_st=time.gmtime() loc_st=time.localtime()`

   - dt>st: `dt.timetuple()`

   - str>st: `time.strptime(str, fmt)`

 - dt

   - ts>dt: `datetime.fromtimestamp(ts, tz=utc) datetime.fromtimestamp(ts, tz=cst`)

     - `datetime.now(tz=utc)`使用当前时间作为ts

     - `datetime.today()` 也使用当前时间作为ts, 但是无时区信息, 不建议使用

   - str>dt: `dt.strptime(str, fmt)`

 - str

   - st>str: `time.strftime(fmt, st)`

   - dt>str: `dt.strftime(fmt)`

- gmt 归 gmt

 - `time.gmtime() calendar.timegm()`

 - `utc=pytz.utc`

- loc 归 loc: 以 CST 为例

 - `time.localtime() time.mktime()`

 - `time.zone=-28800 cst=pytz.timezone('PRC')`

![py中不同时间表示](https://upload-images.jianshu.io/upload_images/567399-cfc90869cf902a3b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


## 关于时期时间的显示 format


查看 formt 文档:


- 相关文档: https://docs.python.org/3.6/library/time.html#time.strftime

- pycharm 在 `strftime()/strptime()` 上按 `F1`


[图片上传失败...(image-90d990-1634529818435)]


简单示例:


```py{cmd=python}

import time


fmt = '%Y-%m-%d %H:%M:%S %Z%z' # 'date time timezone`

s = time.strftime(fmt, time.localtime()) # '2021-10-17 01:11:22 CST+0800'

print(s)

```


## 关于时间戳


- 简单处理, 约定: `ts.时间戳`使用`int`, 精确到`秒`

- 重要的事情再强调一遍: `ts.时间戳` 都是 UTC 下的, 需要时区信息 fmt 成 datetime str


| | -0800 | UTC | CST=+0800 |

| - | - | - | - |

| ts.offset | 28800s | 0 | -28800s |

| 同一时刻ts相等 | ts | ts | ts |

| 相同日期时间的ts | ts+offset | ts+offset | ts+offset |


当前 ts:


```py{cmd=python}

import calendar

import time


ts = int(time.time()) # 1634447629

loc_ts = int(time.mktime(time.localtime())) # 1634447629

print(ts, loc_ts)

```


指定时刻的 ts:


```py{cmd=python}

import calendar

import time


fmt = '%Y-%m-%d %H:%M:%S %Z%z'

st = time.strptime('2021-10-17 01:11:22 CST+0800', fmt)

ts = calendar.timegm(st) # 1634433082

loc_ts = int(time.mktime(st)) # 1634433082

print(ts, loc_ts)

```


## 同一时刻


- 同一个时刻: ts 相等, st/dt 表示的时刻相同, str 根据 fmt 变化


```py{cmd=python}

import calendar

import time

from datetime import datetime


import pytz


utc = pytz.utc  # utc.zone

cst = pytz.timezone('PRC')  # CST.中国标准时间=PRC=Asia/Shanghai


fmt_datetime = '%Y-%m-%d %H:%M:%S'


# 同一个时刻: ts 相等, st/dt 表示的时刻相同, str 根据 fmt 变化

# ts>st

gmt_st = time.gmtime()

loc_st = time.localtime()

# st>ts ts=gmt_ts=loc_ts

ts = int(time.time())

gmt_ts = calendar.timegm(gmt_st)

loc_ts = int(time.mktime(loc_st))

# ts>dt

gmt_dt = datetime.now(tz=utc)

loc_dt = datetime.now(tz=cst)

gmt_dt2 = datetime.today().astimezone(tz=utc)

loc_dt2 = datetime.today().astimezone(tz=cst)

gmt_ts2dt = datetime.fromtimestamp(ts, tz=utc)

loc_ts2dt = datetime.fromtimestamp(ts, tz=cst)

# dt>ts

gmt_dt_ts = int(gmt_dt.timestamp())

loc_dt_ts = int(loc_dt.timestamp())

# dt>st

gmt_dt_st = gmt_dt.timetuple()

loc_dt_st = loc_dt.timetuple()


# fmt_datetime: 不含时区的日期时间字符串

# st/dt>str

gmt_str = time.strftime(fmt_datetime, gmt_st)

loc_str = time.strftime(fmt_datetime, loc_st)

gmt_dt_str = gmt_dt.strftime(fmt_datetime)

loc_dt_str = loc_dt.strftime(fmt_datetime)

# str>st: 无时区, 不建议使用

gmt_str2st = time.strptime(gmt_str, fmt_datetime)

loc_str2st = time.strptime(loc_str, fmt_datetime)

# str>ts

gmt_str2ts = calendar.timegm(time.strptime(gmt_str, fmt_datetime))

loc_str2ts = int(time.mktime(time.strptime(loc_str, fmt_datetime)))

# str>dt

gmt_str2dt = datetime.strptime(loc_str, fmt_datetime).astimezone(tz=utc)  # ✅

gmt_str2dt2 = datetime.strptime(gmt_str, fmt_datetime).astimezone(tz=utc)  # ❌

loc_str2dt = datetime.strptime(loc_str, fmt_datetime).astimezone(tz=cst)

```

![py日期时间: 同一时刻](https://upload-images.jianshu.io/upload_images/567399-d0f24f6eb778ef27.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


## 相同日期时间


- 相同零点时间, 比如 本地时间零点 `2021-10-18 00:00:00`


```py{cmd=py}

import time

from datetime import datetime


import pytz


utc = pytz.utc  # utc.zone

cst = pytz.timezone('PRC')  # CST.中国标准时间=PRC=Asia/Shanghai

fmt_date = '%Y-%m-%d'


# 相同日期时间: 当日零点

# fmt_datetime 相同: 当日零点

ts = int(time.time())

offset = time.timezone

gmt_ts_zero = ts - ts % 86400

loc_ts_zero = gmt_ts_zero + time.timezone

gmt_dt_zero = datetime.fromtimestamp(gmt_ts_zero).astimezone(tz=utc)

loc_dt_zero = datetime.fromtimestamp(loc_ts_zero).astimezone(tz=cst)

gmt_str_zero = time.strftime(fmt_date, time.gmtime(gmt_ts_zero))

loc_str_zero = time.strftime(fmt_date, time.localtime(loc_ts_zero))

loc_dt2str_zero = datetime.now(tz=cst).date()

```


![py 相同零点日期时间](https://upload-images.jianshu.io/upload_images/567399-6aab6adbeefbd32e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



- 相同零点时间: 某日零点时的 date 与 ts


```py{cmd=python}

import calendar

import time


fmt = '%Y-%m-%d %H:%M:%S %Z%z'

fmt_date = '%Y-%m-%d'


st = time.strptime('2021-10-17 01:11:22 CST+0800', fmt)

ts = calendar.timegm(st) # 1634433082

print(ts)


ts = ts - ts % 86400  # '2021-10-17 00:00:00 +0000'

d = time.strftime(fmt_date, time.gmtime(ts))

loc_d = time.strftime(fmt_date, time.localtime(ts))

print(ts, d, loc_d)


offset = time.timezone  # -28800

loc_date_ts = ts - offset  # '2021-10-16 16:00:00 +0000' '2021-10-17 00:00:00 +0800'

print(offset, loc_date_ts)

```


- 任一相同日期时间字符串


```py{cmd=py}

import calendar

import time

from datetime import datetime


import pytz


utc = pytz.utc  # utc.zone

cst = pytz.timezone('PRC')  # CST.中国标准时间=PRC=Asia/Shanghai

s = '2021-10-18 10:57:10'

fmt_datetime = '%Y-%m-%d %H:%M:%S'

st = time.strptime(s, fmt_datetime)  # 无时区信息, 不建议使用

gmt_ts = calendar.timegm(st)

loc_ts = int(time.mktime(st))  # loc_ts=gmt_ts+time.zone

gmt_dt = datetime.strptime(s, fmt_datetime).astimezone(tz=utc)

loc_dt = datetime.strptime(s, fmt_datetime).astimezone(tz=cst)

```

![py 任一相同日期时间](https://upload-images.jianshu.io/upload_images/567399-756fadf9126e3bc5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


## 时区的那些事儿


- py 中使用 pytz 设置时区


```py{cmd=py}

import pytz


utc = pytz.utc  # utc.zone

cst = pytz.timezone('PRC')  # CST.中国标准时间=PRC=Asia/Shanghai

print(pytz.all_timezones)  # 查看时区

```


更详细的 timezone 信息: https://en.wikipedia.org/wiki/Time_zone


- 时间差(秒)转timezone


```py{cmd=py}

import time

from datetime import datetime, timezone, timedelta


seconds = 28800

ts = int(time.time())

tz = timezone(timedelta(seconds=seconds))

dt = datetime.fromtimestamp(ts, tz)

s = dt.strftime('%Y-%m-%d %H:%M:%S')

```

![py 时间差(秒)转timezone](https://upload-images.jianshu.io/upload_images/567399-56e31eebe256176e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


- py 中修改本地时区


```py{cmd=py}

import os

import time


import pytz


cst_off = time.timezone # -28800

os.environ['TZ'] = 'UTC'

time.tzset()

off = time.timezone

print(cst_off, off) # 0

```


## 写在最后


再强调下最佳实践:


- 所有处理都在 UTC 下, 只在需要 fmt 时转成对应时区

- ts 即 UTC, 需要 tz 信息进行 fmt

- 不建议使用不带时区信息的日期时间表示格式(st dt str)

上一篇:万达IBM签订战略合作协议 共推以大数据和认知计算为核心的企业级云服务


下一篇:pytest文档19-doctest测试框架