如何将此时间序列转换为UTC?

时间:2019-05-13 14:29:22

标签: python pandas timezone

我第一次来:)

我有以下Pandas DataFrame

    date            time
0   2018-03-24      23
1   2018-03-24      24
2   2018-03-25      1
3   2018-03-25      2
4   2018-03-25      3
5   2018-03-25      4
6   2018-03-25      5
7   2018-03-25      6
8   2018-03-25      7
9   2018-03-25      8
10  2018-03-25      9
11  2018-03-25      10
12  2018-03-25      11
13  2018-03-25      12
14  2018-03-25      13
15  2018-03-25      14
16  2018-03-25      15
17  2018-03-25      16
18  2018-03-25      17
19  2018-03-25      18
20  2018-03-25      19
21  2018-03-25      20
22  2018-03-25      21
23  2018-03-25      22
24  2018-03-25      23
25  2018-03-26      1
26  2018-03-26      2
27  2018-03-26      3
28  2018-03-26      4
29  2018-03-26      5
30  2018-03-26      6
31  2018-10-27      23
32  2018-10-27      24
33  2018-10-28      1
34  2018-10-28      2
35  2018-10-28      3
36  2018-10-28      4
37  2018-10-28      5
38  2018-10-28      6
39  2018-10-28      7
40  2018-10-28      8
41  2018-10-28      9
42  2018-10-28      10
43  2018-10-28      11
44  2018-10-28      12
45  2018-10-28      13
46  2018-10-28      14
47  2018-10-28      15
48  2018-10-28      16
49  2018-10-28      17
50  2018-10-28      18
51  2018-10-28      19
52  2018-10-28      20
53  2018-10-28      21
54  2018-10-28      22
55  2018-10-28      23
56  2018-10-28      24
57  2018-10-28      25
58  2018-10-29      1
59  2018-10-29      2

time列应表示一天中的小时减去一,例如

    date            time
2   2018-03-25      1 

等于2018-03-25 00:00欧洲/伦敦时间。

生成此时间序列数据的设备设置为根据'Europe/London'时区工作,这就是为什么2018年3月25日在23小时(记录[2:25])结束,而2018年10月28日结束的原因25小时(记录[33:58])(DST开关)。

这是我已经尝试过的东西:

from pytz import timezone
tz = timezone("Europe/London")
dst_switch = [dt for dt in tz._utc_transition_times if dt.year == 2018]
dst_switch_date_range = pd.date_range(dst_switch[0],dst_switch[1], freq='h', tz='utc')

df['datetime'] = pd.to_datetime(df.date) + pd.to_timedelta(df.time - 1, unit='h') 

df['dt_utc']     = df['datetime'].dt.tz_localize('UTC')
df['dst_switch'] = df['datetime'].map(lambda dt: 1 if dt in dst_switch_date_range else -1)
df['dt_p']     = df['datetime'] + pd.to_timedelta(df['dst_switch'], unit='h')
df['dt_utc_p'] = df['dt_p'].dt.tz_localize('Europe/London', ambiguous='NaT', nonexistent='NaT').dt.tz_convert('UTC')

df[['date', 'time', 'dt', 'dt_utc', 'map','dt_p', 'dt_utc_p']]

结果为:

       date           time   dt                      dt_utc                       dst_switch     dt_p                    dt_utc_p
