您可以使用函数tz_localize
来识别Timestamp或DateTimeIndex时区,但是如何做到相反:如何将时区感知时间戳转换为天真时间戳,同时保留其时区?
一个例子:
In [82]: t = pd.date_range(start="2013-05-18 12:00:00", periods=10, freq='s', tz="Europe/Brussels")
In [83]: t
Out[83]:
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: S, Timezone: Europe/Brussels
我可以通过将时区设置为None来删除时区,但结果将转换为UTC(12点钟变为10):
In [86]: t.tz = None
In [87]: t
Out[87]:
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 10:00:00, ..., 2013-05-18 10:00:09]
Length: 10, Freq: S, Timezone: None
我是否有另一种方法可以将DateTimeIndex转换为时区,但保留时区时却设置了?
一些上下文就我问这个问题的原因:我想使用timezone天真的时间序列(以避免时区的额外麻烦,我不需要它们用于我正在处理的情况)。
但出于某种原因,我必须在当地时区(欧洲/布鲁塞尔)处理时区感知的时间序列。由于我所有的其他数据都是时区天真(但在我当地的时区中表示),我想将这个时间序列转换成天真的以便进一步使用它,但它也必须在我的本地时区中表示(所以只需删除时区信息,不将用户可见时间转换为UTC)。
我知道时间实际上是内部存储为UTC,只有当你代表它时才转换为另一个时区,因此当我想要“离域化”时必须进行某种转换。例如,使用python datetime模块,您可以像这样“删除”时区:
In [119]: d = pd.Timestamp("2013-05-18 12:00:00", tz="Europe/Brussels")
In [120]: d
Out[120]: <Timestamp: 2013-05-18 12:00:00+0200 CEST, tz=Europe/Brussels>
In [121]: d.replace(tzinfo=None)
Out[121]: <Timestamp: 2013-05-18 12:00:00>
所以,基于此,我可以做到以下几点,但我认为在使用更大的时间序列时这不会非常有效:
In [124]: t
Out[124]:
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: S, Timezone: Europe/Brussels
In [125]: pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
Out[125]:
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: None, Timezone: None
答案 0 :(得分:69)
要回答我自己的问题,此时此功能已添加到pandas中。从pandas 0.15.0 开始,您可以使用tz_localize(None)
删除导致当地时间的时区。
请参阅whatsnew条目:http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#timezone-handling-improvements
以上我的例子:
In [4]: t = pd.date_range(start="2013-05-18 12:00:00", periods=2, freq='H',
tz= "Europe/Brussels")
In [5]: t
Out[5]: DatetimeIndex(['2013-05-18 12:00:00+02:00', '2013-05-18 13:00:00+02:00'],
dtype='datetime64[ns, Europe/Brussels]', freq='H')
使用tz_localize(None)
删除导致天真的本地时间的时区信息:
In [6]: t.tz_localize(None)
Out[6]: DatetimeIndex(['2013-05-18 12:00:00', '2013-05-18 13:00:00'],
dtype='datetime64[ns]', freq='H')
此外,您还可以使用tz_convert(None)
删除时区信息,但转换为UTC,因此产生天真的UTC时间:
In [7]: t.tz_convert(None)
Out[7]: DatetimeIndex(['2013-05-18 10:00:00', '2013-05-18 11:00:00'],
dtype='datetime64[ns]', freq='H')
这比datetime.replace
解决方案更高效:
In [31]: t = pd.date_range(start="2013-05-18 12:00:00", periods=10000, freq='H',
tz="Europe/Brussels")
In [32]: %timeit t.tz_localize(None)
1000 loops, best of 3: 233 µs per loop
In [33]: %timeit pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
10 loops, best of 3: 99.7 ms per loop
答案 1 :(得分:13)
我认为你无法以比你提出的更有效的方式实现你想要的目标。
潜在的问题是时间戳(如你所知)由两部分组成。表示UTC时间的数据和时区tz_info。在将时区打印到屏幕时,时区信息仅用于显示目的。在显示时,数据被适当地偏移,+ 01:00(或类似)被添加到字符串。剥离tz_info值(使用tz_convert(tz = None))实际上并不会更改表示时间戳的天真部分的数据。
所以,做你想做的事的唯一方法是修改底层数据(pandas不允许这样做...... DatetimeIndex是不可变的 - 请参阅DatetimeIndex上的帮助),或者创建一组新的时间戳对象并将它们包装在新的DatetimeIndex中。你的解决方案是后者:
pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
供参考,以下是replace
的{{1}}方法(请参阅tslib.pyx):
Timestamp
您可以参考def replace(self, **kwds):
return Timestamp(datetime.replace(self, **kwds),
offset=self.offset)
上的文档,看看datetime.datetime
也创建了一个新对象。
如果可以的话,效率的最佳选择是修改数据源,以便(错误地)报告没有时区的时间戳。你提到过:
我想使用时区天真的时间序列(以避免时区的额外麻烦,我不需要它们用于我正在处理的情况)
我很好奇你指的是多么麻烦。我建议作为所有软件开发的一般规则,保留UTC中的时间戳'天真值'。没有比看两个不同的int64值更糟糕的了解它们属于哪个时区。如果您始终始终使用UTC作为内部存储,那么您将避免无数令人头疼的问题。我的口头禅是时区只适用于人类I / O 。
答案 2 :(得分:5)
显式设置索引的tz
属性似乎有效:
ts_utc = ts.tz_convert("UTC")
ts_utc.index.tz = None
答案 3 :(得分:3)
因为我总是想不起来,所以简要总结一下它们的作用:
>>> pd.Timestamp.now() # naive local time
Timestamp('2019-10-07 10:30:19.428748')
>>> pd.Timestamp.utcnow() # tz aware UTC
Timestamp('2019-10-07 08:30:19.428748+0000', tz='UTC')
>>> pd.Timestamp.now(tz='Europe/Brussels') # tz aware local time
Timestamp('2019-10-07 10:30:19.428748+0200', tz='Europe/Brussels')
>>> pd.Timestamp.now(tz='Europe/Brussels').tz_localize(None) # naive local time
Timestamp('2019-10-07 10:30:19.428748')
>>> pd.Timestamp.now(tz='Europe/Brussels').tz_convert(None) # naive UTC
Timestamp('2019-10-07 08:30:19.428748')
>>> pd.Timestamp.utcnow().tz_localize(None) # naive UTC
Timestamp('2019-10-07 08:30:19.428748')
>>> pd.Timestamp.utcnow().tz_convert(None) # naive UTC
Timestamp('2019-10-07 08:30:19.428748')
答案 4 :(得分:2)
最近的贡献,但在Python datetime and pandas give different timestamps for the same date中遇到了类似的情况。
如果您在pandas
中具有可识别时区的日期时间,则从技术上讲,tz_localize(None)
会更改POSIX时间戳(内部使用),就好像时间戳中的本地时间是UTC。在此上下文中,strong> Local 表示在指定时区为 local 。例如:
import pandas as pd
t = pd.date_range(start="2013-05-18 12:00:00", periods=2, freq='H', tz="US/Central")
# DatetimeIndex(['2013-05-18 12:00:00-05:00', '2013-05-18 13:00:00-05:00'], dtype='datetime64[ns, US/Central]', freq='H')
t_loc = t.tz_localize(None)
# DatetimeIndex(['2013-05-18 12:00:00', '2013-05-18 13:00:00'], dtype='datetime64[ns]', freq='H')
# offset in seconds according to timezone:
(t_loc.values-t.values)//1e9
# array([-18000, -18000], dtype='timedelta64[ns]')
请注意,这会使您在DST过渡期间留下奇怪的事情,例如
t = pd.date_range(start="2020-03-08 01:00:00", periods=2, freq='H', tz="US/Central")
(t.values[1]-t.values[0])//1e9
# numpy.timedelta64(3600,'ns')
t_loc = t.tz_localize(None)
(t_loc.values[1]-t_loc.values[0])//1e9
# numpy.timedelta64(7200,'ns')
相比之下,tz_convert(None)
不会修改内部时间戳记,只会删除tzinfo
。
t_utc = t.tz_convert(None)
(t_utc.values-t.values)//1e9
# array([0, 0], dtype='timedelta64[ns]')
我的底线是:如果可以或仅使用t.tz_convert(None)
而不修改基础POSIX时间戳,请坚持使用时区感知日期时间。请记住,那时您实际上正在使用UTC。
(Windows 10上的Python 3.8.2 x64,pandas
v1.0.5。)
答案 5 :(得分:1)
当系列中有多个不同时区时,可接受的解决方案不起作用。它会抛出ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True
解决方案是使用apply
方法。
请参见以下示例:
# Let's have a series `a` with different multiple timezones.
> a
0 2019-10-04 16:30:00+02:00
1 2019-10-07 16:00:00-04:00
2 2019-09-24 08:30:00-07:00
Name: localized, dtype: object
> a.iloc[0]
Timestamp('2019-10-04 16:30:00+0200', tz='Europe/Amsterdam')
# trying the accepted solution
> a.dt.tz_localize(None)
ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True
# Make it tz-naive. This is the solution:
> a.apply(lambda x:x.tz_localize(None))
0 2019-10-04 16:30:00
1 2019-10-07 16:00:00
2 2019-09-24 08:30:00
Name: localized, dtype: datetime64[ns]
# a.tz_convert() also does not work with multiple timezones, but this works:
> a.apply(lambda x:x.tz_convert('America/Los_Angeles'))
0 2019-10-04 07:30:00-07:00
1 2019-10-07 13:00:00-07:00
2 2019-09-24 08:30:00-07:00
Name: localized, dtype: datetime64[ns, America/Los_Angeles]
答案 6 :(得分:0)
建立在D.A.的建议“做你想要的唯一方法是修改基础数据”并使用numpy修改基础数据......
这适合我,而且非常快:
def tz_to_naive(datetime_index):
"""Converts a tz-aware DatetimeIndex into a tz-naive DatetimeIndex,
effectively baking the timezone into the internal representation.
Parameters
----------
datetime_index : pandas.DatetimeIndex, tz-aware
Returns
-------
pandas.DatetimeIndex, tz-naive
"""
# Calculate timezone offset relative to UTC
timestamp = datetime_index[0]
tz_offset = (timestamp.replace(tzinfo=None) -
timestamp.tz_convert('UTC').replace(tzinfo=None))
tz_offset_td64 = np.timedelta64(tz_offset)
# Now convert to naive DatetimeIndex
return pd.DatetimeIndex(datetime_index.values + tz_offset_td64)
答案 7 :(得分:0)
最重要的是定义日期时间对象时添加tzinfo
。
from datetime import datetime, timezone
from tzinfo_examples import HOUR, Eastern
u0 = datetime(2016, 3, 13, 5, tzinfo=timezone.utc)
for i in range(4):
u = u0 + i*HOUR
t = u.astimezone(Eastern)
print(u.time(), 'UTC =', t.time(), t.tzname())