You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Polars中带时区Datetime过滤的偏移问题及解决方法

Polars时区感知日期时间过滤时的偏移问题

问题背景

我有一个DataFrame,其“datetime”列包含带时区感知的日期时间,原始时区为UTC。将时区转换为欧洲/柏林时区(CEST)的操作正常,但过滤datetime时出现了2小时偏移:原始数据集中的第一行本应被保留却未出现在结果中,第三行本应被过滤却被保留。即使使用无时区感知的datetime对象(如datetime(2023,8,1)),结果仍相同。

原始代码如下:

from zoneinfo import ZoneInfo
from datetime import datetime

(
    pl.DataFrame(
        {
            "datetime": [
                "[01/Aug/2023:00:00:02 +0200]",
                "[01/Aug/2023:02:00:02 +0200]",
                "[03/Aug/2023:01:00:02 +0200]",
            ]
        }
    )
    .with_columns(pl.col("datetime").str.to_datetime("[%d/%b/%Y:%H:%M:%S %z]"))
    .with_columns(pl.col("datetime").dt.convert_time_zone("Europe/Berlin"))
    .filter(
        pl.col("datetime")
        .cast(pl.Date)
        .is_between(
            datetime(2023, 8, 1, tzinfo=ZoneInfo("Europe/Berlin")),
            datetime(2023, 8, 3, tzinfo=ZoneInfo("Europe/Berlin")),
        )
    )
)

过滤结果:

datetime
datetime[μs, Europe/Berlin]
2023-08-01 02:00:02 CEST
2023-08-03 01:00:02 CEST

问题原因

问题出在cast(pl.Date)这一步:当把带时区的datetime转换为Date类型时,Polars会先将datetime转换为UTC时间,再提取日期。

  • 第一行的2023-08-01 00:00:02 CEST对应的UTC时间是2023-07-31 22:00:02 UTC,转换为Date后是2023-07-31,不在过滤范围2023-08-01至2023-08-03内,因此被过滤。
  • 第三行的2023-08-03 01:00:02 CEST对应的UTC时间是2023-08-02 23:00:02 UTC,转换为Date后是2023-08-02,符合过滤范围,因此被保留。

解决方案

有两种方式可以让Polars在过滤时正确考虑时区:

方案1:直接比较带时区的datetime范围(推荐)

无需转换为Date,直接用目标时区的datetime范围进行过滤,注意结束时间的边界(如果要包含8月3日全天,需要设置为8月4日0点,或者明确指定当天的最后一刻):

from zoneinfo import ZoneInfo
from datetime import datetime

(
    pl.DataFrame(
        {
            "datetime": [
                "[01/Aug/2023:00:00:02 +0200]",
                "[01/Aug/2023:02:00:02 +0200]",
                "[03/Aug/2023:01:00:02 +0200]",
            ]
        }
    )
    .with_columns(pl.col("datetime").str.to_datetime("[%d/%b/%Y:%H:%M:%S %z]"))
    .with_columns(pl.col("datetime").dt.convert_time_zone("Europe/Berlin"))
    .filter(
        pl.col("datetime").is_between(
            datetime(2023, 8, 1, tzinfo=ZoneInfo("Europe/Berlin")),
            datetime(2023, 8, 4, tzinfo=ZoneInfo("Europe/Berlin"))  # 包含8月3日全天的结束边界
        )
    )
)

方案2:使用dt.date()提取本地时区的日期

dt.date()方法会基于datetime所在的时区(而非UTC)提取日期,替代cast(pl.Date)即可:

from zoneinfo import ZoneInfo
from datetime import datetime

(
    pl.DataFrame(
        {
            "datetime": [
                "[01/Aug/2023:00:00:02 +0200]",
                "[01/Aug/2023:02:00:02 +0200]",
                "[03/Aug/2023:01:00:02 +0200]",
            ]
        }
    )
    .with_columns(pl.col("datetime").str.to_datetime("[%d/%b/%Y:%H:%M:%S %z]"))
    .with_columns(pl.col("datetime").dt.convert_time_zone("Europe/Berlin"))
    .filter(
        pl.col("datetime").dt.date().is_between(
            datetime(2023, 8, 1).date(),
            datetime(2023, 8, 3).date(),
        )
    )
)

两种方案都能得到正确的过滤结果:

datetime
datetime[μs, Europe/Berlin]
2023-08-01 00:00:02 CEST
2023-08-01 02:00:02 CEST

内容的提问来源于stack exchange,提问作者Laxas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 19:16:07