0      2018-03-24     23     2018-03-24 23:00:00     2018-03-24 22:00:00+00:00     -1            2018-03-24 21:00:00     2018-03-24 21:00:00+00:00
1      2018-03-24     24     2018-03-25 00:00:00     2018-03-24 23:00:00+00:00     -1            2018-03-24 22:00:00     2018-03-24 22:00:00+00:00
2      2018-03-25     1      2018-03-25 01:00:00     2018-03-25 00:00:00+00:00     -1            2018-03-24 23:00:00     2018-03-24 23:00:00+00:00
3      2018-03-25     2      2018-03-25 02:00:00     2018-03-25 01:00:00+00:00     1             2018-03-25 02:00:00     2018-03-25 01:00:00+00:00
4      2018-03-25     3      2018-03-25 03:00:00     2018-03-25 02:00:00+00:00     1             2018-03-25 03:00:00     2018-03-25 02:00:00+00:00
5      2018-03-25     4      2018-03-25 04:00:00     2018-03-25 03:00:00+00:00     1             2018-03-25 04:00:00     2018-03-25 03:00:00+00:00
6      2018-03-25     5      2018-03-25 05:00:00     2018-03-25 04:00:00+00:00     1             2018-03-25 05:00:00     2018-03-25 04:00:00+00:00
7      2018-03-25     6      2018-03-25 06:00:00     2018-03-25 05:00:00+00:00     1             2018-03-25 06:00:00     2018-03-25 05:00:00+00:00
8      2018-03-25     7      2018-03-25 07:00:00     2018-03-25 06:00:00+00:00     1             2018-03-25 07:00:00     2018-03-25 06:00:00+00:00
9      2018-03-25     8      2018-03-25 08:00:00     2018-03-25 07:00:00+00:00     1             2018-03-25 08:00:00     2018-03-25 07:00:00+00:00
10     2018-03-25     9      2018-03-25 09:00:00     2018-03-25 08:00:00+00:00     1             2018-03-25 09:00:00     2018-03-25 08:00:00+00:00
11     2018-03-25     10     2018-03-25 10:00:00     2018-03-25 09:00:00+00:00     1             2018-03-25 10:00:00     2018-03-25 09:00:00+00:00
12     2018-03-25     11     2018-03-25 11:00:00     2018-03-25 10:00:00+00:00     1             2018-03-25 11:00:00     2018-03-25 10:00:00+00:00
13     2018-03-25     12     2018-03-25 12:00:00     2018-03-25 11:00:00+00:00     1             2018-03-25 12:00:00     2018-03-25 11:00:00+00:00
14     2018-03-25     13     2018-03-25 13:00:00     2018-03-25 12:00:00+00:00     1             2018-03-25 13:00:00     2018-03-25 12:00:00+00:00
15     2018-03-25     14     2018-03-25 14:00:00     2018-03-25 13:00:00+00:00     1             2018-03-25 14:00:00     2018-03-25 13:00:00+00:00
16     2018-03-25     15     2018-03-25 15:00:00     2018-03-25 14:00:00+00:00     1             2018-03-25 15:00:00     2018-03-25 14:00:00+00:00
17     2018-03-25     16     2018-03-25 16:00:00     2018-03-25 15:00:00+00:00     1             2018-03-25 16:00:00     2018-03-25 15:00:00+00:00
18     2018-03-25     17     2018-03-25 17:00:00     2018-03-25 16:00:00+00:00     1             2018-03-25 17:00:00     2018-03-25 16:00:00+00:00
19     2018-03-25     18     2018-03-25 18:00:00     2018-03-25 17:00:00+00:00     1             2018-03-25 18:00:00     2018-03-25 17:00:00+00:00
20     2018-03-25     19     2018-03-25 19:00:00     2018-03-25 18:00:00+00:00     1             2018-03-25 19:00:00     2018-03-25 18:00:00+00:00
21     2018-03-25     20     2018-03-25 20:00:00     2018-03-25 19:00:00+00:00     1             2018-03-25 20:00:00     2018-03-25 19:00:00+00:00
22     2018-03-25     21     2018-03-25 21:00:00     2018-03-25 20:00:00+00:00     1             2018-03-25 21:00:00     2018-03-25 20:00:00+00:00
23     2018-03-25     22     2018-03-25 22:00:00     2018-03-25 21:00:00+00:00     1             2018-03-25 22:00:00     2018-03-25 21:00:00+00:00
24     2018-03-25     23     2018-03-25 23:00:00     2018-03-25 22:00:00+00:00     1             2018-03-25 23:00:00     2018-03-25 22:00:00+00:00
25     2018-03-26     1      2018-03-26 01:00:00     2018-03-26 00:00:00+00:00     1             2018-03-26 01:00:00     2018-03-26 00:00:00+00:00
26     2018-03-26     2      2018-03-26 02:00:00     2018-03-26 01:00:00+00:00     1             2018-03-26 02:00:00     2018-03-26 01:00:00+00:00
27     2018-03-26     3      2018-03-26 03:00:00     2018-03-26 02:00:00+00:00     1             2018-03-26 03:00:00     2018-03-26 02:00:00+00:00
28     2018-03-26     4      2018-03-26 04:00:00     2018-03-26 03:00:00+00:00     1             2018-03-26 04:00:00     2018-03-26 03:00:00+00:00
29     2018-03-26     5      2018-03-26 05:00:00     2018-03-26 04:00:00+00:00     1             2018-03-26 05:00:00     2018-03-26 04:00:00+00:00
30     2018-03-26     6      2018-03-26 06:00:00     2018-03-26 05:00:00+00:00     1             2018-03-26 06:00:00     2018-03-26 05:00:00+00:00
31     2018-10-27     23     2018-10-27 23:00:00     2018-10-27 22:00:00+00:00     1             2018-10-27 23:00:00     2018-10-27 22:00:00+00:00
32     2018-10-27     24     2018-10-28 00:00:00     2018-10-27 23:00:00+00:00     1             2018-10-28 00:00:00     2018-10-27 23:00:00+00:00
33     2018-10-28     1      2018-10-28 01:00:00     2018-10-28 00:00:00+00:00     1             2018-10-28 01:00:00     NaT
34     2018-10-28     2      2018-10-28 02:00:00     2018-10-28 01:00:00+00:00     1             2018-10-28 02:00:00     2018-10-28 02:00:00+00:00
35     2018-10-28     3      2018-10-28 03:00:00     2018-10-28 02:00:00+00:00     -1            2018-10-28 01:00:00     NaT
36     2018-10-28     4      2018-10-28 04:00:00     2018-10-28 03:00:00+00:00     -1            2018-10-28 02:00:00     2018-10-28 02:00:00+00:00
37     2018-10-28     5      2018-10-28 05:00:00     2018-10-28 04:00:00+00:00     -1            2018-10-28 03:00:00     2018-10-28 03:00:00+00:00
38     2018-10-28     6      2018-10-28 06:00:00     2018-10-28 05:00:00+00:00     -1            2018-10-28 04:00:00     2018-10-28 04:00:00+00:00
39     2018-10-28     7      2018-10-28 07:00:00     2018-10-28 06:00:00+00:00     -1            2018-10-28 05:00:00     2018-10-28 05:00:00+00:00
40     2018-10-28     8      2018-10-28 08:00:00     2018-10-28 07:00:00+00:00     -1            2018-10-28 06:00:00     2018-10-28 06:00:00+00:00
41     2018-10-28     9      2018-10-28 09:00:00     2018-10-28 08:00:00+00:00     -1            2018-10-28 07:00:00     2018-10-28 07:00:00+00:00
42     2018-10-28     10     2018-10-28 10:00:00     2018-10-28 09:00:00+00:00     -1            2018-10-28 08:00:00     2018-10-28 08:00:00+00:00
43     2018-10-28     11     2018-10-28 11:00:00     2018-10-28 10:00:00+00:00     -1            2018-10-28 09:00:00     2018-10-28 09:00:00+00:00
44     2018-10-28     12     2018-10-28 12:00:00     2018-10-28 11:00:00+00:00     -1            2018-10-28 10:00:00     2018-10-28 10:00:00+00:00
45     2018-10-28     13     2018-10-28 13:00:00     2018-10-28 12:00:00+00:00     -1            2018-10-28 11:00:00     2018-10-28 11:00:00+00:00
46     2018-10-28     14     2018-10-28 14:00:00     2018-10-28 13:00:00+00:00     -1            2018-10-28 12:00:00     2018-10-28 12:00:00+00:00
47     2018-10-28     15     2018-10-28 15:00:00     2018-10-28 14:00:00+00:00     -1            2018-10-28 13:00:00     2018-10-28 13:00:00+00:00
48     2018-10-28     16     2018-10-28 16:00:00     2018-10-28 15:00:00+00:00     -1            2018-10-28 14:00:00     2018-10-28 14:00:00+00:00
49     2018-10-28     17     2018-10-28 17:00:00     2018-10-28 16:00:00+00:00     -1            2018-10-28 15:00:00     2018-10-28 15:00:00+00:00
50     2018-10-28     18     2018-10-28 18:00:00     2018-10-28 17:00:00+00:00     -1            2018-10-28 16:00:00     2018-10-28 16:00:00+00:00
51     2018-10-28     19     2018-10-28 19:00:00     2018-10-28 18:00:00+00:00     -1            2018-10-28 17:00:00     2018-10-28 17:00:00+00:00
52     2018-10-28     20     2018-10-28 20:00:00     2018-10-28 19:00:00+00:00     -1            2018-10-28 18:00:00     2018-10-28 18:00:00+00:00
53     2018-10-28     21     2018-10-28 21:00:00     2018-10-28 20:00:00+00:00     -1            2018-10-28 19:00:00     2018-10-28 19:00:00+00:00
54     2018-10-28     22     2018-10-28 22:00:00     2018-10-28 21:00:00+00:00     -1            2018-10-28 20:00:00     2018-10-28 20:00:00+00:00
55     2018-10-28     23     2018-10-28 23:00:00     2018-10-28 22:00:00+00:00     -1            2018-10-28 21:00:00     2018-10-28 21:00:00+00:00
56     2018-10-28     24     2018-10-29 00:00:00     2018-10-28 23:00:00+00:00     -1            2018-10-28 22:00:00     2018-10-28 22:00:00+00:00
57     2018-10-28     25     2018-10-29 01:00:00     2018-10-29 00:00:00+00:00     -1            2018-10-28 23:00:00     2018-10-28 23:00:00+00:00
58     2018-10-29     1      2018-10-29 01:00:00     2018-10-29 00:00:00+00:00     -1            2018-10-28 23:00:00     2018-10-28 23:00:00+00:00
59     2018-10-29     2      2018-10-29 02:00:00     2018-10-29 01:00:00+00:00     -1            2018-10-29 00:00:00     2018-10-29 00:00:00+00:00

