表題

時刻データ型と手続き

著者

Will Fitzgerald

状態

この SRFI は現在「確定」の状態である。SRFI の各状態の説明については ここ を参照せよ。 この SRFI に関する議論については メーリングリストのアーカイブ を参照せよ。

概要

時刻は「エポック」(the epoch: 時刻の基準点) からの経過時間 (ナノ秒精度) で表現する。 UTC (協定世界時)、TAI (国際原子時)、 モノトニック時刻 (monotonic time) のような標準時の変種を定義する。 時刻はユリウス日 (Julian Day) や 準ユリウス日 (Modified Julian Day) でも表現できる。 時間差や、プロセスやスレッドの実行時間も定義する。 時刻の変換関数を提供する。 手続き CURRENT-TIME は、 システムに依存した分解能で、 指定された形式の時刻を取得する。 時刻の計算や比較を行うための手続きも提供する。

日付は、グレゴリオ暦と、 (ナノ秒精度の) 24 時間形式の時刻と、 UTC からのタイムゾーン オフセットで表現する。 時刻と日付の変換関数や、 日付の文字列表現を読み書きする手続きを提供する。

課題

[現在はない]

論拠

R5RS Scheme では、時刻を表す標準データ型を定義していない。 この SRFI では、時刻データ型とそれに関連する手続きを定義することで、 この不足に対処する。

仕様

時刻 オブジェクトは、他のデータ型とは異なる型であり、 何らかの標準時システムにおける時刻や時間差を表す。 標準時システムとしては以下のものがある:

UTC、モノトニック時刻、現在のプロセスにおける CPU 時刻、および時間差は、 必ず実装しなければならない。 その他の標準時システムを実装してもよい (たとえば、ガーベージ コレクションに費やした時間など)。

時刻オブジェクトは、次の 3 つの要素で構成される。

日付オブジェクトは、他のデータ型とは異なる型であり、 グレゴリオ暦の時刻とタイムゾーンを表す。 日付は変更不可能 (immutable) なオブジェクトである。 日付は以下の要素で構成される。

ユリウス日 は -4714-11-24T12:00:00Z (-4714年11月24日の正午, UTC) からの日数を実数で表現する時刻表現である。

準ユリウス日 は 1858-11-17T00:00:00Z (1858年11月17日の夜中, UTC) からの日数を実数で表現する時刻表現である。

定数

以下の定数を提供しなければならない。

time-duration
時間差を表すシンボル
time-monotonic
モノトニック時刻を表すシンボル
time-process
現在のプロセスの経過時間を表すシンボル
time-tai
TAI 時刻を表すシンボル
time-thread
現在のスレッドの経過時間を表すシンボル
time-utc
UTC 時刻を表すシンボル

現在の時刻と時計の解像度

以下の手続きを提供しなければならない。

current-date [tz-offset] -> date
現在の UTC 時刻に対応する日付。
current-julian-day -> jdn
現在のユリウス日。
current-modified-julian-day -> mjdn
現在の準ユリウス日。
current-time [time-type] -> time
現在の時刻を、時刻タイプ time-type の時刻システムで返す。 デフォルトの時刻タイプは TIME-UTC である。
time-resolution [time-type] -> integer
時刻タイプ time-type の時刻システムの解像度をナノ秒単位で返す。 デフォルトの時刻タイプは TIME-UTC である。

時刻オブジェクトとアクセッサ

以下の手続きを提供しなければならない。

make-time type nanosecond second -> time
時刻オブジェクトを作成する。
time? object -> boolean
時刻オブジェクトであれば #t を返し、 そうでなければ #f を返す。
time-type time -> time-type
時刻タイプ。
time-nanosecond time -> integer
ナノ秒。
time-second time -> integer
秒。
set-time-type! time time-type
時刻タイプを設定する。 ノート: この手続きを呼び出すと、時刻オブジェクトの表す時刻が変化する。 時刻システムの間で時刻を変換するためには、後述の変換手続きを使用すること。
set-time-nanosecond! time integer
ナノ秒を設定する。
set-time-second! time integer
秒を設定する。
copy-time time1 -> time2
time1 と同じ時刻タイプ、ナノ秒、秒をもつ 新しい時刻オブジェクトを作成する。

