Next: , Previous: , Up: オペレーティングシステムのインターフェース   [Contents][Index]


42.7 時刻の変換

以下の関数はtime値(時刻を参照)をLispタイムスタンプや暦情報(calendrical information)に変換したり逆の変換を行います。

秒を数えるために多くのオペレーティングシステムは64ビット符号付き整数を使用しており、過去や未来の時刻を表すことができます。しかしより制限されているオペレーティングシステムもいくつか存在します。たとえば32ビット符号付き整数を使用する旧式のオペレーティングシステムでは、通常は協定世界時で1901-12-13 20:45:52から2038-01-19 03:14:07までの時刻しか扱うことができません。

暦変換関数はたとえグレゴリオ暦導入前の日付や、グレゴリオ暦では誤差が非常大きくなるために天文学や古生物学のような科学分野の一般的慣習としてユリウス暦の年数が使用されるような遠い過去や未来の日付であってもグレゴリオ暦を使用します。伝統的なグレゴリオ年が行うように0年をスキップせずにBCE 1年から年数を数えます。たとえば年数-37はグレゴリオ年のBCE 38年を表します。

Function: time-convert time form

この関数はtime値をLispタイムスタンプに変換する。

form引数はリターンするタイムスタンプ形式を指定する。この関数はformがシンボルintegerなら整数でカウントした秒数をリターンする。正の整数のformはクロック周波数を指定する。その場合にはこの関数は(ticks . form)という整数ペアのタイムスタンプをリターンする。formtなら、この関数はタイムスタンプを適切に表現するような正の整数としてそれを扱う。たとえばtimenilでプラットフォームのタイムスタンプがナノ秒の解像度をもつ場合には1000000000としてそれを扱う。formlistなら、この関数は整数のリスト(high low micro pico)をリターンする。現在のところformnilの場合にはlistのように動作するとしても、Emacsの将来バージョンで変更が予定されているので、呼び出し側がリスト形式のタイムスタンプを必要とする場合には明示的にlistを渡すこと。

timeがtime値でなければ、この関数はエラーをシグナルする。それ以外の場合には、timeを正確に表せなければ負の無限大方向に切り詰めて変換する。formtなら変換は常に正確なので切り詰めは発生せず、リターン値のクロック解像度がtimeの解像度より小さくなることはない。それとは対照的にfloat-timeはエラーをシグナルせずに任意のtime値を変換できるものの、結果は不正確かもしれない。時刻を参照のこと。

この関数は効率化のためにtimeeqな値、あるいはtimeと構造を共有する値をリターンするかもしれない。

(time-convert nil nil)(current-time)と等価だが後者は幾分速い。