我需要的:我需要将这两列合并为datetime列,然后将时区从'Europe/London'转换为'UTC'(或其他方式:在time列以适合'UTC',然后与date列合并)

我的逻辑:1年的时间为24h * 365天= 8760小时。如果我采用上述全年的时间序列数据,则应该得到363天,每条记录24条,1天23条记录,和1天25条记录,总共8712 + 23 + 25 = 8760条记录,因此,每条记录一年中的小时。

我失败了:将我的逻辑翻译为代码:P。我对如何补偿这些日期没有好主意,因此一切都会有意义,这意味着DST切换日期之间的平滑过渡,没有重复的时间序列值,也没有时间序列中的值(空洞)。

预期结果

    date           time   dt_utc                   
0   2018-03-24     23     2018-03-24 22:00:00+00:00
1   2018-03-24     24     2018-03-24 23:00:00+00:00
2   2018-03-25     1      2018-03-25 00:00:00+00:00
3   2018-03-25     2      2018-03-25 01:00:00+00:00
4   2018-03-25     3      2018-03-25 02:00:00+00:00
5   2018-03-25     4      2018-03-25 03:00:00+00:00
6   2018-03-25     5      2018-03-25 04:00:00+00:00
7   2018-03-25     6      2018-03-25 05:00:00+00:00
8   2018-03-25     7      2018-03-25 06:00:00+00:00
9   2018-03-25     8      2018-03-25 07:00:00+00:00
10  2018-03-25     9      2018-03-25 08:00:00+00:00
11  2018-03-25     10     2018-03-25 09:00:00+00:00
12  2018-03-25     11     2018-03-25 10:00:00+00:00
13  2018-03-25     12     2018-03-25 11:00:00+00:00
14  2018-03-25     13     2018-03-25 12:00:00+00:00
15  2018-03-25     14     2018-03-25 13:00:00+00:00
16  2018-03-25     15     2018-03-25 14:00:00+00:00
17  2018-03-25     16     2018-03-25 15:00:00+00:00
18  2018-03-25     17     2018-03-25 16:00:00+00:00
19  2018-03-25     18     2018-03-25 17:00:00+00:00
20  2018-03-25     19     2018-03-25 18:00:00+00:00
21  2018-03-25     20     2018-03-25 19:00:00+00:00
22  2018-03-25     21     2018-03-25 20:00:00+00:00
23  2018-03-25     22     2018-03-25 21:00:00+00:00
24  2018-03-25     23     2018-03-25 22:00:00+00:00
25  2018-03-26     1      2018-03-25 23:00:00+00:00
26  2018-03-26     2      2018-03-26 00:00:00+00:00
27  2018-03-26     3      2018-03-26 01:00:00+00:00
28  2018-03-26     4      2018-03-26 02:00:00+00:00
29  2018-03-26     5      2018-03-26 03:00:00+00:00
30  2018-03-26     6      2018-03-26 04:00:00+00:00
31  2018-10-27     23     2018-10-27 21:00:00+00:00
32  2018-10-27     24     2018-10-27 22:00:00+00:00
33  2018-10-28     1      2018-10-27 23:00:00+00:00
34  2018-10-28     2      2018-10-28 00:00:00+00:00
35  2018-10-28     3      2018-10-28 01:00:00+00:00
36  2018-10-28     4      2018-10-28 02:00:00+00:00
37  2018-10-28     5      2018-10-28 03:00:00+00:00
38  2018-10-28     6      2018-10-28 04:00:00+00:00
39  2018-10-28     7      2018-10-28 05:00:00+00:00
40  2018-10-28     8      2018-10-28 06:00:00+00:00
41  2018-10-28     9      2018-10-28 07:00:00+00:00
42  2018-10-28     10     2018-10-28 08:00:00+00:00
43  2018-10-28     11     2018-10-28 09:00:00+00:00
44  2018-10-28     12     2018-10-28 10:00:00+00:00
45  2018-10-28     13     2018-10-28 11:00:00+00:00
46  2018-10-28     14     2018-10-28 12:00:00+00:00
47  2018-10-28     15     2018-10-28 13:00:00+00:00
48  2018-10-28     16     2018-10-28 14:00:00+00:00
49  2018-10-28     17     2018-10-28 15:00:00+00:00
50  2018-10-28     18     2018-10-28 16:00:00+00:00
51  2018-10-28     19     2018-10-28 17:00:00+00:00
52  2018-10-28     20     2018-10-28 18:00:00+00:00
53  2018-10-28     21     2018-10-28 19:00:00+00:00
54  2018-10-28     22     2018-10-28 20:00:00+00:00
55  2018-10-28     23     2018-10-28 21:00:00+00:00
56  2018-10-28     24     2018-10-28 22:00:00+00:00
57  2018-10-28     25     2018-10-28 23:00:00+00:00
58  2018-10-29     1      2018-10-29 00:00:00+00:00
59  2018-10-29     2      2018-10-29 01:00:00+00:00

