# Buggy code
def _extract_multi_indexer_columns(
    self, header, index_names, col_names, passed_names=False
):
    """
    extract and return the names, index_names, col_names
    header is a list-of-lists returned from the parsers
    """
    if len(header) < 2:
        return header[0], index_names, col_names, passed_names

    # the names are the tuples of the header that are not the index cols
    # 0 is the name of the index, assuming index_col is a list of column
    # numbers
    ic = self.index_col
    if ic is None:
        ic = []

    if not isinstance(ic, (list, tuple, np.ndarray)):
        ic = [ic]
    sic = set(ic)

    # clean the index_names
    index_names = header.pop(-1)
    index_names, names, index_col = _clean_index_names(
        index_names, self.index_col, self.unnamed_cols
    )

    # extract the columns
    field_count = len(header[0])

    def extract(r):
        return tuple(r[i] for i in range(field_count) if i not in sic)

    columns = list(zip(*(extract(r) for r in header)))
    names = ic + columns

    # If we find unnamed columns all in a single
    # level, then our header was too long.
    for n in range(len(columns[0])):
        if all(ensure_str(col[n]) in self.unnamed_cols for col in columns):
            header = ",".join(str(x) for x in self.header)
            raise ParserError(
                f"Passed header=[{header}] are too many rows "
                "for this multi_index of columns"
            )

    # Clean the column names (if we have an index_col).
    if len(ic):
        col_names = [
            r[0] if (len(r[0]) and r[0] not in self.unnamed_cols) else None
            for r in header
        ]
    else:
        col_names = [None] * len(header)

    passed_names = True

    return names, index_names, col_names, passed_names


# Traceback (TypeError)
Traceback (most recent call last):
File "test.py", line 3, in <module>
pd.read_excel('test.xlsx', header=[0, 1], index_col=0, engine='xlrd')
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/excel/_base.py", line 334, in read_excel
**kwds,
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/excel/_base.py", line 888, in parse
**kwds,
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/excel/_base.py", line 512, in parse
**kwds,
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/parsers.py", line 2201, in TextParser
return TextFileReader(*args, **kwds)
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/parsers.py", line 880, in __init__
self._make_engine(self.engine)
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/parsers.py", line 1126, in _make_engine
self._engine = klass(self.f, **self.options)
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/parsers.py", line 2298, in __init__
self.columns, self.index_names, self.col_names
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/parsers.py", line 1508, in _extract_multi_indexer_columns
for r in header
File "/home/sasha/miniconda3/lib/python3.7/site-packages/pandas/io/parsers.py", line 1508, in <listcomp>
for r in header
TypeError: object of type 'datetime.datetime' has no len()

# Fixed code
def _extract_multi_indexer_columns(
    self, header, index_names, col_names, passed_names=False
):
    """
    extract and return the names, index_names, col_names
    header is a list-of-lists returned from the parsers
    """
    if len(header) < 2:
        return header[0], index_names, col_names, passed_names

    # the names are the tuples of the header that are not the index cols
    # 0 is the name of the index, assuming index_col is a list of column
    # numbers
    ic = self.index_col
    if ic is None:
        ic = []

    if not isinstance(ic, (list, tuple, np.ndarray)):
        ic = [ic]
    sic = set(ic)

    # clean the index_names
    index_names = header.pop(-1)
    index_names, names, index_col = _clean_index_names(
        index_names, self.index_col, self.unnamed_cols
    )

    # extract the columns
    field_count = len(header[0])

    def extract(r):
        return tuple(r[i] for i in range(field_count) if i not in sic)

    columns = list(zip(*(extract(r) for r in header)))
    names = ic + columns

    # If we find unnamed columns all in a single
    # level, then our header was too long.
    for n in range(len(columns[0])):
        if all(ensure_str(col[n]) in self.unnamed_cols for col in columns):
            header = ",".join(str(x) for x in self.header)
            raise ParserError(
                f"Passed header=[{header}] are too many rows "
                "for this multi_index of columns"
            )

    # Clean the column names (if we have an index_col).
    if len(ic):
        col_names = [
            r[0] if ((r[0] is not None) and r[0] not in self.unnamed_cols) else None
            for r in header
        ]
    else:
        col_names = [None] * len(header)

    passed_names = True

    return names, index_names, col_names, passed_names