PAYATHON 2026

PySTAC 保存 Catalog 时为何出现 tzinfo 属性错误?

数据小许

结论

从代码看,item.datetime.year 可以正常读取,说明遍历到的 Item 中,datetime 是 Python datetime 对象。接下来可以检查 Collection 的 TemporalExtent:这里直接把 ISO 时间字符串传给了区间:

temporal_extent = pystac.TemporalExtent(
    intervals=[[
        f"{date}-01-01T00:00:00Z",
        f"{date}-12-31T00:00:00Z",
    ]]
)

保存时,PySTAC 也会序列化 Collection 的时间范围。如果区间值仍是字符串,而当前 PySTAC 的代码路径将其当作 datetime 对象处理,就可能在访问 .tzinfo 时触发 AttributeError。catalog.validate() 通过,并不能说明内存中的时间值类型符合序列化代码的预期。

将 Collection 时间范围转换为 datetime

先用 PySTAC 的 str_to_datetime 转换区间端点,再创建 TemporalExtent:

start = pystac.utils.str_to_datetime(f"{date}-01-01T00:00:00Z")
end = pystac.utils.str_to_datetime(f"{date}-12-31T00:00:00Z")

temporal_extent = pystac.TemporalExtent(intervals=[[start, end]])

也可以使用标准库构造带 UTC 时区的值:

from datetime import datetime, timezone

start = datetime(int(date), 1, 1, tzinfo=timezone.utc)
end = datetime(int(date), 12, 31, tzinfo=timezone.utc)

temporal_extent = pystac.TemporalExtent(intervals=[[start, end]])

Item 的时间也应传入 datetime 对象:

item_datetime = pystac.utils.str_to_datetime(f"{date}-01-01T00:00:00Z")

item = pystac.Item(
    id=c_tile.replace('.tif', ''),
    geometry=footprint,
    bbox=bbox,
    datetime=item_datetime,
    properties={},
)

保存前检查时间值类型

保存前可以检查 Item 和 Collection 的时间值,确认是否还有字符串:

for _, _, items in catalog.walk():
    for item in items:
        print(item.id, type(item.datetime), item.datetime)

for child in catalog.get_children():
    if isinstance(child, pystac.Collection):
        for interval in child.extent.temporal.intervals:
            print([type(value) for value in interval], interval)

检查结果中,非空的区间端点应为 datetime 对象。如果 Item 的时间类型正常,而 Collection 区间仍显示为 str,就与代码中直接传入字符串时间范围的情况相符。修正后,再运行 catalog.validate() 和 catalog.save(...)。

核对模型名称的分段

如果示例中的 cur_model 就是实际使用的字符串:

cur_model = 'FLOOD_MAP-1ARCSEC-NW_OFFSET-1in100-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.1'
deets = cur_model.split("-")

那么 2020 在 deets[6],PERCENTILE50 在 deets[7],v3.1 在 deets[8]。当前代码把 deets[7] 赋给 date,又把 deets[8] 当作场景。如果实际字符串结构与示例一致,应核对并调整索引:

date = deets[6]
scenario = deets[7] if deets[7] != "PERCENTILE50" else "Baseline"

否则,传给时间解析器的值可能不是年份。如果真实模型名称采用了不同的分段规则,应以实际命名格式为准。

备注:内容仅供参考。