時刻の比較手続き

時刻の比較手続きでは、比較する時刻オブジェクトが同じ時刻タイプをもたなければらない。 異なる時刻タイプをもつ時刻オブジェクトに対して 比較手続きを呼び出すことはエラーである。 時刻を表す時刻システム (TIME-TAITIME-UTC) に対しては、比較の意味を (括弧外の) 通常の文章で説明している。 時間差を表す時刻システム (TIME-DURATIONTIME-CPU) に対しては、比較の意味を括弧内で説明している。

以下の手続きを提供しなければならない。

time<=? time1 time2 -> boolean
time1time2 以前であれば (以下であれば) #t を返し、そうでなければ #f を返す。
time<? time1 time2 -> boolean
time1time2 より前であれば (小さければ) #t を返し、そうでなければ #f を返す。
time=? time1 time2 -> boolean
time1time2 と同じであれば (等しければ) #t を返し、そうでなければ #f を返す。
time>=? time1 time2 -> boolean
time1time2 以後であれば (以上であれば) #t を返し、そうでなければ #f を返す。
time>? time1 time2 -> boolean
time1time2 より後であれば (大きければ) #t を返し、そうでなければ #f を返す。

時刻の演算手続き

以下の手続きを提供しなければならない。

time-difference time1 time2 -> time-duration
time1time2 の間の TIME-DURATION を返す。 time1time2 の時刻タイプが異なる場合は、エラーである。 新しい時刻オブジェクトを作成して返す。
time-difference! time1 time2 -> time-duration
time1time2 の間の TIME-DURATION を返す。 time1time2 の時刻タイプが異なる場合は、エラーである。 time1 を破壊的に使用して戻り値の TIME-DURATION オブジェクトを返してもよい。
add-duration time1 time-duration -> time
time1time-duration を加算した時刻を返す。 戻り値は time1 と同じ時刻タイプの時刻オブジェクトである。 新しい時刻オブジェクトを作成して返す。
add-duration! time1 time-duration -> time
time1time-duration を加算した時刻を返す。 戻り値は time1 と同じ時刻タイプの時刻オブジェクトである。 time1 を破壊的に使用して戻り値のオブジェクトを返してもよい。
subtract-duration time1 time-duration -> time
time1 から time-duration を減算した時刻を返す。 戻り値は time1 と同じ時刻タイプの時刻オブジェクトである。 新しい時刻オブジェクトを作成して返す。
subtract-duration! time1 time-duration -> time
time1 から time-duration を減算した時刻を返す。 戻り値は time1 と同じ時刻タイプの時刻オブジェクトである。 time1 を破壊的に使用して戻り値のオブジェクトを返してもよい。

日付オブジェクトとアクセッサ

日付オブジェクトは、いったん作成すると変更することはできない。 以下の手続きを提供しなければならない。

make-date nanosecond second minute hour day month year zone-offset -> date
日付オブジェクトを作成する。
date? date -> boolean
時刻オブジェクトであれば #t を返し、そうでなければ #f を返す。
date-nanosecond date -> integer
ナノ秒。
date-second date -> integer
秒。
date-minute date -> integer
date-hour date -> integer
時。
date-day date -> integer
日。
date-month date -> integer
月。
date-year date -> integer
年。
date-zone-offset date -> integer
タイムゾーン オフセット。
date-year-day date -> integer
その年の何番目の日であるかを返す。1月1日であれば 1 を返す。
date-week-day date -> integer
日付の曜日を返す。日曜日=0, 月曜日=1 など。
date-week-number date day-of-week-starting-week -> integer
その日を含む週がその年の何番目の週であるかを返す。 ただし、その年の最初の7日に満たない週は無視する。 'day-of-week-starting-week' 引数は、週の最初の曜日を整数で指定する (日曜日=0, 月曜日=1 など)。

時刻/日付/ユリウス日/準ユリウス日の変換