(setq a (time-convert nil t))
⇒ (1564826753904873156 . 1000000000)
(time-convert a 100000)
⇒ (156482675390487 . 100000)
(time-convert a 'integer)
⇒ 1564826753
(time-convert a 'list)
⇒ (23877 23681 904873 156000)
Function: decode-time &optional time zone form

この関数はtime値を暦情報に変換する。timeを指定しなければカレント時刻をデコードする。同様にzoneのデフォルトはカレントのタイムゾーンルール。タイムゾーンのルールを参照のこと。timeの範囲およびzoneの値はオペレーティングシステムが制限する。

form引数はリターンされるseconds要素の形式を制御する(以下参照)。リターン値は以下のような9要素のリスト:

(seconds minutes hour day month year dow dst utcoff)

以下は各要素の意味:

seconds

以下で説明する形式による、分秒の秒。

minutes

0から59までの整数で表した時を過ぎた時分秒の分。

hour

0から23までの整数で表した時分秒の時。

day

1から31までの整数で表した年月日の日。

month

1から12までの整数で表した年月日の月。

year

通常は1900より大きい整数で表した年月日の年。

dow

0から6までの整数で表した曜日であり0は日曜日を意味する。

dst

夏時間が有効ならt、無効ならnil、その情報が利用できなければ-1。

utcoff

万国標準時からの秒数、すなわち東グリニッジの秒数を示す整数。

Lispタイムスタンプのseconds要素は非負かつ61より小さいこと。これはは正の閏秒の間以外は60より小さくなる(オペレーティングシステムが閏秒をサポートする場合)。オプションのform引数がtなら、secondstimeと同じ精度を使用する。formintegerならsecondsを整数に切り捨てる。たとえばtimeがタイムスタンプ(1566009571321 . 1000) (閏秒がない通常のシステムでは2019-08-17 02:39:31.321 UTCを表す)なら、(decode-time time t t)((31321 . 1000) 39 2 17 8 2019 6 nil 0)だが(decode-time time t 'integer)(31 39 2 17 8 2019 6 nil 0)をリターンする。formが省略かnilの場合のデフォルトは現在のところintegerだが、このデフォルトはEmacsの将来バージョンで変更されるかもしれないので、呼び出し側は特定の形式が必要ならformを指定すること。

Common Lispに関する注意: Common Lispではdowdstutcoffの意味が異なり、secondは0から59(両端を含む)の整数である。

暦情報の要素にアクセス(や変更)するためのアクセッサとしてdecoded-time-seconddecoded-time-minutedecoded-time-hourdecoded-time-daydecoded-time-monthdecoded-time-yeardecoded-time-weekdaydecoded-time-dstdecoded-time-zoneを使用できる。

Function: encode-time time &rest obsolescent-arguments

これはtimeをLispタイムスタンプに変換する。これはdecode-timeの逆の関数として機能する。

通常だとdecode-time形式でデコードされた時刻を指定する(second minute hour day month year ignored dst zone)がリストの1つ目の引数となる。これらのリスト要素の意味については、decode-timeのテーブルを参照のこと。特にdstはタイムスタンプが繰り返される夏時間(DST: daylight saving time)の期間中のフォールバックにおけるタイムスタンプの解釈法を指定する。dstが-1ならDSTを推測、tnilの場合にはそのDST値をもつタイムスタンプをリターン、そのようなタイムスタンプが存在しなければエラーをシグナルする。残念なことにtnildst値は、たとえばzoneが‘"Europe/Volgograd"’において、その日の02:00にグリニッジ東の標準時を+04:00から+03:00に変更した際の2020-12-27 01:30にたいする2つの標準時タイムスタンプの不明確さといったような、TZDBタイムゾーンがグリニッジを更に超えて西に移動した際における重複したタイムスタンプの不明確さを解消するものではない。このような状況を処理するためには、不明確さを解消するために数値のzoneを使用することができる。

1つ目の引数は(second minute hour day month year)のようなリストでもよい。これは(second minute hour day month year nil -1 nil)のようなリストとして扱われる。

廃れた呼び出し規約として、この関数は6つ以上の引数を受け取ることができる。最初の6つの引数secondminutehourdaymonthyearはデコード済みtimeのほとんどの要素を指定する。7つ目以降の引数があれば、最後の引数はzoneとして使用されれるので、(apply #'encode-time (decode-time ...))は機能する。この廃れた規約においてはdstは-1、zoneのデフォルトはカレントタイムゾーンルール(タイムゾーンのルールを参照)となる。時代遅れな呼び出し側を現代化する際には、9つの要素を含んだより最新の同等リストのdst要素に、nilではなく-1がセットされていることを確認すること。

100未満の年が特別に扱われることはない。これに1900や2000を超える年を意味させたい場合には、encode-timeを呼び出す前に自身でこれらを修正しなければならない。timeの範囲およびzoneの値はオペレーティングシステムが制限する。とはいえエポックから近い将来に渡るタイムスタンプ範囲は常にサポートされる。

encode-time関数はdecode-timeのラフな逆関数として動作する。たとえば以下のように後者の出力を前者に渡すことができる:

(encode-time (decode-time …))

secondsminuteshourdaymonthに範囲外の値を使用することにより単純な日付計算ができる。たとえばdayが0なら与えられたmonthの前月末日になる。失敗する場合もよくあるので、これを行う際には注意すること。たとえば:

;; 現在から1ヶ月後を算出。
;; 期待どおりに動かないかもしれないので注意
(let ((time (decode-time)))
  (setf (decoded-time-month time)
        (+ (decoded-time-month time) 1))
  time)

残念ながらこのコードは月の長さの違いによって結果時刻が不正になったり、夏時間への移行、タイムゾーン変更、閏日や閏秒を考慮しない等で期待どおり動作しないかもしれない。たとえばこのコードを1月30日に実行すれば、encode-timeであれば3月初頭に調整するであろう2月30日という存在しない日付を得ることになる。同様に2096年2月29日に4年を加えた2100年2月29日は存在せず、ニューヨークで3月13日の01:30に1時間を加えるとタイムスタンプとして02:30を得るだろうが、ニューヨークではその日の02:00に03:00へ時刻が飛ぶのでこれも存在しないタイムスタンプを得ることになる。問題のいくつか(すべてではない)を回避するために影響を受ける単位の半ば、たとえば月の加算を行う際にはその月の15日で開始するといったような計算を基にすることができる。別の策としてはcalendartime-dateといったライブラリーを使うことができる。