Расхождение дат в Excel и ошибки на единицу
Разбираемся в системах отсчёта дат в Excel и в том, как исправлять ошибки смещения дат на один день при чтении файлов .xls и .xlsx на разных системах.

Расхождение дат в Excel и ошибки на единицу
Файлы .xls или .xlsx хранят значения дат как количество дней, прошедших с начала эпохи. И когда же начинается эта эпоха? У Excel на этот счёт есть целых два ответа: 1900 и 1904, в зависимости от настроек. История довольно длинная, но подробности можно найти здесь.
Почему эпоха Excel вообще важна?
Если вы читаете современный .xls файл с помощью старого ридера, даты могут разъехаться на 4 года. Некоторые библиотеки пытаются это исправить, прибавляя 4 года, но забывают про коррекцию високосного дня, и от этого почему-то становится только хуже.
Представьте, что вы загружаете xls-файл, где дата сопоставлена с продажами:
"21/09/2025" => 1 "22/09/2025" => 2 "23/09/2025" => 3 ...И используете вот такую функцию:
function get_excel_date_by_date($date) {
$excel_arr = read_file('path_to_file');
foreach($excel_arr as $row) { if($row[0] === $formatted_date) return $row }
}Если вы ошиблись при чтении в read_file или используете старую библиотеку (у меня это была Spreadsheet_Excel_Reader для php 4!), в итоге получите ровно однодневное расхождение в датах. То есть для вызова get_excel_date_by_date("21/09/2025") вы получите 2!
Проверить формат ваших дат можно здесь:

Одно число, две даты, между ними четыре года и день. Это путаница с 1904. Ошибка на один день прячется на 60.
Как это исправить?
Если не хочется менять ридер, самый быстрый способ — обработать смещение на единицу напрямую. Некоторые ридеры умеют переключаться между режимами 1900 и 1904, так что сначала проверьте это. Чтобы увидеть, что одно и то же число значит в каждой системе, вставьте его в конвертер дат Excel.
А ещё лучше — уйти от Excel, если есть такая возможность. CSV творит чудеса: это обычный текст, в нём нет эпохи, о которой можно спорить, и для его обработки не нужно ни одной зависимости. Если только на той стороне не банк: тогда это ASCII с фиксированной шириной.
Комментарии
Комментарии живут в GitHub Discussions (через giscus), так что для участия нужен аккаунт GitHub.