Anda dapat menggunakan fungsi ini tz_localize
untuk membuat Timestamp atau DateTimeIndex menjadi sadar, tetapi bagaimana Anda bisa melakukan yang sebaliknya: bagaimana Anda bisa mengubah Timestamp yang sadar zona waktu menjadi yang naif, sambil mempertahankan zona waktunya?
Sebuah contoh:
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
Saya dapat menghapus zona waktu dengan mengaturnya ke Tidak Ada, tetapi kemudian hasilnya diubah ke UTC (pukul 12 menjadi 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
Apakah ada cara lain untuk mengonversi DateTimeIndex menjadi zona waktu naif, tetapi sambil mempertahankan zona waktu yang telah ditetapkan?
Beberapa konteks tentang alasan saya menanyakan ini: Saya ingin bekerja dengan rangkaian waktu naif zona waktu (untuk menghindari kerumitan ekstra dengan zona waktu, dan saya tidak membutuhkannya untuk kasus yang sedang saya tangani).
Tetapi untuk beberapa alasan, saya harus berurusan dengan rangkaian waktu yang sadar zona waktu di zona waktu lokal saya (Eropa / Brussel). Karena semua data saya yang lain adalah zona waktu naif (tetapi terwakili dalam zona waktu lokal saya), saya ingin mengubah rangkaian waktu ini menjadi naif untuk digunakan lebih lanjut, tetapi juga harus diwakili dalam zona waktu lokal saya (jadi hapus saja info zona waktu tersebut, tanpa mengubah waktu yang terlihat pengguna ke UTC).
Saya tahu waktu sebenarnya disimpan secara internal sebagai UTC dan hanya dikonversi ke zona waktu lain jika Anda mewakilinya, jadi harus ada semacam konversi ketika saya ingin "mendelokalisasi" -nya. Misalnya, dengan modul datetime python Anda dapat "menghapus" zona waktu seperti ini:
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>
Jadi, berdasarkan ini, saya dapat melakukan hal berikut, tetapi saya rasa ini tidak akan terlalu efisien saat bekerja dengan rangkaian waktu yang lebih besar:
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
apa yangreplace(tzinfo=None)
dilakukan untuk datetimes, tetapi memang bukan cara yang sangat jelas.Jawaban:
Untuk menjawab pertanyaan saya sendiri, sementara itu fungsi ini telah ditambahkan ke panda. Mulai dari panda 0.15.0 , Anda dapat menggunakan
tz_localize(None)
untuk menghapus zona waktu yang menghasilkan waktu lokal.Lihat entri whatsnew: http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#timezone-handling-improvements
Jadi dengan contoh saya dari atas:
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')
menggunakan
tz_localize(None)
menghapus informasi zona waktu yang mengakibatkan waktu lokal yang naif :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')
Selanjutnya, Anda juga dapat menggunakan
tz_convert(None)
untuk menghapus informasi zona waktu tetapi mengonversi ke UTC, sehingga menghasilkan waktu UTC yang naif :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')
Ini jauh lebih baik daripada
datetime.replace
solusinya: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
sumber
from tzlocal import get_localzone
,tz_here = get_localzone()
,<datetime object>.tz_convert(tz_here).tz_localize(None)
t.dt.tz_localize(None)
ataut.dt.tz_convert(None)
. Perhatikan.dt
.Saya pikir Anda tidak dapat mencapai apa yang Anda inginkan dengan cara yang lebih efisien daripada yang Anda usulkan.
Masalah yang mendasarinya adalah bahwa stempel waktu (seperti yang Anda ketahui) terdiri dari dua bagian. Data yang mewakili waktu UTC, dan zona waktu, tz_info. Informasi zona waktu digunakan hanya untuk tujuan tampilan saat mencetak zona waktu ke layar. Pada waktu tampilan, data diimbangi dengan tepat dan +01: 00 (atau serupa) ditambahkan ke string. Menghapus nilai tz_info (menggunakan tz_convert (tz = None)) sebenarnya tidak mengubah data yang mewakili bagian naif dari stempel waktu.
Jadi, satu-satunya cara untuk melakukan apa yang Anda inginkan adalah mengubah data yang mendasarinya (panda tidak mengizinkan ini ... DatetimeIndex tidak dapat diubah - lihat bantuan di DatetimeIndex), atau untuk membuat kumpulan baru objek stempel waktu dan menggabungkannya di DatetimeIndex baru. Solusi Anda melakukan yang terakhir:
pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
Untuk referensi, berikut adalah
replace
metodeTimestamp
(lihat tslib.pyx):def replace(self, **kwds): return Timestamp(datetime.replace(self, **kwds), offset=self.offset)
Anda dapat merujuk ke dokumen di
datetime.datetime
untuk melihat itudatetime.datetime.replace
juga membuat objek baru.Jika Anda bisa, taruhan terbaik Anda untuk efisiensi adalah memodifikasi sumber data sehingga (secara tidak benar) melaporkan cap waktu tanpa zona waktunya. Anda menyebutkan:
Saya ingin tahu kerumitan ekstra apa yang Anda maksud. Saya sarankan sebagai aturan umum untuk semua pengembangan perangkat lunak, pertahankan 'nilai naif' stempel waktu Anda di UTC. Ada sedikit yang lebih buruk daripada melihat dua nilai int64 yang berbeda bertanya-tanya di zona waktu mana mereka berada. Jika Anda selalu, selalu, selalu menggunakan UTC untuk penyimpanan internal, maka Anda akan terhindar dari sakit kepala yang tak terhitung jumlahnya. Mantra saya adalah Zona Waktu hanya untuk I / O manusia .
sumber
Karena saya selalu kesulitan untuk mengingat, ringkasan singkat tentang apa yang masing-masing lakukan:
>>> 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')
sumber
Menetapkan
tz
atribut indeks secara eksplisit tampaknya berfungsi:ts_utc = ts.tz_convert("UTC") ts_utc.index.tz = None
sumber
tz
ke Tidak Ada juga mengubahnya menjadi UTC.tz_convert
akan menimbulkan kesalahan.Solusi yang diterima tidak berfungsi saat ada beberapa zona waktu berbeda dalam satu Rangkaian. Itu melempar
ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True
Solusinya adalah dengan menggunakan
apply
metode tersebut.Silakan lihat contoh di bawah ini:
# 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]
sumber
Berdasarkan saran DA bahwa " satu-satunya cara untuk melakukan apa yang Anda inginkan adalah mengubah data yang mendasarinya " dan menggunakan numpy untuk mengubah data pokok ...
Ini bekerja untuk saya, dan cukup cepat:
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)
sumber
Kontribusi terlambat tetapi baru saja menemukan sesuatu yang serupa di Python datetime dan panda memberikan stempel waktu yang berbeda untuk tanggal yang sama .
Jika Anda memiliki tanggal waktu sadar zona dalam
pandas
, secara teknis,tz_localize(None)
ubah stempel waktu POSIX (yang digunakan secara internal) seolah-olah waktu lokal dari stempel waktu adalah UTC. Lokal dalam konteks ini berarti lokal dalam zona waktu yang ditentukan . Ex: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]')
Perhatikan bahwa ini akan membuat Anda mengalami hal-hal aneh selama transisi DST , mis
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')
Sebaliknya,
tz_convert(None)
tidak mengubah stempel waktu internal, itu hanya menghapustzinfo
.t_utc = t.tz_convert(None) (t_utc.values-t.values)//1e9 # array([0, 0], dtype='timedelta64[ns]')
Intinya saya adalah: tetap dengan waktu yang sadar zona waktu jika Anda dapat atau hanya menggunakan
t.tz_convert(None)
yang tidak mengubah cap waktu POSIX yang mendasarinya. Ingatlah bahwa Anda secara praktis bekerja dengan UTC.(Python 3.8.2 x64 di Windows 10,
pandas
v1.0.5.)sumber
Yang paling penting adalah menambahkan
tzinfo
saat Anda mendefinisikan objek 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())
sumber