Skip to content

feat(vm): Time — MRI 3.4/4.0 conformance (constructors, parts, conversions, full strftime) - #339

Merged
tannevaled merged 3 commits into
mainfrom
feat/time-conformance
Aug 9, 2026
Merged

feat(vm): Time — MRI 3.4/4.0 conformance (constructors, parts, conversions, full strftime)#339
tannevaled merged 3 commits into
mainfrom
feat/time-conformance

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Horodate: 2026-08-09 18:47 CEST

Completes the Ruby Time core class toward MRI 3.4/4.0. Time is re-backed with Go's time.Time (was go-composites/time, whole-second only) to gain nanosecond sub-second precision and fixed-offset zones — the foundation MRI-faithful Time needs. Timecop still drives Time.now through the VM clock seam (vm.nowInstant).

Implemented

ConstructorsTime.new (parts, 7th-arg zone string, the 4.0 in: keyword), Time.utc/gm, Time.local/mktime (µs 7th arg), Time.at (Integer/Float/Rational/Time; subsec + :millisecond/:microsecond/:nanosecond unit and the :usec/:nsec aliases; in:), Time.now(in:). MRI field range-validation (mon/mday/hour/min/sec out of range) with overflow normalised as MRI does (Feb 30 → Mar 2, hour 24 → next day, sec 60 → next minute).

Partsusec nsec subsec (Rational, Integer 0 on a whole second) yday utc_offset/gmt_offset/gmtoff utc?/gmt? dst?/isdst to_r, alongside the pre-existing yearsec/wday/zone/tv_*.

Conversionsutc/gmtime/localtime mutate-in-place and return self (MRI); getutc/getlocal(offset) return a new Time; round/floor/ceil(ndigits); to_a. Time − Time now returns Float seconds; +/- accept fractional seconds.

Formatting — a from-scratch strftime covering the full directive set (%Y %C %y %m %d %e %H %k %I %l %M %S %L %N %j %p %P %A %a %B %b %u %w %s %z %:z %::z %Z %U %W %G %V %n %t %% and the %c %D %F %R %r %T %x %X %h compounds) with the -_0^# flags and width; inspect renders the sub-second fraction (4.0); ctime/asctime.

Every value is asserted against real ruby 4.0.5 on fixed instants (no wall-clock/TZ dependence).

Deferred (out of core scope / gate risk)

iso8601/xmlschema/httpdate/rfc2822/rfc822 and a Time.parse/Time.strptime rewrite need the time/date stdlib parser — kept as-is (existing go-composites lenient parsers untouched). One rbgo-specific convention preserved to avoid churn: a UTC instant renders +0000 (not UTC) in to_s/inspect, matching the pre-existing Go tests.

Verification

  • CI-exact whole-package coverage gate shows only the tolerated pre-existing partials — time.go is 100% (incl. every error branch).
  • go test ./... exit 0; gofmt + go vet ./internal/vm/ clean.
  • Fresh-binary ruby/spec ratchet: 12246 passing (language + core), up from the old floor 12068. FLOOR bumped 12068 → 12214 (N−30).

🤖 Generated with Claude Code

tannevaled and others added 3 commits August 9, 2026 18:18
2026-08-09 17:56 CEST

Re-back the Ruby Time class with Go's time.Time (was go-composites/time,
whole-second only) to gain nanosecond sub-second precision and fixed-offset
zones — the foundation for MRI-faithful Time. Mechanical: the ~24 non-Time
bindings that built instants from a Unix second count now call the new
unixTime() helper; .t.ToUnix() → .t.Unix(); tzinfo/timecop wrappers updated.

Verification: go build/vet clean; full internal/vm test suite green (136s).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-09 18:40 CEST

Complete the Ruby Time core class toward MRI 3.4/4.0 on the new time.Time
backing:

Constructors: Time.new (parts, 7th-arg zone, 4.0 in: keyword), Time.utc/gm,
Time.local/mktime (usec 7th arg), Time.at (Integer/Float/Rational/Time, subsec
+ :millisecond/:microsecond/:nanosecond unit, in:), Time.now(in:). MRI field
range-validation (mon/mday/hour/min/sec out of range) with overflow normalised
as MRI does (Feb 30 → Mar 2, hour 24 → next day, sec 60 → next minute).

Parts: usec/nsec/subsec (Rational)/yday/utc_offset/gmt_offset/gmtoff/utc?/gmt?/
dst?/isdst/to_r, alongside the existing year…sec/wday/zone. subsec is Integer 0
on a whole second (MRI).

Conversions: utc/gmtime/localtime mutate-in-place and return self (MRI);
getutc/getlocal(offset) return a new Time; round/floor/ceil(ndigits); to_a;
Time − Time now returns Float seconds; + accepts fractional seconds.

Formatting: a from-scratch strftime covering the full directive set (%Y %C %y
%m %d %e %H %k %I %l %M %S %L %N %j %p %P %A %a %B %b %u %w %s %z %:z %::z %Z
%U %W %G %V %n %t %% and the %c %D %F %R %r %T %x %X %h compounds) with the
-_0^# flags and width; inspect renders the sub-second fraction (4.0);
ctime/asctime.

Deferred (need require "time"/date stdlib, out of core scope): iso8601/
xmlschema/httpdate/rfc2822/rfc822, Time.parse/strptime rewrite (kept as-is).

Verification: CI-exact whole-package coverage gate shows ONLY the tolerated
pre-existing partials (time.go 100%); go test ./internal/vm/ green; gofmt +
go vet clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-09 18:45 CEST

Time conformance work lifts the measured ruby/spec pass total to 12244
(language + core, SPEC_SHA 87b1631). Lock in the win at N−30 = 12214.

Verification: fresh-binary ratchet OK (12244 ≥ floor).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 0b3d7d5 into main Aug 9, 2026
5 of 7 checks passed
@tannevaled
tannevaled deleted the feat/time-conformance branch August 9, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant