Excelでは日付に見えた列が、pandasのread_excelで46308のような数値になる。そんなときは、元のセル書式、読み込まれた値の型、ブックの日付方式を確認してから変換します。列全体を同じ日付変換へ渡すだけでは、既に日時になった行や、日付とは確認できない数値まで混ぜてしまいます。

対象は、Pythonで表を読み込んだことがあり、業務の入力日や研修日を扱いたい人です。実在の業務データを使わず、編集部作成のXLSXを使います。Excelアプリを操作した受講記録ではなく、Pythonからファイルを作成し、読み込んで期待値と照合した教材です。

確認日:2026年10月13日。pandasのread_excelとto_datetime、openpyxlの日付仕様を参照しました。pandas公式は3.0.6、openpyxl資料は3.1.3表示です。掲載コードはPython 3.12.14・pandas 2.2.3・openpyxl 3.1.5で参照実行しました。

Excelの8行をセル書式と読み込み型で分け、実際のブックの日付方式で変換する。確認4行と保留4行、1900と1904の差1462日を別に照合。
横にスクロールできます。編集部作成。元の値を残し、変換できた行と確認が必要な行を分けます。

日付へ変換する前に、Excelに入っている値を確かめる

表示が同じでも、日付・数値・文字列は同じ入力ではない

表で「2026/10/13」と見える行にも、日時として読み込まれる行と、文字列のまま残る行があります。逆に、46308という数値が日付を表す列もあります。画面上の見た目だけを根拠に、Python側の値へ同じ処理を適用しないことが出発点です。

元のブックを変えずに、列名と一行の意味を控えます。本稿の入力日は、2020年から2030年までの日付を扱う練習用の列です。46308はこの列に置いた日付の数値ですが、会員番号の46308なら日付へ直す理由がありません。数値の大きさから列の意味を推測する方法は採用しません。

見る順序は、対象シート、列名、ID、セルの値、セル書式、Pythonで読み込まれた型です。日時の列を読みたいのに別シートの受付番号を読んでいた場合、日付方式を直しても問題は解決しません。元の位置へ戻れるよう、今回の教材はE01からE08までのIDを残しています。

また、空欄を日付の0と同じにしないでください。空欄は未記録、0は入力された数値という違いがあります。本稿では両方を保留しますが、保留理由は別々に記録します。いずれかを今日の日付で埋めれば処理は進むかもしれませんが、元の記録にない日付を作ることになります。

日付の意味にも注意します。研修の開催日、申込日、受領日、締切日は、同じ形式で表示されても別の列です。「日付に変換できた」という確認と、「必要な日付が正しい列から来た」という確認を分けてください。どの日時を集計に使うかが決まってから、関数の選択に進みます。

列全体のdtypeだけで、すべての行が同じ入力だと判断するのも早計です。混在した列はobjectとして扱われることがあります。行ごとの型とIDを少数で確認すると、日時になっている行まで数値として変換し直す誤りを避けやすくなります。

セル書式とread_excelのdtypeを別々に見る

本稿は、XLSXをopenpyxlで読み込むエンジンを明示し、pandasではdtype=objectを指定します。それでも日付書式のセルが読み込み時にdatetimeになる例です。objectは「Excelの生の数値を一切解釈せず取り出す」という意味ではありません。エンジンが日時として読んだ値も、列の中に入ります。

確認する型は、表示文字列ではなく値そのものの型です。例えば、strで表示したdatetimeが「2026-10-13 09:30:00」になっても、その値が元から文字列で保存されていたとは言えません。文字列化した後では、日時と文字列の区別を失うため、先に型を控えます。

元セルの書式は、openpyxlで読み込んだ教材のB2からB9までのnumber_formatで確認できます。ただし、ここで見ている値もライブラリが読み取った結果です。本稿はXLSX内部のXMLを直接解析したり、Excelアプリの表示を実測したりした検証ではありません。この範囲を超えて読み込みエンジン全般の挙動を断定しません。

日付に見える値を調べる三つの場所
確認する場所分かることそれだけでは決められないこと
元セルの書式日付・時刻として表示する設定か列の業務上の意味、実際の日時の正しさ
読み込み後の値の型datetime・int・float・strなど数値が日付のシリアル値かどうか
ブックのepochこのファイルが使う日付方式空欄や年なし文字列を補ってよいか

列のconvertersを使う場合は、dtypeとの関係も確認します。公式にはconvertersが指定された列ではdtype変換に代わって適用されると説明されています。最初から変換処理を重ねると、何が読み込み時に変わったかを追いにくくなるため、この教材は読み込みと変換を別の工程にしています。

