日付時刻の処理の決着

d:id:imait:20090316:1237203606でいっていた、日付時刻の扱いの問題。とりあえず決着させたので、そいつを紹介しておきたいと思います。

まずは、正規表現で大雑把にばらした数値をチェックするという話から。関数を次のように書き換えました。

import re
import datetime
import calendar

redt = re.compile(u'(\d{4})\D?([01]?\d)\D?([0-3]?\d)\D?([0-2]?\d?)\D?([0-5]?\d?)\D?([0-5]?\d?)')
def strtdatetime(string):
    if isinstance(string, basestring):
        m = redt.match(string)
        if m is not None:
            l = [2001, 01, 01, 00, 00, 00]
            for i in range(len(m.groups())):
                try:
                    l[i] = int(m.group(i + 1))
                except ValueError:
                    l[i] = 00
            if l[0] >= 1900:
                if l[1] > 0 and l[1] <= 12:
                    lastdate = 31
                    if l[1] == 2:
                        if calendar.isleap(l[0]):
                            lastdate = 29
                        else:
                            lastdate = 28
                    elif l[1] == 4 or l[1] == 6 or l[1] == 9 or l[1] == 11:
                        lastdate = 30
                    if l[2] > 0 and l[2] <= lastdate:
                        return datetime.datetime(l[0], l[1], l[2], \
                                                 l[3], l[4], l[5])
    return u''

結局、年、月、日、それぞれをチェックすることで解決しました。年は1900年以上であること、月は1月から12月までの範囲におさまること、そして日はちょっとややこしい。大の月、小の月があって、そして2月、うるう年の問題がありますからね。なので、例の4で割り切れるやら、100で、400でどうたらこうたらというお定まりを、orやらandやら使って書いてみて、試してみたのですが、calendarモジュールがうるう年かどうかを判定する関数、isleap()を持ってることがわかったので、無駄な考え休むに似たりということが改めて理解されることとなりました。

やっぱり、リファレンスはちゃんと読んでおかないといけませんね。

さて、後者、datetimeオブジェクトのisoformat()メソッドの問題です。ミリセカンドが0の場合には、確かにYYYY-MM-DDTHH:MM:SSと返されることが確認されました。なので、昨日のコードではリストの範囲をオーバーしてエラーが出てしまいます。ということで、ふたつの処置を試みました。

ひとつは、isoformat(' ')で作られた文字列にミリセカンドがなかった場合、足りない分を補ってやるという処置。これは文字列からdatetimeにする時ですね。

import re
import datetime

cd = re.split('[\-\s:\.]', created_date)
if len(cd) == 6:
    cd.append(u'000000')
created_date = datetime.datetime(int(cd[0]), \
                                 int(cd[1]), \
                                 int(cd[2]), \
                                 int(cd[3]), \
                                 int(cd[4]), \
                                 int(cd[5]), \
                                 int(cd[6]))

もうひとつは、isoformat(' ')で文字列にした時に、タイムゾーンがついていたらそれを切り落し、ミリセカンドがついていなかったら、それを付け加えるという処置です。

import datetime
import re

created_date = datetime.datetime.today().isoformat(' ')
if re.match(u'.*[\-\+]\d\d:\d\d$', created_date):
    created_date = created_date[0:-6]
if not re.match(u'.*\.\d{6}$', created_date):
    created_date = created_date + u'.000000'

保存時、読み出し時、それぞれで対処するようにしたら安心かなと思ったわけです。多分これで大丈夫だと思います。