Skip to content

IntlDurationFormat: default minutes/seconds display to "always" when inheriting numeric from prevStyle - #375

Open
robobun wants to merge 1 commit into
mainfrom
farm/948392b0/intl-durationformat-numeric-display-default
Open

IntlDurationFormat: default minutes/seconds display to "always" when inheriting numeric from prevStyle#375
robobun wants to merge 1 commit into
mainfrom
farm/948392b0/intl-durationformat-numeric-display-default

Conversation

@robobun

@robobun robobun commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

new Intl.DurationFormat("en", { hours: "numeric" }).format({ hours: 1 }) returned "1"; the spec (and V8) require "1:00:00". resolvedOptions() reported minutesDisplay/secondsDisplay as "auto" instead of "always", so zero minutes and seconds were dropped even though they are part of the clock form. { style: "digital" } already produced "1:00:00", so the two spellings of the digital style were inconsistent.

Cause

intlDurationUnitOptions handles ECMA-402 GetDurationUnitOptions. In the branch where the unit's own style option is undefined and baseStyle is not "digital", it set displayDefault = Auto unconditionally before checking prevStyle:

} else {
    displayDefault = IntlDurationFormat::Display::Auto;
    if (prevStyle && (prevStyle.value() == Numeric || prevStyle.value() == TwoDigit))
        style = IntlDurationFormat::UnitStyle::Numeric;
    else
        style = static_cast<IntlDurationFormat::UnitStyle>(baseStyle);
}

Per spec step 3.b.i, when prevStyle is "numeric"/"2-digit" the display default is "auto" only if the unit is not "minutes" or "seconds"; minutes and seconds keep "always".

Fix

Split the non-digital branch so the prevStyle numeric/2-digit case guards displayDefault = Auto behind unit != Minute && unit != Second, mirroring the digital branch directly above it.

Repro

const DF = Intl.DurationFormat;
console.log(new DF("en", { hours: "numeric" }).format({ hours: 1 }));                // "1"     -> "1:00:00"
console.log(new DF("en", { minutes: "numeric" }).format({ minutes: 5 }));            // "5"     -> "5:00"
const o = new DF("en", { hours: "numeric" }).resolvedOptions();
console.log(o.minutesDisplay, o.secondsDisplay, o.millisecondsDisplay);              // "auto" "auto" "auto" -> "always" "always" "auto"

Branched from 34c01d1339 (Bun's current WEBKIT_VERSION) so the paired Bun PR can pin the preview build without pulling in unrelated WebKit changes.

…inheriting numeric from prevStyle

GetDurationUnitOptions step 3.b.i: when the unit style option is
undefined, baseStyle is not "digital", and the previous unit resolved
to "numeric"/"2-digit", only units other than "minutes"/"seconds" get
displayDefault "auto". Minutes and seconds must keep displayDefault
"always" so that a per-unit numeric clock like {hours:"numeric"}
produces the full "1:00:00" form rather than "1".

Before this change the non-digital branch set displayDefault to Auto
unconditionally and only rewrote the inherited style, so the per-unit
spelling diverged from the equivalent {style:"digital"} spelling.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3f1b5aab-d4bd-4cd1-8f9b-f7742f2b5a17

📥 Commits

Reviewing files that changed from the base of the PR and between 34c01d1 and 8394b80.

📒 Files selected for processing (1)
  • Source/JavaScriptCore/runtime/IntlDurationFormat.cpp

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — small, targeted spec-conformance fix.

What was reviewed:

  • Traced the new branch against ECMA-402 GetDurationUnitOptions step 3.b.i — minutes/seconds now keep displayDefault "always" while sub-second units still fall through to "auto".
  • Confirmed the else branch is unchanged in behavior for the non-numeric prevStyle case.
  • Checked which units can reach this path via initializeDurationFormat's prevStyle propagation (Minute/Second/Millisecond/Microsecond/Nanosecond) — all resolve as expected.
Extended reasoning...

Overview

This PR modifies a single conditional in intlDurationUnitOptions (IntlDurationFormat.cpp), which implements ECMA-402's GetDurationUnitOptions. The old code unconditionally set displayDefault = Auto in the non-digital fallback branch before checking prevStyle. The fix hoists the prevStyle == Numeric/TwoDigit check into its own else if and guards displayDefault = Auto behind unit != Minute && unit != Second, so minutes and seconds retain the initial Always default when they inherit numeric style from the preceding unit.

Security risks

None. This is pure Intl option-resolution logic — no memory management, no untrusted-input parsing beyond what already existed, no changes to allocation or control flow that could affect safety.

Level of scrutiny

Low-to-moderate. The diff is 7 lines net, mechanically restructures an existing if/else, and directly mirrors both the spec text (step 3.b.i.1) and the adjacent Digital branch immediately above it (which already uses the same unit != ... && unit != ... guard pattern). The PR description includes a clear repro and cites V8's behavior as the reference, and the fix produces the spec-mandated resolvedOptions() values.

Other factors

I traced the callers: prevStyle is only populated for Hour/Minute/Second/Millisecond/Microsecond in initializeDurationFormat, so the units that can hit the new branch are Minute, Second, Millisecond, Microsecond, and Nanosecond. Minute and Second correctly keep Always; the sub-second units correctly get Auto (matching the PR's expected millisecondsDisplay: "auto"). The final else branch is semantically identical to the old non-prevStyle path. No behavior change for any case that was previously correct.

@github-actions

Copy link
Copy Markdown

Preview Builds

Commit Release Date
8394b80f autobuild-preview-pr-375-8394b80f 2026-07-29 12:54:27 UTC

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