数式セルの扱いは別の問題です。数式の結果がNoneになる場合、セルに表示される日付の計算結果を得られていない可能性があります。本稿のファイルには数式を使いません。数式の保存済み結果を調べる必要がある人は、openpyxlのdata_onlyとNoneの確認へ進んでください。

1900・1904方式は、使っているOSから推測しない

Excelには1900日付方式と1904日付方式があります。Microsoftの日付方式の説明では、同じ日付を表すシリアル値の差は1462日です。数値が同じでも、どちらの方式として読んだかによって日付が変わります。

「Macだから1904」「Windowsだから1900」と決める方法は使いません。受け取ったファイルは別の環境で作られているかもしれず、Macでも1900方式を扱います。このファイルの方式を、読み込んだブックのepochから確認するのが本稿の手順です。

教材では二つのブックを別々に作ります。同じ2026年10月13日9時30分を、1900方式の数値と1904方式の数値で保存します。違うのは日付そのものではなく、日付を数値にする基準です。二つの数値が異なることは、記録された日時が異なることを意味しません。

1904方式の数値へ1900方式の基準を適用すると、本稿の例では2022年10月12日9時30分になります。これは変換エラーではなく、有効な日時として生成されます。値がNaTにならないため、欠損の件数だけを点検しても見つけられません。

本稿は2026年の日付を検算する教材です。1900年付近の日付にはExcelの歴史的な扱いの注意点があり、現在の日付へ使える説明をそのまま歴史資料へ広げません。1900方式の起点を単に固定した足し算へ置き換えるより、ライブラリの変換と実際のブックの方式を対応させます。

この確認のために、元ブックの日付方式を変更する必要はありません。まず読み取り側の基準を合わせ、元の入力と結果を照合します。業務ファイルの設定を変えると別のセルへの影響もあり得るため、教材と実務の変更作業を混ぜないでください。

8行の教材を作り、既に日時になった行を残す

次のコードは、作業フォルダに新しい教材ファイルを作ります。既存の業務ファイルは指定しません。同名の教材ファイルがある場合はそのファイルを上書きするので、練習用の空のフォルダで実行してください。必要なライブラリが利用できる環境を前提にしています。

from pathlib import Path
from datetime import datetime, date, time
from numbers import Real
import math, re
import pandas as pd
from openpyxl import Workbook, load_workbook
from openpyxl.utils.datetime import (
    to_excel, from_excel, CALENDAR_WINDOWS_1900, CALENDAR_MAC_1904
)

work = Path('excel-date-practice')
work.mkdir(exist_ok=True)
target=datetime(2026,10,13,9,30)
wb=Workbook();wb.epoch=CALENDAR_WINDOWS_1900
ws=wb.active;ws.title='教材'
ws.append(['ID','入力日'])
ws.append(['E01',target]);ws['B2'].number_format='yyyy-mm-dd hh:mm'
ws.append(['E02',to_excel(datetime(2026,10,13),wb.epoch)])
ws.append(['E03',to_excel(target,wb.epoch)])
ws.append(['E04','2026/10/13'])
ws.append(['E05','10/13'])
ws.append(['E06','2026/02/30'])
ws.append(['E07',None])
ws.append(['E08',0])
wb.save(work/'mixed-1900.xlsx')
read=load_workbook(work/'mixed-1900.xlsx',data_only=True)
df=pd.read_excel(work/'mixed-1900.xlsx',sheet_name='教材',dtype=object,engine='openpyxl')

E01は日時を日付書式のセルへ入れ、E02は整数のシリアル値、E03は時刻を含む小数、E04は年/月/日の文字列です。E05以降は確認できない入力を意図的に用意しています。読者のデータでどの形式が多いかを示す統計ではありません。

編集部の自作8行と期待する判定
ID・入力参照実行で読んだ型確認結果
E01:日付書式の日時datetime2026-10-13 09:30
E02:46308int2026-10-13 00:00
E03:46308.395833…float2026-10-13 09:30
E04:2026/10/13str2026-10-13(日付のみ)
E05:10/13str年がないため保留
E06:2026/02/30str存在しない日付のため保留
E07:空欄floatのnan未記録として保留
E08:0int日付を含まないため保留

まずIDと入力の値を一行ずつ見ます。読み込み時にE01がdatetimeになっていることを確かめたら、変換工程ではその日時を残します。整数や小数として読んだE02・E03と同じ計算を、E01へもう一度適用しないことが重要です。

E04を変換した後の0時は、教材上で日付だけをdatetimeへ揃えた表現です。実際に0時に入力された記録ではありません。時刻の有無も判断材料として必要な仕事では、「日付のみ」という理由を結果と一緒に残してください。

