【问题标题】:How to convert & localize naive datetimes (using daylight saving time) to aware datetimes如何将天真的日期时间(使用夏令时)转换和本地化为有意识的日期时间
【发布时间】:2022-09-23 19:03:03
【问题描述】:

我有一个数据框,包括列DateTime,它们是本地时钟读数(作为字符串)。以及dst 列,它指示夏令时是否启用,W 用于冬季,S用于夏季。

我知道时区是Europe/Berlin,导致冬季从 UTC 偏移 1 小时,夏季偏移 2 小时。

我对这种表示非常不满意,并希望转换为 UTC 中的感知日期时间对象,并且只在需要时提供人类可读的时间。

      Date      Time  dst
27.03.2022  01:15:00    W
27.03.2022  01:30:00    W
27.03.2022  01:45:00    W
27.03.2022  03:00:00    S
27.03.2022  03:15:00    S
27.03.2022  03:30:00    S
27.03.2022  03:45:00    S
27.03.2022  04:00:00    S
27.03.2022  04:15:00    S
27.03.2022  04:30:00    S
27.03.2022  04:45:00    S
27.03.2022  05:00:00    S
27.03.2022  05:15:00    S

我的第一种方法是,使用 pandas 检索一个日期时间对象,对其进行本地化并根据给定的 dst 减去两个或一个小时,使用 numpy.

from datetime import datetime, timedelta, timezone
from dateutil import tz

import numpy as np
import pandas as pd

