std:time — 日時 (Date / Time / Instant / Duration)
標準ライブラリモジュール (index.md)。本書中の裸の §N は本書の節を指す。言語の意味論は ../language-spec.md を参照。
import t := "std:time" は日時を扱う 4 つの不透明型 (Date / Time / Instant / Duration) のコンストラクターと曜日タグを slot に持つ namespace object を t に束縛する。prelude の now() (epoch ミリ秒 Int) を補完し、暦の構築・分解・整形・期間演算・順序比較を提供する。
std:fs / std:term と違い I/O を伴わず、純粋・同期 (Future を返さない)。すべての関数・メソッドは即値を返す。
UTC 固定である。Instant はタイムゾーンを保持しない絶対 epoch ミリ秒であり、「UTC」は暦フィールドを読む / 文字列化するときの解釈規約にすぎない (§3)。ローカル時刻・IANA 名前ゾーン・DST は将来枠 (§9)。非 UTC 出力は固定オフセットのレンダリングのみ対応する (§4)。
import t := "std:time" match t.of({ year := 2026, month := 6, day := 19, hour := 5, minute := 30 |}) { Ok(inst) => print(inst.to_iso!) # "2026-06-19T05:30:00Z" Err(e) => print(e.message) } t.now().add(t.hours(3)).to_iso! # 3 時間後の Instant を ISO 文字列に
以下では import t := "std:time" で束縛したものとして t.now 等で記す。
1. 不透明型
4 つの型は内部表現を露出しない不透明値型で、いずれも int64 を 1 つ内包する (Hikari は浮動小数を持たない、language-spec.md §7)。生成は §5 のコンストラクター経由のみ。
| 型 | 意味 | Inspect() 例 |
|---|---|---|
Date |
暦日 (年月日のみ・ゾーン無し) | 2026-06-19 |
Time |
時刻帯 (時分秒ミリ・日付/ゾーン無し、0:00:00〜23:59:59.999) | 05:30:00 / 05:30:00.250 |
Instant |
絶対時刻の一点 (UTC 解釈) | 2026-06-19T05:30:00Z |
Duration |
期間 (負可) | Duration(1h30m0s) |
これらの型は値等価 (==) と順序 (< <= > >=、language-spec.md §9.2) を持つ。等価は内部表現の一致で判定され、別インスタンスでも同じ時点・同じ期間なら等しい (不透明値型は Equatable で値等価を実装する。language-spec.md §9.1)。順序は各型の compare メソッド経由で導出される。+ / - 演算子はオーバーロードしない — 加減算は add / sub / diff メソッドで行う。単項 - も同じで、Duration が negate メソッドを持っていても -dur は書けない (単項 - が引くのは受け手が宣言したスロットだけで、ランタイム層が実装する組込メソッドは引かない。../language-spec.md §8.6)。符号反転は dur.negate! と書く。
4 型は std:time の型メンバーとして export され、型注釈に書ける (language-spec.md §13 のモジュール型 export と対称)。import t := "std:time" のもとで x: t.Instant と修飾参照するか、import { Instant, Duration } := "std:time" で名前を取り出して x: Instant と書く。不透明性は保たれ、注釈に書けても内部表現は露出しない (生成は §5 のコンストラクター経由のみ・パターン分解は不可)。
4 型はいずれもCopyable である (../language-spec.md の複製の節)。不透明型だがホストの可変状態を持たない不変な値なので、複製は Int を複製するのと同じ意味を持つ。
2. Instant とタイムゾーンの意味論
Instant はゾーンを格納しない。持つのは絶対 epoch ミリ秒だけである。「UTC である」ことは値の属性ではなく、暦フィールドを読む / 文字列化するときの解釈規約である。
inst.year!等の暦フィールドは「UTC で解釈した値」を返す。inst.to_iso!の末尾Zは UTC 規約でレンダリングした結果であって、値が UTC を覚えているからではない。date.at(time) → Instantが UTC でタイムラインに落とすのもatの規約 (格納ではない)。- 等価は epoch ミリ秒の一致のみ (ゾーン差を考えなくてよい)。
- 閏秒は値に現れない。 持つのが epoch ミリ秒だけなので目盛りとして存在せず、
diffの返すDurationは暦上の 2 点間に閏秒が挟まっても増えない。挿入された秒に固有のInstantは無く、ofにもsecond := 60は渡せない (§5 の範囲検査)。
将来のタイムゾーン対応は Instant を不変のまま、別型 (Instant + zone) を新設して載せる方針 (§9)。
3. 取得方法
import t := "std:time"
返るのは閉じた (closed) namespace object。slot は §5 のコンストラクター Builtin 群と、§6 の曜日タグ 7 個。型メソッド (§7) は受け手の型に対して登録され、inst.year! のように呼ぶ。
4. 固定オフセットのレンダリング
Instant をゾーン無しに保ったまま +09:00 のような表示を行うため、純粋なフォーマット関数として固定オフセット出力を提供する (§7.1 Instant の to_iso_at / format_at)。
- オフセットは UTC からの分 (Int)。例: JST =
540、IST =330、UTC−5 =-300。 - 範囲は ±1080 分 (±18:00)。これを超える値を渡すと呼び出し位置で
Error(panic 型、language-spec.md §16)。 - DST・IANA 名前ゾーンは非対応 — オフセットは呼び手が与える。
- 出力は
Instantの絶対値を変えず、表示だけをオフセット適用後の壁掛け時刻で行う。 - 暦フィールドアクセサー (
year!等) は UTC 固定のまま (オフセット下のフィールド取得は将来枠 §9)。 - 入力側は対称で、
parse_isoはZ/±HH:MMを受理して絶対時刻にまとめる (オフセットは正規化で捨てる)。
5. namespace コンストラクター
生成口のみ自由関数として namespace の slot に置く。型不一致 (object 以外を渡す等) は呼び出し位置で Error (panic 型)、範囲外・解析失敗は Result の Err({kind, message}) (§8)。
| 名前 | 形 | 効果 | 戻り | 意味 |
|---|---|---|---|---|
now |
t.now() |
Time |
Instant |
現在時刻 (UTC) |
today |
t.today() |
Time |
Date |
現在の UTC 日付 |
of |
t.of(rec) |
なし | Result(Instant) |
UTC 暦から構築。rec = { year, month, day, hour?, minute?, second?, milli? |} (時刻部省略は 0) |
date |
t.date(rec) |
なし | Result(Date) |
{ year, month, day |} |
time_of |
t.time_of(rec) |
なし | Result(Time) |
{ hour, minute?, second?, milli? |} (分以降省略は 0) |
from_epoch_ms |
t.from_epoch_ms(n) |
なし | Instant |
epoch ミリ秒 Int から。prelude now() / fs.mtime の橋渡し |
millis / seconds / minutes / hours / days |
t.hours(n) 等 |
なし | Duration |
単位 × n の期間。days は 24h 固定 |
parse_iso |
t.parse_iso(text) |
なし | Result(Instant) |
RFC3339。Z / ±HH:MM を受理し絶対時刻へまとめる |
parse |
t.parse(layout, text) |
なし | Result(Instant) |
strftime 風レイアウトで解析 (§7.5) |
parse_date |
t.parse_date(text) |
なし | Result(Date) |
ISO YYYY-MM-DD |
parse_time |
t.parse_time(text) |
なし | Result(Time) |
ISO HH:MM:SS[.SSS] |
of / date / time_of は year / month / day / hour を必須スロット (time_of は hour のみ必須) とし、省略可スロットは 0 を既定とする。必須スロットの欠落・非 Int や暦として範囲外の値は Err (§8)。Result 値は match で分岐するか、値が要るだけなら t.of(...).unwrap! でよい。
6. 曜日 (Variant enum)
曜日は Monday..Sunday の 7 個の Variant 値として namespace に export される (t.Monday 等)。Instant.weekday! / Date.weekday! はこれらのいずれかを返す。
inst := t.of({ year := 2026, month := 6, day := 19 |}).unwrap! # 金曜 inst.weekday! == t.Friday #> true label := match inst.weekday! { t.Saturday => "weekend" t.Sunday => "weekend" _ => "weekday" } # "weekday"
match の値パターンには曜日タグ (t.Saturday 等) を置き、== で照合する。
7. メソッド
無引数メソッドは postfix ! で呼ぶ (v! ≡ v()、language-spec.md)。引数の型不一致は呼び出し位置で Error (panic 型)。
7.1 Instant
| メソッド | 形 | 効果 | 戻り | 意味 |
|---|---|---|---|---|
year / month / day / hour / minute / second / millisecond / day_of_year |
inst.year! 等 |
なし | Int | UTC 解釈の暦フィールド (month 1-12、day 1-31、day_of_year 1-366) |
weekday |
inst.weekday! |
なし | Variant | Monday..Sunday (§6) |
epoch_ms |
inst.epoch_ms! |
なし | Int | epoch ミリ秒 |
date |
inst.date! |
なし | Date |
日付部 (UTC) |
time |
inst.time! |
なし | Time |
時刻部 (UTC) |
add |
inst.add(dur) |
なし | Instant |
dur (Duration) を加算 |
sub |
inst.sub(dur) |
なし | Instant |
dur (Duration) を減算 |
diff |
inst.diff(other) |
なし | Duration |
self − other |
add_days |
inst.add_days(n) |
なし | Instant |
n 日加算 (Int) |
add_months |
inst.add_months(n) |
なし | Instant |
n ヶ月加算。日は対象月末にクランプ (例 1/31 +1 → 2/28) |
add_years |
inst.add_years(n) |
なし | Instant |
n 年加算 (月末クランプ) |
to_iso |
inst.to_iso! |
なし | String | RFC3339・UTC・末尾 Z。ミリ秒は非 0 のときのみ .SSS |
format |
inst.format(layout) |
なし | String | strftime 風整形 (UTC、§7.5) |
to_iso_at |
inst.to_iso_at(offset_min) |
なし | String | RFC3339・末尾 ±HH:MM (§4) |
format_at |
inst.format_at(layout, offset_min) |
なし | String | オフセット適用後の壁掛け時刻で整形 (§4) |
compare |
inst.compare(other) |
なし | Ordering | epoch ミリ秒の全順序 |
7.2 Date
| メソッド | 形 | 効果 | 戻り | 意味 |
|---|---|---|---|---|
year / month / day / day_of_year |
date.year! 等 |
なし | Int | 暦フィールド |
weekday |
date.weekday! |
なし | Variant | Monday..Sunday |
add_days / add_months / add_years |
date.add_days(n) 等 |
なし | Date |
暦演算 (月末クランプ) |
at |
date.at(time) |
なし | Instant |
time (Time) と結合し UTC でタイムラインに落とす |
to_iso |
date.to_iso! |
なし | String | YYYY-MM-DD |
format |
date.format(layout) |
なし | String | strftime 風整形 |
compare |
date.compare(other) |
なし | Ordering | 日付の全順序 |
7.3 Time
| メソッド | 形 | 効果 | 戻り | 意味 |
|---|---|---|---|---|
hour / minute / second / millisecond |
tm.hour! 等 |
なし | Int | 時刻フィールド |
to_iso |
tm.to_iso! |
なし | String | HH:MM:SS[.SSS] |
format |
tm.format(layout) |
なし | String | strftime 風整形 (時刻ディレクティブ前提) |
compare |
tm.compare(other) |
なし | Ordering | 時刻の全順序 |
7.4 Duration
| メソッド | 形 | 効果 | 戻り | 意味 |
|---|---|---|---|---|
as_millis / as_seconds / as_minutes / as_hours / as_days |
dur.as_seconds! 等 |
なし | Int | 総量 (0 方向 truncate) |
add / sub |
dur.add(o) / dur.sub(o) |
なし | Duration |
期間同士の加減算 |
negate |
dur.negate! |
なし | Duration |
符号反転 |
compare |
dur.compare(other) |
なし | Ordering | 期間の全順序 |
7.5 strftime ディレクティブ
format / format_at / parse が解釈するディレクティブ:
| ディレクティブ | 意味 |
|---|---|
%Y |
年 (4 桁) |
%m |
月 (01-12) |
%d |
日 (01-31) |
%H |
時 (00-23) |
%M |
分 (00-59) |
%S |
秒 (00-59) |
%L |
ミリ秒 (000-999) |
%j |
年内通日 (001-366) |
%a / %A |
曜日 (略 / 完全・英語) |
%b / %B |
月名 (略 / 完全・英語) |
%p |
AM / PM |
%z |
オフセット (±HHMM)。format は UTC で +0000、format_at は与えたオフセット |
%% |
リテラル % |
format/format_atの未知ディレクティブ%xはリテラル通過 (%と当該文字をそのまま出力)。整形は total で必ず String を返す。parseは%Y %m %d %H %M %S %L %zと%%・リテラルを解釈する。%zを含むparseはオフセットを読んで絶対時刻にまとめる。不一致はErr({kind := "parse_error" …})(§8)。
8. エラーモデル
回復可能な失敗は Result の Err(e)、e は {kind, message} (language-spec.md §16)。std:time が返す正規 kind (閉じた一覧):
| kind | 関数 | 意味 |
|---|---|---|
invalid_date |
of / date |
年月日が暦として範囲外、または必須スロットの欠落・非 Int |
invalid_time |
time_of |
時分秒ミリが範囲外、または必須スロットの欠落・非 Int |
parse_error |
parse_iso / parse / parse_date / parse_time |
テキストがレイアウトに一致しない |
match t.of(rec) { Ok(inst) => use(inst) Err(e) => match e.kind { "invalid_date" => print("out of range date") _ => print(e.message) } }
message は of: ... のように関数名を含む人間可読文字列。分岐は kind で行い、message の文字列マッチに依存しないこと。一方、型不一致 (object 以外を of に渡す、Duration でない値を Instant.add に渡す、to_iso_at のオフセットが範囲外、等) は回復可能エラーではなく、呼び出し位置で Error (panic 型、language-spec.md §16) になる。暦フィールド・epoch ミリ秒・Duration の量など処理系側の数値として保持する引数の受理範囲は実装定義 (64 bit 符号付き整数) であり、超過はいずれの場合もエラーになる (of 系は Err、from_epoch_ms/add_days 等は Error)。
9. 将来枠
次は扱わない。いずれも実使用を見るまで入れない (YAGNI) 判断である。
- ローカル時刻・IANA 名前ゾーン・DST (
Instantを不変のまま、ゾーン付き別型を新設して対応)。 - 暦フィールドのオフセット下取得 (今は文字列レンダリングのみ)。
- 暦期間の値型 (
add_months/add_yearsメソッドで代替)。