小数の時刻とタイムゾーンを混同しない

Excelのシリアル値は、小数部分で一日の中の時刻を表せます。本稿の46308.395833…は9時30分を含む例です。変換の前に整数へ丸めると、その時刻を落とします。日付だけ必要なのか、時刻まで必要なのかを決める前に小数を捨てないでください。

この例では9時30分の期待値を先に決め、変換後の日時と照合します。表示を年月日だけにすると、時刻が失われても見た目が一致してしまいます。締切の確認や時刻順の並べ替えに使うなら、年月日と時分を含む値を確認します。

0.5のように日付部分のない値は、時刻だけを示す場合があります。本稿の0はfrom_excelで日付を含まないtimeとして扱われるため、datetimeと同じ確認済み欄へ入れません。「0だから未入力」「0だから起点の日付」という推測も避けています。

タイムゾーンは小数の時刻とは別の情報です。openpyxlの公式資料は、Excelの日付・時刻表現がタイムゾーンに対応しないことを説明しています。本稿で扱う日時もタイムゾーンなしです。海外拠点のデータを比べる場合、この教材だけで日本時間やUTCへ確定したことにはなりません。

例えば、締切を日本時間として運用しているなら、その前提はファイル外の業務仕様から確認します。「9:30」という値から地域を読み取ることはできません。変換後に勝手な時差を加えず、列の定義と対象拠点を確認する工程を別に設けます。

日付だけの一覧を作る場合でも、元の時刻付き値は残せます。表示用の列を日付にし、確認用の列には元の日時と時刻の有無を残す設計にすると、あとで締切時刻を問われたときに説明できます。必要な粒度を決めることが、単に見栄えのよい形式へ揃えるより先です。

数値・文字列を分けて変換し、期待する日付と照合する

確認済みの日付列だけを、方式付きで変換する

次の処理は、今回の列をExcel日付の列と確認したうえで使う教材です。どの数値列でも日付へ直す関数ではありません。数値はこのブックのepochで変換し、文字列は年4桁/月2桁/日2桁の形式だけを受け付けます。分からない値に推測の年を足しません。

数値から得た日付については、教材の対象期間である2020年から2030年までかを点検します。この範囲は仕事の標準ではありません。範囲外の入力を誤りと断定するためではなく、想定外の値を確認へ戻す教材上の条件です。実務の対象期間は、その入力仕様から決めてください。

def checked_date(value, epoch):
    if pd.isna(value):
        return None,'未記録'
    if isinstance(value,datetime):
        return value,'読込済み日時'
    if isinstance(value,date):
        return datetime.combine(value,time()),'日付のみ'
    if isinstance(value,Real) and not isinstance(value,bool):
        if not math.isfinite(value):
            return None,'有限数ではない'
        try:
            converted=from_excel(value,epoch=epoch)
        except (ValueError,OverflowError):
            return None,'変換範囲を確認'
        if not isinstance(converted,datetime):
            return None,'日付を含まない'
        if not (datetime(2020,1,1)<=converted<datetime(2031,1,1)):
            return None,'教材の対象期間外'
        return converted,'確認済みExcel日付列の数値'
    if isinstance(value,str) and re.fullmatch(r'\d{4}/\d{2}/\d{2}',value):
        try:
            return datetime.strptime(value,'%Y/%m/%d'),'年/月/日文字列'
        except ValueError:
            return None,'存在しない日付'
    return None,'形式または年を確認'

records=[]
for row in df.itertuples(index=False,name=None):
    ident,value=row
    converted,reason=checked_date(value,read.epoch)
    records.append({'ID':ident,'input':str(value),'loadedType':type(value).__name__,'checked':converted,'reason':reason})
result=pd.DataFrame(records)
assert result['checked'].notna().sum()==4
assert result['checked'].isna().sum()==4
assert result.loc[result['ID']=='E03','checked'].iloc[0]==target
wrong=pd.to_datetime(df.loc[df['ID']=='E02','入力日'].iloc[0])
assert not pd.isna(wrong) and wrong.year==1970

resultには、元の入力を表示用に文字列へした欄、読み込み型、確認済み日時、判定理由が残ります。元ブックとdfも変えません。確認済み列が欠損になった行は、その理由を使って調べます。「変換できなかった4行」とだけ報告するより、年なし、存在しない日付、未記録、時刻のみを分けた方が次の確認先を決めやすくなります。

この関数は、一つの値を扱う小さい教材用です。配列や辞書、複雑な独自オブジェクトを受け付ける汎用変換ではありません。すべての入力形式を自動で理解させようとする前に、列へ入ってよい値の種類を決めるために使ってください。

