Date / Time / DateTime method coverage audit
Generated 2026-05-23 as the Phase 1 artifact of a rigor-type-coverage-uplift
session (Phase 4 / Slice 4 of the post-c9a535a coverage-uplift line).
Unlike the String / Integer / Hash / Math audits, the Date / Time conclusion is not a list of dispatch-tier additions. The reader surface is already catalog-ready; the single blocker is a missing type carrier. This document records that finding. The carrier was authorised and implemented (see § 4); the 🟦 rows below are now ✅.
Legend
Section titled “Legend”| Symbol | Meaning |
|---|---|
| ✅ | Folded via Constant[Date] / Constant[Time] carrier |
| 🚫 | Out of scope (destructive / nondeterministic / machine-dependent) |
1. Current state
Section titled “1. Current state”Date / DateTime / Time are already registered in CATALOG_BY_CLASS
(constant_folding.rb), with DATE_CATALOG / TIME_CATALOG extracted from C
source. Reader methods (year / month / day / hour / wday / leap? /
strftime / iso8601 / next_day / >> …) are classified as :leaf and
fold-eligible.
Yet precision uplift does not occur because Date / Time are not in
Type::Constant’s SCALAR_CLASSES (lib/rigor/type/constant.rb), and
ConstantFolding’s foldable_constant_value? also rejects both classes.
Date.new(2026,1,1) stops at Nominal[Date], and .year on it widens to
the RBS tier’s Integer.
The spec integration fixtures date_catalog/demo.rb and time_catalog.rb
explicitly state “a future Constant<Date> carrier would be eligible to fold
them”, fixing the current Nominal answer as a regression guard against
unsound carriers. This audit formalises that “future carrier” as a decision item.
2. Method classification (Date / DateTime)
Section titled “2. Method classification (Date / DateTime)”When all arguments to Date.new(...) are Constant[Integer], the following
become foldable to Constant (catalog classification confirmed, backed by
date_catalog/demo.rb):
| Group | Methods | Folds to | Status |
|---|---|---|---|
| Integer readers | year month/mon day/mday wday yday cwyear cweek cwday jd | Constant[Integer] | ✅ |
| bool predicates | leap? julian? gregorian? sunday?…saturday? | Constant[bool] | ✅ |
| String readers | to_s iso8601 strftime(fmt) httpdate rfc3339 | Constant[String] | ✅ |
| Date navigation | next_day prev_day next_month prev_year succ >> << next | Constant[Date] | ✅ |
| DateTime additions | hour min sec offset zone | Constant[Integer|String] | ✅ |
| Comparison | <=> == < > (Date × Date) | Constant[bool|Integer] | ✅ |
| Destructive | (Date is immutable. None apply.) | — | — |
3. Method classification (Time)
Section titled “3. Method classification (Time)”When all arguments to Time.utc(...) / Time.gm(...) / Time.at(epoch) are constants:
| Group | Methods | Folds to | Status |
|---|---|---|---|
| Integer readers | year month day hour min sec wday yday usec nsec utc_offset | Constant[Integer] | ✅ |
| bool predicates | utc?/gmt? sunday?…saturday? dst? | Constant[bool] | ✅ |
| String readers | strftime(fmt) to_s ctime/asctime inspect | Constant[String] | ✅ |
| Time navigation | getutc/getgm + -(Numeric) round floor ceil | Constant[Time] | ✅ |
| Destructive | localtime gmtime utc | — | 🚫 Blocklisted |
| Machine-dependent | getlocal | — | 🚫 Added to TIME_CATALOG blocklist |
| Nondeterministic | Time.now / Time.at / Time.local / Time.new | — | 🚫 Not carrier-eligible (local zone dependent) |
Note on Time mutability: Time#localtime / gmtime / utc modify the
receiver in-place via time_modify. Time is not purely immutable. When
making it a Constant[Time] carrier, it must be frozen via value.dup.freeze
(same as String / Set; localtime on a frozen Time raises FrozenError
→ fold declines via rescue, sound). TIME_CATALOG already blocklists the
three pseudo-mutators.
4. Implementation (new carrier, authorised and executed)
Section titled “4. Implementation (new carrier, authorised and executed)”Date / Time precision uplift cannot be achieved by adding a dispatch tier —
it required a type system change: adding new scalar carriers to Type::Constant.
Following the precedent of the Set carrier, the following was implemented:
-
lib/rigor/type/constant.rb- Added
require "date"at the top (Date/DateTimeare stdlib;Timeis core). - Added
Date(DateTimeis a subclass ofDate, so covered) andTimetoSCALAR_CLASSES. - Extracted the freeze branch in
initializeintofreezable_carrier?, addingDate/Timeto thevalue.dup.freezetargets (localtimeon frozenTimeraisesFrozenError→ fold declines via rescue, sound). - Specialised
describe—Date#inspectuses astronomical Julian day notation which is unreadable, so useiso8601("2026-01-01") instead.Time#inspectis concise as"2026-01-01 00:00:00 UTC"so left as-is.
- Added
-
constant_folding.rb- Added
Date/Timeto the permitted class set (FOLDABLE_CONSTANT_CLASSES) infoldable_constant_value?. Reader methods fold automatically via the catalog (catalog_allows?), so no UNARY/BINARY Set additions needed.
- Added
-
Constructor folding (entry point producing
Constant[Date]/Constant[Time])Date.new(y,m,d)/DateTime.new(...)—date_new_liftinMethodDispatcher#meta_new(same location and shape asrange_new_lift/regexp_new_lift).Time.utc(...)/Time.gm(...)— Tier DTimeFoldingmodule (wired indispatch_stdlib_module_tiers). UTC-fixed, so machine-independent.Time.now/Time.at/Time.local/Time.new/Date.today/Date.parseare out of scope — nondeterministic or local-zone dependent.
-
FP discipline —排除 machine dependence
- Only
Time.utc/gmare folded (UTC-fixed).Time.at/Time.local/Time.new(without explicit offset) depend on the analysis machine’s zone, so out of scope. - Added
Time#getlocaltoTIME_CATALOG’s blocklist — not a mutator, but the result depends on the analysis machine’s zone, so folding fromConstant[Time](always UTC) would bake host-dependent values into the type.getutc/getgmremain foldable (UTC results). rigor check libclean, 4345 examples 0 failures — regression fixed.
- Only
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.