df[\'datetime\'] = pd.to_datetime(df[\'Date\'] + df[\'Time\'], format=\'%d.%m.%Y%H:%M:%S\')
df[\'datetime_aware\'] = df[\'datetime\'].dt.tz_localize(tz=\'Europe/Berlin\')
df[\'datetime_aware_subtracted\'] = np.where(df[\'dst\']==\'S\', df[\'datetime_aware\']-timedelta(hours=2),
                                           df[\'datetime_aware\']-timedelta(hours=1))

这会产生几乎正确的结果,除了在 03:00 - 05:00(datetime)之间,datetime_aware_subtracted 列会产生错误的结果。减去一小时太多 + 一小时时移太少。我觉得在 dst 边界上减去时间不是一个好主意。

        datetime               datetime_aware   datetime_aware_subtracted
27.03.2022 01:15    2022-03-27 01:15:00+01:00   2022-03-27 00:15:00+01:00
27.03.2022 01:30    2022-03-27 01:30:00+01:00   2022-03-27 00:30:00+01:00
27.03.2022 01:45    2022-03-27 01:45:00+01:00   2022-03-27 00:45:00+01:00
27.03.2022 03:00    2022-03-27 03:00:00+02:00   2022-03-27 00:00:00+01:00
27.03.2022 03:15    2022-03-27 03:15:00+02:00   2022-03-27 00:15:00+01:00
27.03.2022 03:30    2022-03-27 03:30:00+02:00   2022-03-27 00:30:00+01:00
27.03.2022 03:45    2022-03-27 03:45:00+02:00   2022-03-27 00:45:00+01:00
27.03.2022 04:00    2022-03-27 04:00:00+02:00   2022-03-27 01:00:00+01:00
27.03.2022 04:15    2022-03-27 04:15:00+02:00   2022-03-27 01:15:00+01:00
27.03.2022 04:30    2022-03-27 04:30:00+02:00   2022-03-27 01:30:00+01:00
27.03.2022 04:45    2022-03-27 04:45:00+02:00   2022-03-27 01:45:00+01:00
27.03.2022 05:00    2022-03-27 05:00:00+02:00   2022-03-27 03:00:00+02:00
27.03.2022 05:15    2022-03-27 05:15:00+02:00   2022-03-27 03:15:00+02:00

我的第二种方法,是逆减法和本地化。

df[\'datetime\'] = pd.to_datetime(df[\'Date\'] + df[\'Time\'], format=\'%d.%m.%Y%H:%M:%S\')
df[\'datetime_subtracted\'] = np.where(df[\'dst\']==\'S\', df[\'datetime\']-timedelta(hours=2),
                                     df[\'datetime\']-timedelta(hours=1))
df[\'datetime_subtracted_aware\'] = df[\'datetime_subtracted\'].dt.tz_localize(tz=\'Europe/Berlin\')

这给出了正确的幼稚结果,但在本地化时减去后给出NonExistentTimeError(理所当然)。

Traceback (most recent call last):
  File \"<stdin>\", line 1, in <module>
  File \"C:\\ProgramData\\Miniconda3\\envs\\env\\lib\\site-packages\\pandas\\core\\accessor.py\", line 94, in f
    return self._delegate_method(name, *args, **kwargs)
  File \"C:\\ProgramData\\Miniconda3\\envs\\env\\lib\\site-packages\\pandas\\core\\indexes\\accessors.py\", line 123, in _delegate_method
    result = method(*args, **kwargs)
  File \"C:\\ProgramData\\Miniconda3\\envs\\env\\lib\\site-packages\\pandas\\core\\indexes\\datetimes.py\", line 273, in tz_localize
    arr = self._data.tz_localize(tz, ambiguous, nonexistent)
  File \"C:\\ProgramData\\Miniconda3\\envs\\env\\lib\\site-packages\\pandas\\core\\arrays\\_mixins.py\", line 84, in method
    return meth(self, *args, **kwargs)
  File \"C:\\ProgramData\\Miniconda3\\envs\\env\\lib\\site-packages\\pandas\\core\\arrays\\datetimes.py\", line 1043, in tz_localize
    new_dates = tzconversion.tz_localize_to_utc(
  File \"pandas\\_libs\\tslibs\\tzconversion.pyx\", line 328, in pandas._libs.tslibs.tzconversion.tz_localize_to_utc
pytz.exceptions.NonExistentTimeError: 2022-03-27 02:00:00

Europe/Berlin 时区了解UTC 日期时间对象的最佳选择是什么?

标签: python pandas datetime timezone


【解决方案1】:

您可以告诉tz_localize 如何处理不存在的时间:

不存在的str,默认为“raise”

由于 DST,时钟向前移动的特定时区不存在不存在的时间。有效值为:

‘shift_forward’ 将不存在的时间向前移动到最接近的现有时间

‘shift_backward’ 将不存在的时间向后移动到最接近的现有时间

‘NaT’ 将返回不存在时间的 NaT

timedelta 对象会将不存在的时间移动 timedelta

如果时间不存在,“raise”将引发 NonExistentTimeError。

替换为 NaT:

dt = pd.to_datetime(df['Date']+' '+df['Time'])
corr = pd.to_timedelta(df['dst'].map({'W': 1, 'S': 2}), unit='H')

# converting to NaT
dt.sub(corr).dt.tz_localize(tz='Europe/Berlin', nonexistent='NaT')

输出:

0    2022-03-27 00:15:00+01:00
1    2022-03-27 00:30:00+01:00
2    2022-03-27 00:45:00+01:00
3    2022-03-27 01:00:00+01:00
4    2022-03-27 01:15:00+01:00
5    2022-03-27 01:30:00+01:00
6    2022-03-27 01:45:00+01:00
7                          NaT
8                          NaT
9                          NaT
10                         NaT
11   2022-03-27 03:00:00+02:00
12   2022-03-27 03:15:00+02:00
dtype: datetime64[ns, Europe/Berlin]

向前移动:

dt.sub(corr).dt.tz_localize(tz='Europe/Berlin', nonexistent='shift_forward')

0    2022-03-27 00:15:00+01:00
1    2022-03-27 00:30:00+01:00
2    2022-03-27 00:45:00+01:00
3    2022-03-27 01:00:00+01:00
4    2022-03-27 01:15:00+01:00
5    2022-03-27 01:30:00+01:00
6    2022-03-27 01:45:00+01:00
7    2022-03-27 03:00:00+02:00
8    2022-03-27 03:00:00+02:00
9    2022-03-27 03:00:00+02:00
10   2022-03-27 03:00:00+02:00
11   2022-03-27 03:00:00+02:00
12   2022-03-27 03:15:00+02:00
dtype: datetime64[ns, Europe/Berlin]

【讨论】:

    猜你喜欢
    • 2017-08-28
    • 2011-12-20
    • 1970-01-01
    • 2020-11-09
    • 2012-10-07
    • 1970-01-01
    • 2011-05-06
    • 2013-03-24
    • 2022-11-14
    相关资源
    最近更新 更多