ラベル 時間 の投稿を表示しています。 すべての投稿を表示
ラベル 時間 の投稿を表示しています。 すべての投稿を表示

2009年4月18日土曜日

時刻を扱う - 特定の日付に属さない時間

例えば、「いつも朝の 6:30 から 6:40 までラジオ体操をする」という事柄を表現したいとする。「いつも」とあるように、この表現は特定の日付と関連したものではない。過去のある期間においてそういう事実があったことを記録したいわけでもなく、未来の予定について述べているわけでもない。「日常」という言葉通り、典型的な「生活パターン」というような抽象的なレベルの事象をどのように表現して扱うかという問題。

 

Access の日付/時刻型

さて、Access でデータを管理しようと思うと、上記の「時刻」をどのデータ型で扱えばいいのだろうか?最初に思い付くが「日付/時刻型」。(cf.  Access で使用できるフィールドのデータ型 (MDB) - Access - Microsoft Office Online)

「日常生活」と命名したテーブルを以下のように定義し、

090417-001

データを入力したとする。

090417-002

 

これに対して、次のように「年月日」を表示させるクエリを実行すると、

SELECT 日常生活.id, 日常生活.内容, Format([開始時刻],"yyyy/mm/dd hh:nn") AS 式1, Format([終了時刻],"yyyy/mm/dd hh:nn") AS 式2
FROM 日常生活;

時刻の前に `1899/12/30’ が表示される。これでは表現したいデータを素直に表現しているとは言い難い。 (+_+)

090417-003

ちなみにこの日付となる理由は、「フィールドのデータ型の設定を修正または変更する - Access - Microsoft Office Online」によると、

Access では、1899 年 12 月 30 日が日付 0 として使用されます。

 

時間について

プログラマのためのSQL 第2版 (P64) によると、

時間に関する重要な問題の 1 つに、時間には 3 種あるというのがあります。固定された出来事「彼は、13:00 に到着した」、期間「その旅は 3 時間かかる」、時間間隔「その列車は 10:00 に出発して 13:00 に到着する」は、それぞれ依存関係を持っています。 SQL-92 は、 INTERVAL データ型を導入していますが、ほとんどの現実の実装 (…) では、明白に持っていません。

(太文字は引用者による)

SQL – Wikipedia を見ると、SQL-92 で INTERVAL が追加されているのがわかる。

データ型の拡張 (可変長文字列、ビット、文字集合、日付・時刻・時間間隔 (DATE, TIME, TIMESTAMP, INTERVAL))

どうやらこの「時間間隔」というのが考える手がかりになりそうだ。

ちなみに Access と SQL のデータ型を比較した表を見ると、Access では INTERVAL 型を直接表現した型がないことがわかる。

 

ところで、Access ではユーザ定義型を作成することができない。(型とその型に適用できる演算子の定義がまともにできる RDB ってどれくらいあるのだろう?) よって、上記の要求を満たすドメインの値を素直に保持することはできない。そのため、結果として機能し得る型で代用することになる。

 

MySQL の Time 型

MySQL :: MySQL 5.1 Reference Manual :: 10.3.2 The TIME Type によると、

MySQL retrieves and displays TIME values in 'HH:MM:SS' format (or 'HHH:MM:SS' format for large hours values). TIME values may range from '-838:59:59' to '838:59:59'. The hours part may be so large because the TIME type can be used not only to represent a time of day (which must be less than 24 hours), but also elapsed time or a time interval between two events (which may be much greater than 24 hours, or even negative).

MySQL 5.1 では「時間間隔」と「一日の時刻」を同じ Time 型で表わすようだ。というより、「一日の時刻」を「時間間隔」で代用しているということかな。

 

Postgres の interval 型と time 型

Postgres は、「時間間隔」 (Interval) と「その日の時刻」(time) が分かれている。

 

Python の time オブジェクト

ところで、以前に Python で指定した日数後の日付を得るのに timedelta オブジェクト を使ったことがある。timedelta 周りを見ると time オブジェクト があるのに気がついた。(@_@)

time クラスは、

理想化された時刻表現で、あらゆる特定の日における影響から独立しており、毎日厳密に 24*60*60 秒であると仮定します ("うるう秒: leap seconds" の概念はありません)。 属性: hourminutesecondmicrosecond、 および tzinfo

(5.1.1 利用可能なデータ型 より)

まさにこれ。ただし、

time オブジェクト間の四則演算はサポートされていないので注意してください。

(time オブジェクト より)

「年月日」がなく「時・分・秒」だけので、上記の例を表現するには概念的にちょうどいい。

 

Visual Basic の日付

VB にも、似たような見かけの TimeSerial 関数 があったが、

    Debug.Print TimeSerial(6, 30, 0)
    Debug.Print Format(TimeSerial(6, 30, 0), "yyyy/mm/dd hh:nn")

実行すると、

6:30:00 
1899/12/30 06:30

あ~、内部では日付型かぁ…

@IT:連載:プロフェッショナルVB.NETプログラミング 第5回 日付時刻の取得とフォーマット によると、

日付と時刻の扱いは、Microsoft系BASICの歴史に限ってもすでに激変が起きている。初期のBASICでは、日付と時刻はそれぞれ文字列として扱われており、日付と時刻は別個の情報として扱われていた。(…)

Timeの場合日付は1899年12月30日となっているが、これはVB 6の日付を扱う起点となる日付である。(…)

VB.NETでは日付時刻の起点は0001/01/01 12:00:00である

うーむ… (@_@;)

 

Haskell の TimeOfDay 型

では、Haskell ではどうかな?と思い調べてみると、Time of day に、

data TimeOfDay

Time of day as represented in hour, minute and second (with picoseconds), typically used to express local time of day.

「時・分・秒」のみを値に持つ型がある。

先ほどの Python の Time オブジェクトと同様に、TimeOfDay 型は Num クラスのインスタンスではないので四則演算はできない。ただし、DiffTime 型という「時間間隔」を表わす型を利用すると計算を行うことができる。

最初に timeOfDayToTime 関数で TimeOfDay 型を DiffTime 型に変換。この関数は午前 0 時からの時間間隔を返してくれる。

timeOfDayToTime :: TimeOfDay -> DiffTime

090417-001

DiffTime 型は Num クラスのインスタンスなので、この型の値を足したり引いたりできる。それを上記の関数とは逆の timeToTimeOfDay で元の型に戻す。例えば、

import Data.Time
main = do
  let s = TimeOfDay 6 30 0
  print s
  let ds = timeOfDayToTime s
  print ds
  print $ timeToTimeOfDay $ ds + (secondsToDiffTime (10*60))

結果は、

06:30:00
23400s
06:40:00

 

対応する型がない場合の妥協

さて、話を元に戻して Access で時刻を扱うにはどうしよう。

一つは、年月日の情報は無視して、つまり ‘1899/12/30’ はないものとして時刻だけを「日付/時刻型」で扱う。とりあえず楽な方法。

自前でどうにかしたいなら、「時・分・秒」をそれぞれ別のフィールドで数値として扱い、それを一塊のものとして自分で思い込み、「時・分・秒」間での計算が必要なら関数を定義。ちょっと面倒だし、それに見合ういいことがあるのかわからない。

また別の方法として、NULL撲滅委員会 にあるように、

日付の列は文字型で宣言しておくべきです。(…) ほとんどの DBMS が日付型を用意していますが、私は個人的に使うメリットを感じません。暦日計算の場合など、必要なときだけキャストしています。

時刻を文字列として保持し、必要なときだけキャストする。ただし、年月日の情報は無視して日付型として計算を行う。この場合、最低でも文字列として時刻を保存するとき、時刻へとキャスト可能な形式であるか確認する必要となる。

2008年9月22日月曜日

Python でアバウトな時間計測

1. 日付と時間を持つ datetime オブジェクトで時間の計測

特定の処理にかかる時間を計測したい。

6.10.4 datetime オブジェクト によると、

datetime オブジェクトは date オブジェクトおよび time オブジェクトの全ての情報が入っている単一のオブジェクトです。(…)

now([tz])

現在のローカルな日付および時刻を返します。オプションの引数 tzNone であるか指定されていない場合、このメソッドは today() と同様ですが、可能ならば time.time() タイムスタンプを通じて得ることができるより高い精度で時刻を提供します (例えば、プラットフォームが C 関数 gettimeofday() をサポートする場合には可能なことがあります)。

試してみる。

from datetime import datetime
from time import sleep

start = datetime.now()
print start

sleep(1)

end = datetime.now()
print end
print end -start

結果は、

2008-09-22 09:53:44.375000
2008-09-22 09:53:45.375000
0:00:01

 

2. time 関数で時間を計測

6.11 time -- 時刻データへのアクセスと変換 によると、

ほとんどの関数が利用可能ですが、全ての関数が全てのプラットフォームで利用可能なわけではありません。このモジュールで定義されているほとんどの関数は、プラットフォーム上の同名の C ライブラリ関数を呼び出します。(…)

time()

時刻を浮動小数点数で返します。単位は UTC におけるエポックからの秒数です。時刻は常に浮動小数点で返されますが、全てのシステムが 1 秒より高い精度で時刻を提供するとは限らないので注意してください。

試してみる。

from time import time
start = time()
print start

sleep(1)

end = time()
print end
print end - start

結果は、

1222044825.38
1222044826.38
1.0

 

3. その他

追記(2008.9.22) : より正確な時間を計測するには、timeit モジュールを利用する。

18.2. Using the timeit Module には

「The most important thing you need to know about optimizing Python code is that you shouldn't write your own timing function.」

とあることに注意。 (via 実行時間計測の基本)

Python でメソッドの実行時間の計測 につづく…

2008年9月4日木曜日

Python で指定した日数後の日付を得る

例えば、今日 (2008.9.4) から 3 日後の日付を得たい場合、

from datetime import date, timedelta
print date.today() + timedelta(days=3)

結果、「2008-09-07」と表示される。

当然ながら、大晦日の 3 日後であるならば、

print date(2008,12,31) + timedelta(days=3)

「2009-01-03」と表示される。

つまり、日付による加算をしたい場合は、year, month, day の数値を得て計算するのではなく、 timedelta を使用すること。

 

datetimeモジュール

date オブジェクトdatetime モジュールで定義されており、

date オブジェクトは日付 (年、月、および日) を表します。(…)

today()

現在のローカルな日付を返します。

timedelta オブジェクト は、

timedelta オブジェクトは経過時間、すなわち二つの日付や時刻間の差を表します。