2025, Oct 02 17:16

to_datetime в pandas: корректная конвертация Unix-меток в миллисекундах

Разбираем типичную ошибку pandas: попытку парсить Unix-метки времени как строки. Покажем правильный to_datetime с unit='ms' и получение даты через .dt.date.

При работе со временем в pandas частая ошибка — пытаться разбирать числовые Unix-метки времени как будто это строковые даты. Если в столбце встречаются значения вроде 1743004800000 и вы передаёте их в to_datetime вместе с шаблоном даты вроде '%Y-%m-%d', вы сразу получаете ошибку разбора.

Обзор проблемы

Возьмём набор данных, где поле времени выглядит как 1743004800000. По виду это может напоминать строку, но по сути — Unix-метка времени в миллисекундах. Попытка разобрать её с помощью строкового формата даты приводит к исключению:

ValueError: time data "1743004800000" doesn't match format "%Y-%m-%d"

Воспроизведение проблемы

Ниже — минимальный пример, который показывает неудачную попытку конвертации. Имена полей обобщены, но логика повторяет реальный сценарий.

import pandas as pds

records = pds.DataFrame({
    "event_ts": [1743004800000, 1753004800000]
})

# Неверно: попытка разобрать миллисекунды с помощью строкового формата даты
broken = records.copy()
broken["event_ts"] = pds.to_datetime(broken["event_ts"], format="%Y-%m-%d").dt.date

Это и вызывает указанный выше ошибку: 1743004800000 — это не строка в формате '%Y-%m-%d', а числовая метка времени.

Почему это происходит

Суть проблемы — несоответствие типа и смысла данных. Значения вроде 1743004800000 — это количество миллисекунд с начала эпохи Unix, а не отформатированная строка даты. Параметр format сообщает pandas, как разбирать строковые шаблоны вроде '2025-03-26', но на вход здесь поступает число миллисекунд, поэтому сопоставить его с '%Y-%m-%d' невозможно.

Как исправить

Правильный подход — явно указать pandas, что значения заданы в миллисекундах, через параметр unit. После конвертации, если нужен только календарный день, извлеките его через .dt.date.

import pandas as pds

records = pds.DataFrame({
    "event_ts": [1743004800000, 1753004800000]
})

# Верно: сообщаем pandas, что метки времени в миллисекундах
clean = records.copy()
clean["event_ts"] = pds.to_datetime(clean["event_ts"], unit="ms")

# Если нужна только дата, создайте отдельный столбец
clean["event_date"] = clean["event_ts"].dt.date

print(clean)

Результат:

                event_ts  event_date
0 2025-03-26 16:00:00    2025-03-26
1 2025-07-20 09:46:40    2025-07-20

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

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

Ошибки при работе со временем часто незаметны, но обходятся дорого. Если принять числовую метку за строковую дату, вы либо быстро получите ValueError, либо, что хуже, внесёте тихую порчу данных при неверном приведении. Явное указание единицы измерения делает разбор однозначным, улучшает самодокументируемость кода и не допускает, чтобы последующая логика опиралась на некорректные даты.

Итоги

Если в наборе данных время хранится как большие целые числа, например 1743004800000, воспринимайте их как Unix-метки в миллисекундах и конвертируйте с unit="ms». Параметр format оставляйте для действительно читаемых строк вида '2025-03-26'. После преобразования получайте чистую дату только тогда, когда это нужно, сохраняя полноценный datetime для остальных операций.

Материал основан на вопросе на StackOverflow от user824624 и ответе Panda Kim.