既に日時として読まれた値と、年/月/日文字列から変換した値を残す一方、数値には追加の期間確認をしています。実務では全形式の変換後へ同じ対象期間の点検を加えるなど、受入条件を統一する設計も必要です。本稿の4行という結果を、そのまま実務の受入基準にしないでください。

1970年に変換されても、NaTにはならない

掲載コードでは、E02の46308を単独でpd.to_datetimeへ渡す確認も入れています。参照実行の結果は「1970-01-01 00:00:00.000046308」です。pandasの既定の数値解釈はExcelのシリアル日付と同じではないため、欲しい2026年10月13日になりません。

この値は、欠損を表すNaTではありません。errors=coerceで変換に失敗した値を欠損へ変える処理があっても、「有効な日付として生成されたが意味が違う」ケースは別に点検する必要があります。欠損0件を、日付の確認完了という判定に使わない理由です。

一方、unitを日、originを日付の起点へ変える方法もあります。ただし、既にdatetimeの行や年/月/日の文字列を含んだ列へ、同じ数値変換の規則を一括適用しないでください。方式の確認、入力の種類ごとの分離、結果の照合が必要という点は変わりません。

エラーを起こさない方法を探すだけだと、日付がずれたまま集計が完成することがあります。月ごとの件数を集める仕事なら、2026年の記録が1970年の集計へ入る結果です。件数の総数が同じでも、対象月は異なります。対象期間内かという確認が、件数の確認を補います。

文字列のNaTや曖昧な月日を調べたい場合は、pandasの日付文字列とNaTの確認を参照してください。本稿はXLSXの方式と読み込み型に焦点を置いています。文字列の問題とシリアル値の問題は、原因を分けて調べます。

同じ日時を1904方式で保存し、1462日の差を検算する

次に、同じ2026年10月13日9時30分を1904方式のブックへ保存します。この教材の数値は44846.395833…です。1900方式の46308.395833…との差は1462になります。小数部分が同じなのは、どちらも同じ時刻を表しているためです。

other=Workbook();other.epoch=CALENDAR_MAC_1904
s=other.active;s.title='教材';s.append(['ID','入力日']);s.append(['M01',to_excel(target,other.epoch)])
other.save(work/'serial-1904.xlsx')
other_read=load_workbook(work/'serial-1904.xlsx',data_only=True)
other_df=pd.read_excel(work/'serial-1904.xlsx',sheet_name='教材',dtype=object,engine='openpyxl')
number=other_df.loc[0,'入力日']
right=from_excel(number,epoch=other_read.epoch)
wrong_epoch=from_excel(number,epoch=read.epoch)
assert right==target
assert (right-wrong_epoch).days==1462
assert to_excel(target,read.epoch)-to_excel(target,other_read.epoch)==1462

other_readのepochを使えば、元のtargetと一致します。別ブックのreadのepochを使ったwrong_epochは、2022年10月12日9時30分です。コードは、正しい日時との一致と、誤った方式を使ったときの1462日差を別々に確認しています。

ここでも「差が1462日だから元ファイルの設定を変更する」という順序にしません。まず、そのブックがどの方式を記録しているかを読み、読み取り側の変換に使います。元の日時へ戻ることを確認してから、仕事で使う結果列を作る順序です。

実務で日付が約4年ずれていたとしても、原因がこの方式だけとは限りません。対象列の選択、入力の誤り、別ファイルのepochの使い回し、時差や年の補完なども確認します。症状から原因を一つに決めず、元の数値とブックの方式を対応させてください。

本稿のコードは、二つの自作ブックの読み書きを参照実行したものです。未知のファイル形式や別のExcelエンジン、すべてのpandas版で同じ型になると保証するものではありません。自分の環境では、同じ小さい教材を先に読み、値・型・期待する日時を確かめます。

保留4行を埋めずに、元のIDへ戻して報告する

8行のうち、E05の「10/13」は年が不足しています。対象の表が2026年の一覧だとしても、過年度から転記された記録かもしれません。列の仕様として年を補ってよい根拠が得られるまでは、2026年と決めずに保留します。

E06の「2026/02/30」は存在しない日付です。翌月へ繰り上げたり、月末へ置き換えたりすれば何らかの日付を作れますが、本稿はそのような修正をしません。入力者へ確認する対象として、元の文字列とIDを残します。

E07の空欄は、申込していない、まだ記録していない、抽出で落ちたなど、複数の意味が考えられます。教材では「未記録」という読み取り結果だけを残し、業務上の原因は決めません。E08の0も、未記録と同じ原因だとは扱いません。