以下の変換手続きを提供しなければならない。
date->julian-day date -> jd
日付をユリウス日に変換する。
date->modified-julian-day date -> mjd
日付を準ユリウス日に変換する。
date->time-monotonic date -> time-monotonic
日付をモノトニック時刻に変換する。
date->time-tai date -> time-tai
日付を TAI 時刻に変換する。
date->time-utc date -> time-utc
日付を UTC 時刻に変換する。
julian-day->date jd [tz-offset] -> date
タイムゾーン オフセットを使用して、ユリウス日を日付に変換する。 タイムゾーンのデフォルトは、ローカル タイムゾーンである。
julian-day->time-monotonic jd -> time-monotonic
ユリウス日をモノトニック時刻に変換する。
julian-day->time-tai jd -> time-tai
ユリウス日を TAI 時刻に変換する。
julian-day->time-utc jd -> time-utc
ユリウス日を UTC 時刻に変換する。
modified-julian-day->date mjd [tz-offset] -> date
タイムゾーン オフセットを使用して、準ユリウス日を日付に変換する。 タイムゾーンのデフォルトは、ローカル タイムゾーンである。
modified-julian-day->time-monotonic mjd -> time-monotonic
準ユリウス日をモノトニック時刻に変換する。
modified-julian-day->time-tai mjd -> time-tai
準ユリウス日を TAI 時刻に変換する。
modified-julian-day->time-utc mjd -> time-utc
準ユリウス日を UTC 時刻に変換する。
time-monotonic->date time-monotonic [tz-offset] -> date
タイムゾーン オフセットを使用して、モノトニック時刻を日付に変換する。 タイムゾーンのデフォルトは、ローカル タイムゾーンである。
time-monotonic->julian-day time-monotonic -> jd
モノトニック時刻をユリウス日に変換する。
time-monotonic->modified-julian-day time-monotonic -> mjd
モノトニック時刻を準ユリウス日に変換する。
time-monotonic->time-tai time-monotonic -> time-tai
モノトニック時刻を TAI 時刻に変換する。
time-monotonic->time-tai! time-monotonic -> time-tai
モノトニック時刻を TAI 時刻に変換する。時刻構造体は再利用される可能性がある。
time-monotonic->time-utc time-monotonic -> time-utc
モノトニック時刻を UTC 時刻に変換する。
time-monotonic->time-utc! time-monotonic -> time-utc
モノトニック時刻を UTC 時刻に変換する。時刻構造体は再利用される可能性がある。
time-tai->date time-tai [tz-offset] -> date
タイムゾーン オフセットを使用して、TAI 時刻を日付に変換する。 タイムゾーンのデフォルトは、ローカル タイムゾーンである。
time-tai->julian-day time-tai -> jd
TAI 時刻をユリウス日に変換する。
time-tai->modified-julian-day time-tai -> mjd
TAI 時刻を準ユリウス日に変換する。
time-tai->time-monotonic time-tai -> time-monotonic
TAI 時刻をモノトニック時刻に変換する。
time-tai->time-monotonic! time-tai -> time-monotonic
TAI 時刻をモノトニック時刻に変換する。時刻構造体は再利用される可能性がある。
time-tai->time-utc time-tai -> time-utc
TAI 時刻を UTC 時刻に変換する。
time-tai->time-utc! time-tai -> time-utc
TAI 時刻を UTC 時刻に変換する。時刻構造体は再利用される可能性がある。
time-utc->date time-utc [tz-offset] -> time-utc
タイムゾーン オフセットを使用して、UTC 時刻を日付に変換する。 タイムゾーンのデフォルトは、ローカル タイムゾーンである。
time-utc->julian-day time-utc -> jd
UTC 時刻をユリウス日に変換する。
time-utc->modified-julian-day time-utc -> mjd
UTC 時刻を準ユリウス日に変換する。
time-utc->time-monotonic time-utc -> time-monotonic
UTC 時刻をモノトニック時刻に変換する。
time-utc->time-monotonic! time-utc -> time-monotonic
UTC 時刻をモノトニック時刻に変換する。時刻構造体は再利用される可能性がある。
time-utc->time-tai time-utc -> time-tai
UTC 時刻を TAI 時刻に変換する。
time-utc->time-tai! time-utc -> time-tai
UTC 時刻を TAI 時刻に変換する。時刻構造体は再利用される可能性がある。

