feat(date): use local timezone by default, delegate to strftime (#251)
* feat(date): use local timezone by default, delegate to strftime
Replace the hand-rolled UTC-only format loop with a call to the existing
formatStrftime() from printf/strftime.ts, which already handles local
timezone, TZ env var, and the full set of Linux format specifiers.
- date now shows local time by default; -u still forces UTC
- TZ env var is respected for per-command timezone override
- Adds @timestamp parsing (date -d @1705276800, GNU extension)
- Gains %A %B %C %D %G %g %j %k %l %N %r %U %V %W %x %X at no cost
- Fixes strftime.ts: hour/minute/second = 0 would fall back to local
time via || operator instead of using the Intl-resolved value
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Lars Trieloff <lars@trieloff.net>
* fix(date): address P2/P3 accuracy issues from review
P2 – TZ-aware parsing for bare ISO strings: when TZ is set, a string like
'2024-01-15T00:00:00' is now interpreted in the named timezone rather than
the host timezone. parseBareISOInTimezone() shifts the UTC reference by the
Intl-computed offset so the TZ clock reads the original components. Strings
with an explicit offset (Z or ±HH:MM) are unaffected.
P2 – %U/%W week-number off-by-one: replaced the incorrect formula in
getWeekNumberForParts() with a direct calculation of the first occurrence of
startDay in January, returning 00 for days that fall before it.
P3 – @timestamp suffix validation: parseInt('0abc') silently returned 0;
now the full suffix is validated against /^-?\d+$/ before constructing the
Date, so '@0abc' yields "invalid date".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(date): fix biome formatting
* fix(date): iterative DST resolve in parseBareISOInTimezone
The single-pass offset shift used the TZ offset at the requested
components-as-UTC, which sits on the wrong side of the DST transition
for America/New_York 2024-03-10 and 2024-11-03. Replace with an
iterative resolve: ask Intl what the TZ shows at the candidate and
apply the residual delta, bounded at 3 passes.
Documents edge-case behaviour for skipped and ambiguous wall times in
the function comment.
Adds DST regression tests covering spring-forward, fall-back, the
non-DST winter baseline, and explicit-Z parsing.
* fix(date): stop interpreting bare numeric -d as Unix epoch seconds
GNU 'date -d 2024' parses 2024 as a year (via JS 'new Date(s)'),
not as epoch seconds. The PR briefly short-circuited any all-numeric
-d argument to 'new Date(n * 1000)', turning '-d 2024' into
1970-01-01T00:33:44Z. Only the '@'-prefixed form should mean epoch
seconds, matching GNU date more closely.
Removes the bare-numeric branch from parseDate and adds a regression
test asserting '-u -d 2024 +%Y' outputs '2024'.
* fix(date): validate TZ env var so %Z / %z stay consistent
When TZ is set to a value Intl doesn't understand (e.g. Mars/Olympus),
getDatePartsInTimezone silently fell back to host-local while
getTimezoneName / getTimezoneOffset returned UTC / +0000, so the
displayed wall clock disagreed with the printed offset.
Add a local isValidTimezone helper that probes Intl.DateTimeFormat and
treat an invalid TZ as undefined in date.ts so both parsing and display
fall back to host-local, matching GNU date.
Adds two regression tests: one asserts %Z and %z under an invalid TZ
match the host-local baseline, the other asserts -d parsing of an
explicit-Z ISO string is unaffected by an invalid TZ.
* test(sandbox): update timezone tests to match new local-by-default design
Updates information-disclosure.test.ts to reflect PR #251's contract:
timezone is no longer treated as host-secret; only -u is the contractual
UTC guarantee. Renames the "Timezone Non-Disclosure" block to "Timezone
behavior", switches to full-string assertions, and adds a positive test
for the local-by-default behavior (numeric %z, non-empty %Z).
* fix(date): default to UTC again; $TZ env var opts in to local time
Per maintainer feedback on PR #251, restore UTC as the default display
timezone for sandbox non-disclosure. Local-time display now requires
the caller to opt in by exporting $TZ.
- displayTz defaults to "UTC" when $TZ is unset (was: host-local).
- parseTz keeps its raw value (undefined when unset) so timezone-naive
-d parsing semantics are unchanged.
- Invalid $TZ falls back to UTC (consistent with the no-$TZ default
instead of leaking host-local).
- date.test.ts: flip the default-output and timezone-block tests back
to UTC; add a TZ-opt-in describe asserting PST/PDT and -0800/-0700
under TZ=America/Los_Angeles.
* test(sandbox): restore UTC non-disclosure default, add $TZ opt-in test
Re-aligns the sandbox timezone tests to the PR #251 revised contract:
UTC stays the default (host TZ never leaks unless the caller opts in),
and exporting $TZ enables that specific timezone.
- Rename the describe block back to "Timezone Non-Disclosure".
- Restore the three original full-string assertions: `date +%Z` is
"UTC\n", `date +%z` is "+0000\n", default `date` output contains "UTC".
- Add a new "Timezone opt-in via $TZ" describe with one positive test:
TZ=America/Los_Angeles date +%Z matches /^(PST|PDT)$/ across DST.
* chore: retrigger CI (transient codeload outage)
* docs(date): document TZ env var and UTC default
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Signed-off-by: Lars Trieloff <lars@trieloff.net>
---------
Signed-off-by: Lars Trieloff <lars@trieloff.net>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Droid <droid@factory.ai>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> L
Lars Trieloff committed
fb7ae3cc795a89018369ce8192f27bb8659a7fb5
Parent: c9904de
Committed by GitHub <noreply@github.com>
on 6/3/2026, 11:55:48 PM