时间序列处理工作中最让人不爽的就是对时区的处理。尤其是夏令时(DST)转变,这是一种最常见的麻烦事。就这一点来说,许多人选择以协调世界时(UTC,它是格林尼治标准时间(Greenwich Mean Time)的接替者,目前已经是国际标准了)来处理时间序列。时区是以 UTC 偏移量的形式表示的。
在 Python 中,时区信息来自第三方库 pytz,它使 Python 可以使用 Olson 数据库(汇编了世界时区信息)。这对历史数据非常重要,这是因为由于各地政府的各种突发奇想,夏令时转变日期(甚至 UTC 偏移量)已经发生过多次改变。
#pandas 包装了 pytz 的功能
import pytz
pytz.common_timezones[225]
'Asia/Bishkek'
tz = pytz.timezone('Asia/Bishkek')
tz
本地化和转换
默认情况下,pandas 中的时间序列是单纯的时区
import pandas as pd
from pandas import Series
import numpy as np
rng = pd.date_range('3/9/2015 9:30', periods = 10, freq = 'D')
ts = Series(np.random.randn(len(rng)), index = rng)
# 其索引的 tz 字段为 None
print(ts.index.tz)
None
##在生成日期范围的时候还可以加上一个时区集
pd.date_range('1/1/2015 9:30',periods=5,freq='D',tz='UTC')
DatetimeIndex(['2015-01-01 09:30:00+00:00', '2015-01-02 09:30:00+00:00',
'2015-01-03 09:30:00+00:00', '2015-01-04 09:30:00+00:00',
'2015-01-05 09:30:00+00:00'],
dtype='datetime64[ns, UTC]', freq='D')
#从单纯到本地化的转换是通过 tz_localize 方法处理的:
ts_utc = ts.tz_localize('UTC')
ts_utc
2015-03-09 09:30:00+00:00 0.772150
2015-03-10 09:30:00+00:00 0.460809
2015-03-11 09:30:00+00:00 -0.298266
2015-03-12 09:30:00+00:00 -0.654774
2015-03-13 09:30:00+00:00 -1.166563
2015-03-14 09:30:00+00:00 -2.282594
2015-03-15 09:30:00+00:00 -1.988850
2015-03-16 09:30:00+00:00 0.515585
2015-03-17 09:30:00+00:00 -0.304845
2015-03-18 09:30:00+00:00 0.884675
Freq: D, dtype: float64
ts_utc.index
DatetimeIndex(['2015-03-09 09:30:00+00:00', '2015-03-10 09:30:00+00:00',
'2015-03-11 09:30:00+00:00', '2015-03-12 09:30:00+00:00',
'2015-03-13 09:30:00+00:00', '2015-03-14 09:30:00+00:00',
'2015-03-15 09:30:00+00:00', '2015-03-16 09:30:00+00:00',
'2015-03-17 09:30:00+00:00', '2015-03-18 09:30:00+00:00'],
dtype='datetime64[ns, UTC]', freq='D')
#转为特定时
ts_utc.tz_convert('Asia/Chongqing')
2015-03-09 17:30:00+08:00 0.772150
2015-03-10 17:30:00+08:00 0.460809
2015-03-11 17:30:00+08:00 -0.298266
2015-03-12 17:30:00+08:00 -0.654774
2015-03-13 17:30:00+08:00 -1.166563
2015-03-14 17:30:00+08:00 -2.282594
2015-03-15 17:30:00+08:00 -1.988850
2015-03-16 17:30:00+08:00 0.515585
2015-03-17 17:30:00+08:00 -0.304845
2015-03-18 17:30:00+08:00 0.884675
Freq: D, dtype: float64
操作时区意识型 Timestamp 对象
跟时间序列和日期范围差不多,Timestamp 对象也能被从单纯型本地化为时区意识型(time zone-aware)
stamp = pd.Timestamp('2019-08-12 04:00')
stamp_utc = stamp.tz_localize('utc')
stamp_utc.tz_convert('Asia/Shanghai')
Timestamp('2019-08-12 12:00:00+0800', tz='Asia/Shanghai')
在创建 Timestamp 时,还可以传入一个时区信息:
stamp_moscow=pd.Timestamp('2015-08-12 04:00',tz='Europe/Moscow')
stamp_moscow
Timestamp('2015-08-12 04:00:00+0300', tz='Europe/Moscow')
时区意识型 Timestamp 对象在内部保存了一个 UTC 时间戳值(自 UNIX 纪元(1970年1月1日)算起的纳秒数)。这个 UTC 值在时区转换过程中是不会发生变化的:
stamp_utc.value
1565582400000000000
stamp_utc.tz_convert('US/Eastern').value
1565582400000000000
#反转
docker = pd.read_csv('../data/platform/dcos_docker.csv')
docker
可以转为可以理解的时间
import datetime
docker['datetime'] = docker['timestamp'].map( lambda x : datetime.datetime.fromtimestamp(x / 1000) )
docker['datetime']
0 2020-04-11 00:00:06
1 2020-04-11 00:00:17
2 2020-04-11 00:00:02
3 2020-04-11 00:00:23
4 2020-04-11 00:00:28
...
25582 2020-04-11 05:56:56
25583 2020-04-11 05:59:35
25584 2020-04-11 05:58:03
25585 2020-04-11 05:57:37
25586 2020-04-11 05:56:37
Name: datetime, Length: 25587, dtype: datetime64[ns]
不同时区之间的运算
如果两个时间序列的时区不同,在将它们合并到一起时,最终结果就会是 UTC。由于时间戳其实是以 UTC 存储的,所以这是一个很简单的运算,并不需要发生任何转换:
rng = pd.date_range('3/7/2015 9:30', periods=10,freq='B')
ts = Series(np.random.randn(len(rng)),index=rng)
ts
2015-03-09 09:30:00 -0.689909
2015-03-10 09:30:00 0.469207
2015-03-11 09:30:00 2.020969
2015-03-12 09:30:00 0.266566
2015-03-13 09:30:00 1.123903
2015-03-16 09:30:00 0.184810
2015-03-17 09:30:00 1.547769
2015-03-18 09:30:00 -1.375980
2015-03-19 09:30:00 -0.450079
2015-03-20 09:30:00 -0.262155
Freq: B, dtype: float64
ts1 = ts[:7].tz_localize('Europe/London')
ts2 = ts1[2:].tz_convert('Europe/Moscow')
result = ts1 + ts2
result.index
DatetimeIndex(['2015-03-09 09:30:00+00:00', '2015-03-10 09:30:00+00:00',
'2015-03-11 09:30:00+00:00', '2015-03-12 09:30:00+00:00',
'2015-03-13 09:30:00+00:00', '2015-03-16 09:30:00+00:00',
'2015-03-17 09:30:00+00:00'],
dtype='datetime64[ns, UTC]', freq='B')