日付と文字列の変換

以下の手続きを提供しなければならない。 以下で述べる使用では「ロケール」について言及しているが、 ロケールの仕様については、この SRFI では扱わない。
date->string date [format-string] -> string
形式文字列を使用して、日付を文字列に変換する。 形式文字列は、(チルダで記述する) エスケープ文字が置換されることを除けば、 そのままコピーされる。 エスケープ文字の置換の仕様を表 1 に示す。 実装によっては、この仕様を拡張してもよい。
string->date input-string template-string -> date
入力文字列をテンプレート文字列にしたがって日付に変換する。 入力文字列は、テンプレート文字列の (チルダで記述する) エスケープ文字を除いては、 そのままマッチしなければならない。 エスケープ文字は特殊な変換を表し、次のように解釈される。 (1) 入力文字列の中で、基準を満たす次の文字の位置まで移動する。 (2) その位置から値を読み取る。 (3) 読み取った値を使用して、何らかの操作を行う。 変換の仕様を表 2 に示す。 実装によっては、この仕様を拡張してもよい。

Ch変換する文字列

~~リテラルの ~
~aロケール依存の曜日の省略名 (Sun...Sat)
~Aロケール依存の曜日の正式名 (Sunday...Saturday)
~bロケール依存の月の省略名 (Jan...Dec)
~Bロケール依存の月の正式名 (January...December)
~cロケール依存の日付と時刻 (たとえば "Fri Jul 14 20:28:42-0400 2000")
~d月の日。0 で 2 桁にパディング。 (01...31)
~D日付 (mm/dd/yy)
~e月の日。スペースでパディング ( 1...31)
~f秒+秒の端数。ロケール依存の小数点で区切られる (たとえば 5.2)。
~h~b に同じ
~H時。24 時間形式で、0 で 2 桁にパディング (00...23)
~I時。12 時間形式で、0 で 2 桁にパディング (01...12)
~j年の日。0 でパディング。
~k時。24 時間形式で、スペースでパディング ( 0...23)
~l時。12 時間形式で、スペースでパディング ( 1...12)
~m月。0 で 2 桁にパディング (01...12)
~M分。0 で 2 桁にパディング (00...59)
~n改行
~Nナノ秒。0 でパディング。
~pロケール依存の AM または PM
~r時刻。12 時間形式。"~I:~M:~S ~p" に同じ。
~sエポックからの経過秒数 (UTC)
~S秒。0 で 2 桁にパディング (00...60)
~t水平タブ
~T時刻。24 時間形式。"~H:~M:~S" に同じ。
~U年の週。日曜日が週の最初の日となる。 (00...53)
~V年の週。月曜日が週の最初の日となる。 (01...52)
~w曜日 (0...6)
~W年の週。月曜日が週の最初の日となる。 (01...52)
~x年の週。月曜日が週の最初の日となる。 (00...53)
~Xロケール依存の日付表現。たとえば "07/31/00"
~y年の下 2 桁 (00...99)
~Y
~zRFC-822 スタイルのタイムゾーン
~Zシンボル タイムゾーン (未実装)
~1ISO-8601 year-month-day 形式
~2ISO-8601 hour-minute-second-timezone 形式
~3ISO-8601 hour-minute-second 形式
~4ISO-8601 year-month-day-hour-minute-second-timezone 形式
~5ISO-8601 year-month-day-hour-minute-second 形式

表 1: DATE->STRING の変換指定子


Chスキップ先読み取る内容設定