確認結果の件数は、入力8行=確認4行+保留4行として照合します。確認4行に時刻があるものと日付のみのものが混ざるため、必要ならその内訳も報告します。すべての日付について時刻まで確認できた、という説明へまとめないことが大切です。

報告表には、ファイル名、対象シート、ID、元の値、読み込み型、日付方式、結果、保留理由を残すと追跡しやすくなります。教材のinput欄は表示用です。元のセルへ戻る根拠として、IDとファイルを一緒に残してください。

後続の欠損・重複検査と組み合わせるときは、CSVの欠損と重複を数える練習が使えます。IDの先頭の0が消える問題は日付方式とは別なので、CSVの先頭ゼロと文字コードで確認します。変換の種類を分けると、修正の影響を小さくできます。

次の練習は、表示形式より受入条件を説明できること

日付の処理を学ぶときは、「Excelを読み込めた」「datetimeになった」で練習を終えず、どの入力を確認済みとし、どの入力を止めるかまで説明します。今回なら、日付列の数値という根拠、ブックのepoch、年の有無、時刻の保持を一組で確認できます。

次の課題には、自分で三種類の入力を追加する方法があります。想定期間の外の日付、時刻だけの小数、真偽値です。追加前に確認か保留かを決め、変換後の結果と理由を照合します。実務のファイルを直す前に、入力仕様を小さい教材で確かめる練習になります。

課題の提出物は、コードだけでなく、期待する結果の表です。例えば「年なしの入力は保留」「時刻が必要な列では小数を保持」「ブックごとにepochを読む」という条件を、関数の外にも短く書きます。第三者がコードを実行しなくても確認方針を読めるようにしてください。

Pythonをこれから学ぶ人は、社会人向けのCSV読み込み練習から、列の選択、型、元データを残す考え方を順に試せます。表の処理をすべてPythonへ置き換える必要はありません。目的に応じた学習先は、事務職の業務自動化で何を学ぶかで整理できます。

人に質問するときは、個人情報や実在の業務内容を抜いた小さいファイルと、期待する日付を渡します。「日付がおかしい」だけでなく「E03の9時30分を残したい」「1904方式のこの数値を読んでいる」と具体化すると、入力の問題と変換の問題を分けやすくなります。

本稿の確認結果は、8行の教材と二つの方式についての参照実行です。仕事で使う結果の採否は、元の記録と入力仕様に戻って判断します。日付の見た目を揃えることと、記録された日時を確認することを分けたまま、後続の集計へ進めてください。

2026年10月13日に確認したPython Winnerの公式講座一覧には、Pythonの基礎・応用やデータ分析のコースがあります。Pythonを基礎から学び、表の読み込みや型の扱いを質問したい人の比較候補です。このExcel日付教材の添削が含まれるとは確認していないため、希望する演習・質問対応・受講期間・総費用を申込前に確認してください。

一つの不具合について確認したい人と、Python全体を学び直したい人では必要な支援が違います。日付列の意味やブックの方式を自分で説明できるなら、まず自作の小さい入力で練習を続ける方法もあります。講座は、基礎から指導を受けたい場合の選択肢として比べます。

PR:【PythonWinner】

無料カウンセリング・受講相談と、有料講座への申込みは別です。元の業務データを渡さず、架空の入力と期待する日時を用意して、質問したい範囲を確認します。

Excelの日付読み込みでよくある質問

Q. dtype=objectなら日付の元数値をそのまま取得できますか?
A.

その意味ではありません。読み込みエンジンが日付書式のセルをdatetimeとして解釈する場合があります。本稿の参照実行でもE01はdatetimeです。元のセル書式、行ごとの型、ブックの方式を別々に確認します。

Q. 46308をto_datetimeへ渡すだけで直せますか?
A.

既定の数値解釈はExcelの日付方式とは異なります。参照実行では1970年の日時になりました。日付列の数値と確認し、そのブックのepochを使って変換した後、期待する日時と照合します。

Q. Macで作ったファイルは1904方式ですか?
A.

OSだけでは判断しません。Macでも1900方式を扱い、受け取ったファイルは別環境で作られていることもあります。対象のブックに記録されたepochを確認してください。

Q. 日付だけ必要なら小数を先に捨ててもよいですか?
A.

元の時刻情報を失います。時刻を保持した確認結果を作り、目的に応じて表示用の日付列を別に用意すると、元の日時へ戻れます。タイムゾーンは数値の小数からは分かりません。

Q. 保留の値を今日の日付で埋めてもよいですか?
A.

元記録にない日付を作るため、この教材では埋めません。年なし、存在しない日付、未記録、日付を含まない値を分け、IDと元の入力を使って確認します。補完してよい条件は業務の仕様から決めます。