Engineer as a Lifestyle @tenkoma

What We Find Changes Who We Become -- Peter Morville著『アンビエント・ファインダビリティ 』

Excelの日付に1900年2月29日があること

win32comでExcelにアクセスした実験です.
Excelファイルのセルから日付を取得してpythonのdatetime.dateインスタンスにしたい.
とりあえずValueをゲット.(初期化とかは省略)

>>> tmp = xlApp.Workbooks(u"example.xls").Worksheets(u"hoge").Range("A1").Value
>>> print tmp
04/29/06 00:00:00

確かに日付を取得できるけど,この文字列を利用すると2100年問題を抱えてしまう(笑)
Excelの日付は内部的にエポックからの整数値で表されるとのこと.つまり

>>> tmp = xlApp.Workbooks(u"example.xls").Worksheets(u"hoge").Range("A1").formula
>>> print tmp
u'38836'

これで取得できました.問題ないか確かめてみよう.確か1900年1月1日から数えてるんだから,

>>> import datetime
>>> exceldef = datetime.date(1900,1,1)
>>> exceldef+datetime.timedelta(38836)
datetime.date(2006, 5, 1)

日付がずれています.Excelのデータは1900/1/1が「1」になって,datetime.timedeltaは差を表してるわけだから,1を引かなければいけないようです.

>>> exceldef+datetime.timedelta(38836-1)
datetime.date(2006, 4, 30)

まだ,1日ずれている.セルに入力して確かめたところpythonのdatetime.dateでは1900/2/29が無い*1Excelでは1900/2/29が表現できてしまうようだ.*2.なんでこんなになってるのかと思って検索してみるとJoelのエッセイが引っかかった.今年の6月のだから新しいな…最近全然エッセイWatchしてないな…
はじめてのBillGレビューのこと - The Joel on Software Translation Project
なるほど…1900年.しかしコードで-2なんてマジックナンバー使うのもいやで.
このオブジェクトにはFormatというメソッドが一つだけあります.http://www.python.jp/doc/2.4/lib/module-time.html#l2h-1915に日付時刻表現のdirectiveがあるのでこれを当てはめてみると,

>>> tmp.Format("%Y")
'2006'
>>> isinstance(tmp, datetime.time)
False

という風にstrftimeメソッドのdirectiveがほぼ通用することがわかりました.結局Excelのセルの値をpythonのdatetime.data型に入れるには,

tmp = xlApp.Workbooks(u"example.xls").Worksheets(u"hoge").Range("A1").Value
date = datetime.date(int(tmp.Format("%Y")),int(tmp.Format("%m")),int(tmp.Format("%d")))

というふうに使えばいいかもしれません.IDLEでセルから取得したオブジェクトをそのまま表示するとと表示されるのですが,一体どんなオブジェクトなのかよくわかりません.なにか調べる方法があれば教えてください.もっとスマートな変換方法があればそれも教えてください.

*1:西暦の100で割り切れて400で割り切れない年はうるう年ではないのでこの動作は正しい

*2:セルに60と入力して表示形式を日付にしてみよう