~~任意リテラル ~ を読み取るなし
~achar-alphabetic?ロケール依存の曜日の省略名なし
~Achar-alphabetic?ロケール依存の曜日の完全名なし
~bchar-alphabetic?ロケール依存の月の省略名なし
~Bchar-alphabetic?ロケール依存の月の完全名なし
~dchar-numeric?月の日date-day
~e任意月の日。スペースでパディングdate-day
~hchar-alphabetic?~b に同じなし
~Hchar-numeric?date-hour
~k任意時。スペースでパディングdate-hour
~mchar-numeric?date-month
~Mchar-numeric?date-minute
~Schar-numeric?date-second
~y任意2 桁の年date-year (50 年以内)
~Ychar-numeric?date-year
~z任意タイムゾーンdate-zone-offset

Table 2: STRING->DATE の変換指定子

実装

この SRFI は、標準 Scheme だけでは完全には実装できない。 特に、CURRENT-TIME の値を取得するための システム固有の方法がなくてはならない。 これを実装する場合、GNU C の関数 gettimeofday を使うとよいだろう。

TAI と UTC の差異は決定論的ではなく、 TAI 時刻を取得するための方法を提供しなければならない。 参照実装では、米国海軍観測所の時刻サービスが提供している閏秒テーブル (ftp://maia.usno.navy.mil/ser7/tai-utc.dat から入手できる) を読み取るための手続きを用意した。

この実装では 「SRFI 6: 基本的な文字列ポート」 を使用している。また error 手続きの存在を仮定している。 それから 「SRFI 8: receive による多重値の束縛」 も使用している。 この receive 構文は次のようにして容易に実装できる。

(define-syntax receive
  (syntax-rules ()
    ((receive formals expression body ...)
     (call-with-values (lambda () expression)
                       (lambda formals body ...)))))

参照実装には TAI-UTC.DAT リーダーが含まれている。

参照実装は MzScheme で実装した。 MzScheme は current-seconds 手続きを提供しており、 これは 1970-01-01T00:00:00Z+00:00 からの経過秒数 (UTC) を返す。 また current-milliseconds 手続きも提供しており、 これは、モノトニックなタイムクロックを返す。 この2つを組み合わせることで、 (current-time time-utc) を実装している。 この実装では、モノトニック時刻は TAI 時刻と同じである。 TAI と UTC の差異は閏秒テーブルにより補正される。 国際地球回転観測事業 (IERS: International Earth Rotation Service) によると、2000年12月には閏秒はないだろうとのことだ。 したがって、閏秒テーブルは 2000年1月までは正確であることが保証される。

また、MzScheme は (確か version 102 の時点では)、 SECONDS->DATE と CURRENT-DATE 手続きを介して、 現在のタイムゾーン オフセットを取得する方法を提供している。

時刻と日付のオブジェクトを定義するために MzScheme の DEFINE-STRUCT を使用した。 これの代わりに「SRFI 9: レコード型の定義」 を使うこともできるだろう。

内部的に使用する手続きは TM: で始まる名前にした。 ロケール関係の定数と手続きの名前には locale が含まれている。 将来、ロケール関係の SRFI が定義されるなら、 代わりにそのコードを使うほうがよいだろう。

これらを基礎として、参照実装の残りの部分が実装されている。

テストコードも用意した。

謝辞

Claus Tdering による暦に関する Frequently Asked Questions はとても役に立つリソースである。 ユリウス日、準ユリウス日、および1年の日に関する実装は、 彼の FAQ を参考にした。 Markus Kuhn は、 日付/時刻の記法を定めた ISO Standard 8601 に対する 役に立つ説明 を書いている。 W3 コンソーシアムにも 有用なノート がある。

前のバージョンの草案に対しては、 Mike Sperber, Marc Feely, Dave Mason, および "Prfnoff" も有用なコメントをくれた。 Shriram Krishnamurthi は編集上の助けをしてくれて感謝する。

DATE->STRING 手続きは GNU C の date 関数と scshFORMAT-DATE 手続きをベースにした書式文字列を使用している。

著作権

Copyright (C) Neodesic Corporation (2000). All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


編集者: Shriram Krishnamurthi
著作者による最終更新日:
(display (date->string (current-date 0) "~4")): 2004-03-15T02:21:15Z