请帮助:)

2 个答案:

答案 0 :(得分:1)

该问题中的优秀MCVE。让datetime,timezone和timedeltas处理DST。我没有检查结果,但这应该很好。

import pandas as pd
from pandas.compat import StringIO
print(pd.__version__)

data = """index   date            hour
0   2018-03-24      23
1   2018-03-24      24
2   2018-03-25      1
3   2018-03-25      2
4   2018-03-25      3
5   2018-03-25      4
6   2018-03-25      5
7   2018-03-25      6
8   2018-03-25      7
9   2018-03-25      8
10  2018-03-25      9
11  2018-03-25      10
12  2018-03-25      11
13  2018-03-25      12
14  2018-03-25      13
15  2018-03-25      14
16  2018-03-25      15
17  2018-03-25      16
18  2018-03-25      17
19  2018-03-25      18
20  2018-03-25      19
21  2018-03-25      20
22  2018-03-25      21
23  2018-03-25      22
24  2018-03-25      23
25  2018-03-26      1
26  2018-03-26      2
27  2018-03-26      3
28  2018-03-26      4
29  2018-03-26      5
30  2018-03-26      6
31  2018-10-27      23
32  2018-10-27      24
33  2018-10-28      1
34  2018-10-28      2
35  2018-10-28      3
36  2018-10-28      4
37  2018-10-28      5
38  2018-10-28      6
39  2018-10-28      7
40  2018-10-28      8
41  2018-10-28      9
42  2018-10-28      10
43  2018-10-28      11
44  2018-10-28      12
45  2018-10-28      13
46  2018-10-28      14
47  2018-10-28      15
48  2018-10-28      16
49  2018-10-28      17
50  2018-10-28      18
51  2018-10-28      19
52  2018-10-28      20
53  2018-10-28      21
54  2018-10-28      22
55  2018-10-28      23
56  2018-10-28      24
57  2018-10-28      25
58  2018-10-29      1
59  2018-10-29      2"""

