Вы можете использовать эту функцию, tz_localize
чтобы сделать отметку времени или 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 в наивный часовой пояс, но при сохранении часового пояса, в котором он был установлен?
Некоторый контекст по причине, по которой я спрашиваю об этом: я хочу работать с наивными временными рядами часовых поясов (чтобы избежать лишних хлопот с часовыми поясами, и мне они не нужны в случае, над которым я работаю).
Но по какой-то причине мне приходится иметь дело с временными рядами с учетом часового пояса в моем местном часовом поясе (Европа / Брюссель). Поскольку все мои другие данные являются наивными часовыми поясами (но представлены в моем местном часовом поясе), я хочу преобразовать эти временные ряды в наивные для дальнейшей работы с ними, но они также должны быть представлены в моем местном часовом поясе (поэтому просто удалите информацию о часовом поясе, без преобразования времени, видимого пользователем, в 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
replace
.tz_localize
тому, чтоreplace(tzinfo=None)
делает для datetime, но это действительно не очень очевидный способ.Ответы:
Чтобы ответить на мой собственный вопрос, эта функциональность тем временем была добавлена в 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')
using
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
источник
from tzlocal import get_localzone
,tz_here = get_localzone()
,<datetime object>.tz_convert(tz_here).tz_localize(None)
t.dt.tz_localize(None)
илиt.dt.tz_convert(None)
. Обратите внимание на расширение.dt
.Я думаю, вы не сможете добиться желаемого более эффективным способом, чем предлагали.
Основная проблема заключается в том, что метки времени (как вам кажется) состоят из двух частей. Данные, представляющие время в формате 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
методTimestamp
(см. Tslib.pyx):def replace(self, **kwds): return Timestamp(datetime.replace(self, **kwds), offset=self.offset)
Вы можете обратиться к документации,
datetime.datetime
чтобы увидеть, что этоdatetime.datetime.replace
также создает новый объект.По возможности лучше всего для повышения эффективности изменить источник данных, чтобы он (неправильно) сообщал временные метки без их часового пояса. Ты упомянул:
Мне было бы любопытно, о каких дополнительных хлопотах вы говорите. Я рекомендую в качестве общего правила для всех программных разработок сохранять ваши временные метки «наивные значения» в формате UTC. Нет ничего хуже, чем смотреть на два разных значения int64, задаваясь вопросом, к какому часовому поясу они принадлежат. Если вы всегда, всегда всегда используете UTC для внутреннего хранилища, вы избежите бесчисленных головных болей. Моя мантра - часовые пояса предназначены только для ввода-вывода людей .
источник
Поскольку мне всегда трудно вспомнить, краткое изложение того, что делает каждый из них:
>>> 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')
источник
tz
Кажется, что установка атрибута индекса явно работает:ts_utc = ts.tz_convert("UTC") ts_utc.index.tz = None
источник
tz
None также преобразует его в UTC.tz_convert
вызовет ошибку.Принятое решение не работает, когда в серии есть несколько разных часовых поясов. Это бросает
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]
источник
Основываясь на предположении DA о том, что « единственный способ сделать то, что вы хотите, - это изменить базовые данные. » и использовать 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)
источник
Поздний вклад, но только что наткнулся на что-то похожее в Python datetime, а панды дают разные временные метки для одной и той же даты .
Если у вас есть дата- время с учетом часового пояса
pandas
, техническиtz_localize(None)
изменяет метку времени POSIX (которая используется внутри компании), как если бы местное время из метки времени было UTC. Локальный в этом контексте означает локальный в указанном часовом поясе . Пример: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]')
Обратите внимание, что при переходе на летнее время вы будете сталкиваться со странными вещами , например
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]')
Мой итог: придерживайтесь datetime с учетом часового пояса, если вы можете или только используете
t.tz_convert(None)
времени с учетом что не изменяет базовую метку времени POSIX. Просто имейте в виду, что тогда вы практически работаете с UTC.(Python 3.8.2 x64 в Windows 10,
pandas
v1.0.5.)источник
Самое важное - добавить,
tzinfo
когда вы определяете объект datetime.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())
источник