SQLiteで日時を扱う方法

SQLiteには、DATEやTIMESTAMPなどの、日時を扱うデータ型はありません。一方で、Objective-Cのプログラムでは、NSDateオブジェクトで日時が表現されます。
では、NSDateオブジェクトの値をSQLLiteのテーブルのカラムに格納するときに、どのようにマッピングするのが適切でしょうか?
すでに、答えを持っている方も多いと思いますが、自分のメモを兼ねて、書いておこうと思います。

実際には、何通りか方法はあると思いますが、-[NSDate timeIntervalSince1970]メソッドで返される数値を、日時のデータとするのが適切なのではと思います。
具体的には、以下のようなことになります。

  • データを格納するとき
    -[NSDate timeIntervalSince1970]メソッドで返される数値を設定する。(注: この数値は、UTCである)
  • データを取得するとき
    取得した数値から、+[NSDate dateWithTimeIntervalSince1970:]メソッドでNSDateオブジェクトを生成する。

例えば、FMDB for iPhoneという、著名なSQLiteラッパーライブラリがありますが、その中でも、これと同じ方法が使われています。

ただし、この方法を使うと、ちょっとだけ扱いにくい部分もあります。SQLite Managerなどのツールから、データをブラウズしたときに、日時としてではなく、数値として表示されてしまうことです。
それに対応するためには、日付関数を使います。
(詳細は、SQLite Query Language: Date And Time Functionsを参照してください)

データを参照する時には、datetime()関数を使います。

SELECT datetime(<カラム名>, 'unixepoch', 'localtime') from <テーブル名> .....;

注: datetime()関数の第3引数である'localtime'を省略すると、UTCで表示されます。
反対にデータを登録したい場合は、strftime()関数を使うことで、文字列編集された日時を-[NSDate timeIntervalSince1970]メソッドで返されるのと同じ数値に変換することができます。

insert into <テーブル名> (<カラム名>, ...) values (strftime('%s', '2009-05-31 01:23:45'), ...);

注: ここで指定する日時の文字列は、UTCで指定します。

以下のようなSQL文を実行してみると、もう少し、動作イメージが掴みやすいかもしれません。

select
    strftime('%s', '2009-05-31 01:23:45') as num_of_utc,
    datetime(strftime('%s', '2009-05-31 01:23:45'), 'unixepoch', 'localtime') as str_as_localtime;

ツールでの扱いは、若干面倒ですが、Objective-Cプログラムの中では、この方法が扱いやすいし、処理効率もいいのではと思います。