df = pd.read_csv(StringIO(data), sep='\s+', index_col=0)
df['hour'] = pd.to_timedelta(df['hour'] - 1, 'h')
df['date'] = pd.to_datetime(df['date'])
df['naive_datetime'] = df['date'] + df['hour']
df.set_index(df['naive_datetime'], inplace=True)
df.index = df.index.tz_localize('Europe/London').tz_convert('UTC')

print(df)

产生

pytz.exceptions.NonExistentTimeError: 2018-03-25 01:00:00

因此,必须处理不存在的日期时间。有许多选项可以执行此操作,其中一种是忽略每年两次跳转时间的本地DST时区中不存在的日期时间。

# receives non-existent time exception because of naive datetime that does not exist in Europe/London
#df.index = df.index.tz_localize('Europe/London').tz_convert('UTC')

# receives AmbiguousTimeError: Cannot infer dst time from 2018-10-28 01:00:00 as there are no repeated times
#df.index = df.index.tz_localize('Europe/London', ambiguous='infer').tz_convert('UTC')

df.index = df.index.tz_localize('Europe/London', ambiguous='NaT', nonexistent='NaT').tz_convert('UTC')
df.index.name = "datetime Europe/London"

# if there is timestamped data in the dataframe, something has to be done with it.
# The data for the missing time is probably best dropped
#df = df[df.index.notnull()]

# interpolate doesn't work: https://github.com/pandas-dev/pandas/issues/11701, but then again the time does not exist in local tz...
#df['datetime Europe/London interpolated'] = df.index.to_series().interpolate(method='linear')

答案 1 :(得分:0)

