A pure-Go (no cgo) reimplementation of Ruby's date
standard library — MRI 4.0.5's Date and DateTime. It is the deterministic,
interpreter-independent core of calendar arithmetic, parsing and formatting:
construct a date from civil / ordinal / week-date / Julian-Day coordinates, shift
it by days / months / years, render it with the full strftime directive set,
and read it back with Date.parse / Date.strptime — without any Ruby
runtime, matching MRI byte-for-byte across a broad differential corpus.
It is the date backend for
go-embedded-ruby, but is a
standalone, reusable module with no dependency on the Ruby runtime — a sibling
of go-ruby-regexp (the Onigmo engine),
go-ruby-erb (the ERB compiler) and
go-ruby-yaml (the Psych port).
What it is — and isn't. Calendar math, the
strftimedirective engine and theparse/strptimereaders are fully deterministic and need no interpreter, so they live here as pure Go built on the astronomical Julian Day Number — exactly as MRI'sdate_core.cdoes. Binding these values to live RubyDate/DateTimeobjects is the host's job; this library hands back a small, idiomatic Go*Datethe host maps to and from its own objects.
A faithful port of MRI's date, validated against the ruby binary on every
supported platform:
- The Julian Day Number model — arbitrary BC/AD years on the proleptic
calendar, with the Julian → Gregorian reform (
Date::ITALYdefault,ENGLAND/GREGORIAN/JULIAN, or any JDN): pre-1582 dates use the Julian calendar, the 1582-10-05…14 gap is invalid, and leap-year rules switch with the calendar. - Constructors —
NewDate(Date.new/civil),DateJD(jd),Ordinal,Commercial(ISO week date),NewDateTime, plus the deterministicToday/Nowseam (SetToday/SetTodayInstantfor tests — never real time in tests). - The full
strftimedirective set —%Y %y %C %m %B %b %h %d %e %j %H %k %I %l %M %S %L %N %p %P %A %a %u %w %U %W %V %G %g %s %Q %z %Z %D %F %T %R %r %v %c %x %X %n %t %%— with the- _ 0 ^ #flags, an explicit field width, the:/::/:::%zvariants, the ignoredE/Olocale modifiers, and the MRI year-padding quirk (%Yof a negative year is sign-plus-four-digits). Parse(heuristic, multi-format — ISO, slashed/dashed, month-name, RFC-2822, week-date, ordinal;comptwo-digit-year expansion) andStrptime(explicit format, including%s/%Qepoch input).- Arithmetic —
+/-days (Plus/Minus/Diff),>>/<<months (PlusMonths, day clamping),PlusYears,Next*/Prev*, andStep/Upto/Downto. - Accessors —
Year Month Day Wday Yday Cwyear Cweek Cwday Jd Mjd Leap, and forDateTimeHour Min Sec SecFractionNanos Offset. - Named formats —
Iso8601,Rfc3339,Rfc2822,Httpdate,Ctime,Jisx0301(Japanese eras), plusCmp/Equal(instant-aware).
package main
import (
"fmt"
"github.com/go-ruby-date/date"
)
func main() {
d, _ := date.NewDate(2026, 6, 29)
fmt.Println(d.Strftime("%A, %-d %B %Y")) // Monday, 29 June 2026
fmt.Println(d.Iso8601(), d.Jd(), d.Cweek()) // 2026-06-29 2461221 27
fmt.Println(d.PlusMonths(8).String()) // 2027-02-28 (Jun 29 → Feb clamp)
dt, _ := date.NewDateTime(2026, 6, 29, 14, 3, 5, 19800) // +05:30
fmt.Println(dt.Strftime("%Y-%m-%dT%H:%M:%S%:z")) // 2026-06-29T14:03:05+05:30
fmt.Println(dt.Strftime("%s")) // 1782721985
p, _ := date.Parse("June 29, 2026", false)
fmt.Println(p.Iso8601()) // 2026-06-29
s, _ := date.Strptime("2026-180", "%Y-%j")
fmt.Println(s.Iso8601()) // 2026-06-29
}go test -race -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1 # total: (statements) 100.0%The suite has two layers: deterministic, Ruby-free golden tests (with
MRI-4.0.5-verified expectations inline) that hold to 100% coverage on the
no-Ruby, qemu cross-arch and Windows lanes, and a differential MRI oracle
(gated on Ruby ≥ 4.0, $stdout.binmode for exact bytes on Windows) that runs
where ruby is present. CI builds and tests on three OSes and all six supported
64-bit architectures (amd64, arm64, riscv64, loong64, ppc64le, s390x).
BSD-3-Clause — see LICENSE. Copyright the go-ruby-date/date authors.
Being pure Go (CGO=0), this library also compiles to WebAssembly — both
GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI).
CI builds both targets on every push, alongside the six 64-bit native/qemu arches.
GOOS=js GOARCH=wasm go build ./... # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./... # WASI (wasmtime, wasmer, wasmedge, …)