2025, Dec 24 09:02

pywintypes.datetime из Excel (win32com) в pandas: причина ошибки и приведение к datetime64[ns]

Excel через win32com и pandas: pywintypes.datetime приводит к AttributeError при печати DataFrame. Как конвертировать столбцы в int64 и datetime64[ns].

Извлечение данных из Excel через win32com — практичный выбор, если вы уже работаете в экосистеме Office. Неожиданность возникает, когда столбец со значениями времени приходит как pywintypes.datetime, и, казалось бы, обычная печать DataFrame приводит к ошибке AttributeError: 'NoneType' object has no attribute 'total_seconds'. Источник конфликта — несоответствие между COM-объектами даты/времени и тем, чего pandas ожидает от собственного типа datetime.

Как воспроизвести проблему

Пример ниже автоматизирует Excel, забирает таблицу ListObject, превращает её диапазон данных в массив NumPy и пытается собрать pandas DataFrame. В схеме есть столбец времени из Excel и ещё один столбец со значением None.

import win32com.client as W32C
import numpy as np
import pandas as pd

# Запуск Excel
app = W32C.gencache.EnsureDispatch('Excel.Application')
app.Visible = True

# Открываем книгу и находим таблицу
book = app.Workbooks.Open('Test Excel.xlsx')
sheet = book.Worksheets('Sheet1')
listobj = sheet.ListObjects('Table1')

# Читаем заголовки
hdr = listobj.HeaderRowRange()
col_names = list(np.array(hdr))

# Читаем тело таблицы и преобразуем в массив NumPy
body = listobj.DataBodyRange()
body_arr = np.array(body)

# Смотрим, что вернул Excel
for rec in body:
    print(rec)
for rec in body_arr:
    print(rec)

# Собираем DataFrame
df = pd.DataFrame(body_arr, columns=col_names)
print(df)

На этом шаге простая печать DataFrame может привести к трассировке pandas, заканчивающейся ошибкой AttributeError: 'NoneType' object has no attribute 'total_seconds'.

В чём дело

Excel передаёт ячейки с датой/временем через COM как pywintypes.datetime. Увидев такие значения в столбце, pandas считает его похожим на datetime и пытается представить с помощью своей внутренней логики для дат/времени. В этом пути конвертации участвует обработка часовых поясов и перехода на летнее время; с pywintypes.datetime в смеси pandas попадает в ветку, где ожидаемая часть метаданных временной зоны равна None, и шаг рендеринга падает.

Исправление — сначала нормализовать эти значения в родной формат NumPy datetime64. Надёжный способ — пройти «туда-обратно» через целочисленное представление, а затем привести к datetime64[ns].

Решение

Если нужно обработать все столбцы с типом, похожим на datetime, за один проход, обнаружьте их и нормализуйте.

from pandas.api.types import is_datetime64_any_dtype as is_dt64

for col in df.columns:
    if is_dt64(df[col]):
        df[col] = df[col].astype('int64').astype('datetime64[ns]')

Если требуется привести только один столбец со временем, примените ту же двухшаговую конвертацию к нему напрямую.

df['Time Column'] = df['Time Column'].astype('int64').astype('datetime64[ns]')

Почему это важно

Pandas и NumPy опираются на чётко определённые, векторизованные типы datetime ради производительности и корректности. Если в поток попадают посторонние объекты даты/времени, могут сломаться вывод, форматирование, группировки и любые операции, зависящие от времени. Ранняя нормализация предотвращает трудноловимые ошибки во время выполнения и держит последующую аналитику быстрой и предсказуемой.

Вывод

При загрузке таблиц Excel через win32com ожидайте pywintypes.datetime в столбцах с датами. Прежде чем работать с DataFrame, приведите такие столбцы к «родному» для pandas datetime: сначала в int64, затем в datetime64[ns]. Это убирает COM-специфику, предотвращает AttributeError при рендеринге и возвращает привычную семантику datetime в pandas без построчной обработки.