我最终写了一个函数calc_hour_offset,该函数映射到date列上,以产生int偏移量,然后将其用于调整time列中的值匹配UTC时区。解决方案适用于我所有的极端情况

import pandas as pd
from pandas.compat import StringIO
from pytz import timezone

def calc_hour_offset(date, to_timezone='UTC'):
    tz = timezone('Europe/London')
    dst_switch_start, dst_switch_end = [
        tzdt.date() 
        for tzdt 
        in tz._utc_transition_times 
        if tzdt.year == date.year]
    dst_switch_date_range = pd.date_range(dst_switch_start, 
                                          dst_switch_end, 
                                          freq='h', 
                                          tz=to_timezone)
    if date.date() not in dst_switch_date_range:
        return -1
    elif date.date() == dst_switch_start:
        return -1
    elif date.date() == dst_switch_end:
        return -2
    elif date.date() in dst_switch_date_range:
        return -2
    else:
        raise

str_df = r"""date           time   target_dt_utc                   
0   2018-03-24     23     2018-03-24 22:00:00+00:00
1   2018-03-24     24     2018-03-24 23:00:00+00:00
2   2018-03-25     1      2018-03-25 00:00:00+00:00
3   2018-03-25     2      2018-03-25 01:00:00+00:00
4   2018-03-25     3      2018-03-25 02:00:00+00:00
5   2018-03-25     4      2018-03-25 03:00:00+00:00
6   2018-03-25     5      2018-03-25 04:00:00+00:00
7   2018-03-25     6      2018-03-25 05:00:00+00:00
8   2018-03-25     7      2018-03-25 06:00:00+00:00
9   2018-03-25     8      2018-03-25 07:00:00+00:00
10  2018-03-25     9      2018-03-25 08:00:00+00:00
11  2018-03-25     10     2018-03-25 09:00:00+00:00
12  2018-03-25     11     2018-03-25 10:00:00+00:00
13  2018-03-25     12     2018-03-25 11:00:00+00:00
14  2018-03-25     13     2018-03-25 12:00:00+00:00
15  2018-03-25     14     2018-03-25 13:00:00+00:00
16  2018-03-25     15     2018-03-25 14:00:00+00:00
17  2018-03-25     16     2018-03-25 15:00:00+00:00
18  2018-03-25     17     2018-03-25 16:00:00+00:00
19  2018-03-25     18     2018-03-25 17:00:00+00:00
20  2018-03-25     19     2018-03-25 18:00:00+00:00
21  2018-03-25     20     2018-03-25 19:00:00+00:00
22  2018-03-25     21     2018-03-25 20:00:00+00:00
23  2018-03-25     22     2018-03-25 21:00:00+00:00
24  2018-03-25     23     2018-03-25 22:00:00+00:00
25  2018-03-26     1      2018-03-25 23:00:00+00:00
26  2018-03-26     2      2018-03-26 00:00:00+00:00
27  2018-03-26     3      2018-03-26 01:00:00+00:00
28  2018-03-26     4      2018-03-26 02:00:00+00:00
29  2018-03-26     5      2018-03-26 03:00:00+00:00
30  2018-03-26     6      2018-03-26 04:00:00+00:00
31  2018-10-27     23     2018-10-27 21:00:00+00:00
32  2018-10-27     24     2018-10-27 22:00:00+00:00
33  2018-10-28     1      2018-10-27 23:00:00+00:00
34  2018-10-28     2      2018-10-28 00:00:00+00:00
35  2018-10-28     3      2018-10-28 01:00:00+00:00
36  2018-10-28     4      2018-10-28 02:00:00+00:00
37  2018-10-28     5      2018-10-28 03:00:00+00:00
38  2018-10-28     6      2018-10-28 04:00:00+00:00
39  2018-10-28     7      2018-10-28 05:00:00+00:00
40  2018-10-28     8      2018-10-28 06:00:00+00:00
41  2018-10-28     9      2018-10-28 07:00:00+00:00
42  2018-10-28     10     2018-10-28 08:00:00+00:00
43  2018-10-28     11     2018-10-28 09:00:00+00:00
44  2018-10-28     12     2018-10-28 10:00:00+00:00
45  2018-10-28     13     2018-10-28 11:00:00+00:00
46  2018-10-28     14     2018-10-28 12:00:00+00:00
47  2018-10-28     15     2018-10-28 13:00:00+00:00
48  2018-10-28     16     2018-10-28 14:00:00+00:00
49  2018-10-28     17     2018-10-28 15:00:00+00:00
50  2018-10-28     18     2018-10-28 16:00:00+00:00
51  2018-10-28     19     2018-10-28 17:00:00+00:00
52  2018-10-28     20     2018-10-28 18:00:00+00:00
53  2018-10-28     21     2018-10-28 19:00:00+00:00
54  2018-10-28     22     2018-10-28 20:00:00+00:00
55  2018-10-28     23     2018-10-28 21:00:00+00:00
56  2018-10-28     24     2018-10-28 22:00:00+00:00
57  2018-10-28     25     2018-10-28 23:00:00+00:00
58  2018-10-29     1      2018-10-29 00:00:00+00:00
59  2018-10-29     2      2018-10-29 01:00:00+00:00"""

df = pd.read_csv(StringIO(str_df), sep='\s{2,}', index_col=0)
df['target_dt_utc'] = pd.to_datetime(df['target_dt_utc'])
df['date'] = pd.to_datetime(df['date'])

df['time_utc'] = df['date'].map(calc_hour_offset) + df['time'] 

df['dt_utc_calculated'] = (pd.to_datetime(df['date']) + pd.to_timedelta(df['time_utc'], 'h')).dt.tz_localize('UTC')

df['is dt_utc_calculated equal to target_dt_utc'] = df['target_dt_utc'] == df['dt_utc_calculated']

print(df)

退出:

         date  time             target_dt_utc  time_utc         dt_utc_calculated  is dt_utc_calculated equal to target_dt_utc
0  2018-03-24  23   2018-03-24 22:00:00+00:00  22       2018-03-24 22:00:00+00:00  True                                       
1  2018-03-24  24   2018-03-24 23:00:00+00:00  23       2018-03-24 23:00:00+00:00  True                                       
2  2018-03-25  1    2018-03-25 00:00:00+00:00  0        2018-03-25 00:00:00+00:00  True                                       
3  2018-03-25  2    2018-03-25 01:00:00+00:00  1        2018-03-25 01:00:00+00:00  True                                       
4  2018-03-25  3    2018-03-25 02:00:00+00:00  2        2018-03-25 02:00:00+00:00  True                                       
5  2018-03-25  4    2018-03-25 03:00:00+00:00  3        2018-03-25 03:00:00+00:00  True                                       
6  2018-03-25  5    2018-03-25 04:00:00+00:00  4        2018-03-25 04:00:00+00:00  True                                       
7  2018-03-25  6    2018-03-25 05:00:00+00:00  5        2018-03-25 05:00:00+00:00  True                                       
8  2018-03-25  7    2018-03-25 06:00:00+00:00  6        2018-03-25 06:00:00+00:00  True                                       
9  2018-03-25  8    2018-03-25 07:00:00+00:00  7        2018-03-25 07:00:00+00:00  True                                       
10 2018-03-25  9    2018-03-25 08:00:00+00:00  8        2018-03-25 08:00:00+00:00  True                                       
11 2018-03-25  10   2018-03-25 09:00:00+00:00  9        2018-03-25 09:00:00+00:00  True                                       
12 2018-03-25  11   2018-03-25 10:00:00+00:00  10       2018-03-25 10:00:00+00:00  True                                       
13 2018-03-25  12   2018-03-25 11:00:00+00:00  11       2018-03-25 11:00:00+00:00  True                                       
14 2018-03-25  13   2018-03-25 12:00:00+00:00  12       2018-03-25 12:00:00+00:00  True                                       
15 2018-03-25  14   2018-03-25 13:00:00+00:00  13       2018-03-25 13:00:00+00:00  True                                       
16 2018-03-25  15   2018-03-25 14:00:00+00:00  14       2018-03-25 14:00:00+00:00  True                                       
17 2018-03-25  16   2018-03-25 15:00:00+00:00  15       2018-03-25 15:00:00+00:00  True                                       
18 2018-03-25  17   2018-03-25 16:00:00+00:00  16       2018-03-25 16:00:00+00:00  True                                       
19 2018-03-25  18   2018-03-25 17:00:00+00:00  17       2018-03-25 17:00:00+00:00  True                                       
20 2018-03-25  19   2018-03-25 18:00:00+00:00  18       2018-03-25 18:00:00+00:00  True                                       
21 2018-03-25  20   2018-03-25 19:00:00+00:00  19       2018-03-25 19:00:00+00:00  True                                       
22 2018-03-25  21   2018-03-25 20:00:00+00:00  20       2018-03-25 20:00:00+00:00  True                                       
23 2018-03-25  22   2018-03-25 21:00:00+00:00  21       2018-03-25 21:00:00+00:00  True                                       
24 2018-03-25  23   2018-03-25 22:00:00+00:00  22       2018-03-25 22:00:00+00:00  True                                       
25 2018-03-26  1    2018-03-25 23:00:00+00:00 -1        2018-03-25 23:00:00+00:00  True                                       
26 2018-03-26  2    2018-03-26 00:00:00+00:00  0        2018-03-26 00:00:00+00:00  True                                       
27 2018-03-26  3    2018-03-26 01:00:00+00:00  1        2018-03-26 01:00:00+00:00  True                                       
28 2018-03-26  4    2018-03-26 02:00:00+00:00  2        2018-03-26 02:00:00+00:00  True                                       
29 2018-03-26  5    2018-03-26 03:00:00+00:00  3        2018-03-26 03:00:00+00:00  True                                       
30 2018-03-26  6    2018-03-26 04:00:00+00:00  4        2018-03-26 04:00:00+00:00  True                                       
31 2018-10-27  23   2018-10-27 21:00:00+00:00  21       2018-10-27 21:00:00+00:00  True                                       
32 2018-10-27  24   2018-10-27 22:00:00+00:00  22       2018-10-27 22:00:00+00:00  True                                       
33 2018-10-28  1    2018-10-27 23:00:00+00:00 -1        2018-10-27 23:00:00+00:00  True                                       
34 2018-10-28  2    2018-10-28 00:00:00+00:00  0        2018-10-28 00:00:00+00:00  True                                       
35 2018-10-28  3    2018-10-28 01:00:00+00:00  1        2018-10-28 01:00:00+00:00  True                                       
36 2018-10-28  4    2018-10-28 02:00:00+00:00  2        2018-10-28 02:00:00+00:00  True                                       
37 2018-10-28  5    2018-10-28 03:00:00+00:00  3        2018-10-28 03:00:00+00:00  True                                       
38 2018-10-28  6    2018-10-28 04:00:00+00:00  4        2018-10-28 04:00:00+00:00  True                                       
39 2018-10-28  7    2018-10-28 05:00:00+00:00  5        2018-10-28 05:00:00+00:00  True                                       
40 2018-10-28  8    2018-10-28 06:00:00+00:00  6        2018-10-28 06:00:00+00:00  True                                       
41 2018-10-28  9    2018-10-28 07:00:00+00:00  7        2018-10-28 07:00:00+00:00  True                                       
42 2018-10-28  10   2018-10-28 08:00:00+00:00  8        2018-10-28 08:00:00+00:00  True                                       
43 2018-10-28  11   2018-10-28 09:00:00+00:00  9        2018-10-28 09:00:00+00:00  True                                       
44 2018-10-28  12   2018-10-28 10:00:00+00:00  10       2018-10-28 10:00:00+00:00  True                                       
45 2018-10-28  13   2018-10-28 11:00:00+00:00  11       2018-10-28 11:00:00+00:00  True                                       
46 2018-10-28  14   2018-10-28 12:00:00+00:00  12       2018-10-28 12:00:00+00:00  True                                       
47 2018-10-28  15   2018-10-28 13:00:00+00:00  13       2018-10-28 13:00:00+00:00  True                                       
48 2018-10-28  16   2018-10-28 14:00:00+00:00  14       2018-10-28 14:00:00+00:00  True                                       
49 2018-10-28  17   2018-10-28 15:00:00+00:00  15       2018-10-28 15:00:00+00:00  True                                       
50 2018-10-28  18   2018-10-28 16:00:00+00:00  16       2018-10-28 16:00:00+00:00  True                                       
51 2018-10-28  19   2018-10-28 17:00:00+00:00  17       2018-10-28 17:00:00+00:00  True                                       
52 2018-10-28  20   2018-10-28 18:00:00+00:00  18       2018-10-28 18:00:00+00:00  True                                       
53 2018-10-28  21   2018-10-28 19:00:00+00:00  19       2018-10-28 19:00:00+00:00  True                                       
54 2018-10-28  22   2018-10-28 20:00:00+00:00  20       2018-10-28 20:00:00+00:00  True                                       
55 2018-10-28  23   2018-10-28 21:00:00+00:00  21       2018-10-28 21:00:00+00:00  True                                       
56 2018-10-28  24   2018-10-28 22:00:00+00:00  22       2018-10-28 22:00:00+00:00  True                                       
57 2018-10-28  25   2018-10-28 23:00:00+00:00  23       2018-10-28 23:00:00+00:00  True                                       
58 2018-10-29  1    2018-10-29 00:00:00+00:00  0        2018-10-29 00:00:00+00:00  True                                       
59 2018-10-29  2    2018-10-29 01:00:00+00:00  1        2018-10-29 01:00:00+00:00  True