Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

feat(napi): support std::time::SystemTime - #3347

#3347
Open
bg-softcom wants to merge 1 commit into
napi-rs:mainnapi-rs/napi-rs:mainfrom
softcom-su:system-timesoftcom-su/napi-rs:system-timeCopy head branch name to clipboard
Open

feat(napi): support std::time::SystemTime#3347
bg-softcom wants to merge 1 commit into
napi-rs:mainnapi-rs/napi-rs:mainfrom
softcom-su:system-timesoftcom-su/napi-rs:system-timeCopy head branch name to clipboard

Conversation

@bg-softcom

@bg-softcom bg-softcom commented Jun 23, 2026

Copy link
Copy Markdown

Added support for std::time::SystemTime based on existing support for chrono::DateTime.

For that, I've split date.rs into 4 files:

  • date.rs containing module declarations for other files
  • chrono.rs containing previous date.rs content with minor changes (more below)
  • time.rs containing new code for std::time::SystemTime support
  • common.rs containing common logic used by chrono.rs and time.rs.

I've extracted validation and conversion between f64 and JS Date into thin helper functions which are reused by existing and new code. This changes the error messages in some cases to avoid referencing the specific type for which the conversion is performed.

Summary by CodeRabbit

  • New Features

    • Added support for converting Rust SystemTime values to and from JavaScript Date objects.
    • Supports dates before the Unix epoch and validates invalid or out-of-range dates.
    • Added example bindings for converting dates to milliseconds and returning positive or pre-epoch dates.
  • Tests

    • Added coverage for normal, negative, invalid, and boundary date conversions.
    • Updated generated type declarations and snapshots.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds SystemTime support for JavaScript Date conversion, extracts shared date helpers, relocates chrono bindings, updates type generation and example exports, and adds tests for normal, invalid, and pre-epoch timestamps.

Changes

SystemTime N-API Binding and Date Module Refactor

Layer / File(s) Summary
Shared date N-API helpers
crates/napi/src/bindgen_runtime/js_values/date/common.rs
Adds helpers for validating JavaScript dates, reading epoch milliseconds, and constructing dates with range checks.
Date module restructuring and chrono migration
crates/napi/src/bindgen_runtime/js_values.rs, crates/napi/src/bindgen_runtime/js_values/date.rs, crates/napi/src/bindgen_runtime/js_values/date/chrono.rs
Splits date implementations into submodules, updates the napi5 gate, and moves chrono conversions to the dedicated module.
SystemTime binding and type generation
crates/backend/src/typegen.rs, crates/napi/src/bindgen_runtime/js_values/date/time.rs
Maps SystemTime to TypeScript Date and implements validated conversion in both directions, including negative timestamps and checked bounds.
Example API wiring and validation
examples/napi/src/date.rs, examples/napi/index.cjs, examples/napi/index.d.cts, examples/napi/example.wasi.*, examples/napi/__tests__/*
Adds SystemTime example functions, exports and declarations, updates the snapshot, and tests normal, invalid, and pre-epoch behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant JSCaller
  participant NAPIExample
  participant SystemTimeBinding
  participant DateHelpers
  participant JavaScriptDate

  JSCaller->>NAPIExample: systemTimeToMillis(Date)
  NAPIExample->>SystemTimeBinding: FromNapiValue
  SystemTimeBinding->>DateHelpers: get_date_millis
  DateHelpers->>JavaScriptDate: napi_get_date_value
  JavaScriptDate-->>DateHelpers: epoch milliseconds
  DateHelpers-->>SystemTimeBinding: milliseconds
  SystemTimeBinding-->>NAPIExample: SystemTime
  NAPIExample-->>JSCaller: number

  JSCaller->>NAPIExample: systemTimeReturn()
  NAPIExample->>SystemTimeBinding: ToNapiValue
  SystemTimeBinding->>DateHelpers: date_from_millis
  DateHelpers->>JavaScriptDate: napi_create_date
  JavaScriptDate-->>JSCaller: Date
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding std::time::SystemTime support in napi.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
examples/napi/__tests__/values.spec.ts (1)

2069-2086: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a pre-epoch Date round-trip test to lock regression coverage.

Please add a case like new Date(-1) for systemTimeToMillis so Line 36 in examples/napi/src/date.rs can’t regress back to a panic path unnoticed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/napi/__tests__/values.spec.ts` around lines 2069 - 2086, Add a new
test case using Napi5Test to verify that systemTimeToMillis correctly handles
pre-epoch dates (negative timestamps). Create a test that instantiates a Date
with a negative value such as new Date(-1) and verifies that systemTimeToMillis
returns the expected result, ensuring that the implementation at Line 36 in
examples/napi/src/date.rs does not regress into a panic path. This test should
be placed alongside the existing Date to SystemTime tests to provide
comprehensive coverage for edge cases.
crates/napi/src/bindgen_runtime/js_values/date/chrono.rs (1)

107-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse date_from_millis here for consistency and bounds checking.

NaiveDateTime::to_napi_value (line 29) goes through the shared date_from_millis helper, but this DateTime<Tz> impl calls sys::napi_create_date directly. This bypasses the timeClip range validation (abs() > 8.64e15) that the helper provides, so an out-of-range timestamp_millis() can silently produce an invalid JS Date instead of surfacing an error. Aligning it with the helper also matches the PR's de-duplication goal.

♻️ Proposed change
 impl<Tz: TimeZone> ToNapiValue for DateTime<Tz> {
   unsafe fn to_napi_value(env: sys::napi_env, val: DateTime<Tz>) -> Result<sys::napi_value> {
-    let mut ptr = std::ptr::null_mut();
-    let millis_since_epoch_utc = val.timestamp_millis() as f64;
-
-    check_status!(
-      unsafe { sys::napi_create_date(env, millis_since_epoch_utc, &mut ptr) },
-      "Failed to convert rust type `DateTime` into napi value",
-    )?;
-
-    Ok(ptr)
+    let millis_since_epoch_utc = val.timestamp_millis() as f64;
+    unsafe { date_from_millis(env, millis_since_epoch_utc) }
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/napi/src/bindgen_runtime/js_values/date/chrono.rs` around lines 107 -
119, The `DateTime<Tz>` implementation of `to_napi_value` is calling
`sys::napi_create_date` directly instead of reusing the shared
`date_from_millis` helper that is already used in the
`NaiveDateTime::to_napi_value` implementation. Replace the direct call to
`sys::napi_create_date` with a call to the `date_from_millis` helper function,
passing the result of `val.timestamp_millis()` as the argument. This ensures the
helper's timeClip range validation is applied to catch out-of-range timestamps
and maintains consistency across implementations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/napi/src/bindgen_runtime/js_values/date.rs`:
- Around line 1-2: Change the feature gate on the chrono module declaration from
`#[cfg(feature = "chrono")]` to `#[cfg(feature = "chrono_date")]` to match the
actual feature name defined in Cargo.toml. The `chrono_date` feature is the one
declared in the manifest as a composite feature that includes chrono and napi5,
so the mod chrono statement needs to reference this correct feature name for the
module to compile when the feature is enabled.

In `@crates/napi/src/bindgen_runtime/js_values/date/common.rs`:
- Around line 59-64: The validation guard checking whether
millis_since_epoch.abs() exceeds 8.64e15 does not catch NaN values, since NaN
comparisons always return false, allowing NaN to bypass the check and silently
create an Invalid Date object. Add a finiteness check using is_finite() on
millis_since_epoch before or alongside the existing magnitude check to ensure
NaN values are rejected and return an appropriate error, preventing invalid
dates from being silently created when NaN is passed as input.

In `@examples/napi/src/date.rs`:
- Around line 34-37: The system_time_to_millis function calls unwrap() on the
result of input.duration_since(UNIX_EPOCH), which will panic if the input is a
pre-epoch timestamp. Remove the unwrap() call and instead handle the error case
gracefully. Either check if the result is an error and handle pre-epoch
timestamps appropriately (such as returning 0, a negative value, or calculating
the duration differently), or use a method like unwrap_or() to provide a safe
default behavior that prevents panicking on valid pre-epoch SystemTime values.

---

Nitpick comments:
In `@crates/napi/src/bindgen_runtime/js_values/date/chrono.rs`:
- Around line 107-119: The `DateTime<Tz>` implementation of `to_napi_value` is
calling `sys::napi_create_date` directly instead of reusing the shared
`date_from_millis` helper that is already used in the
`NaiveDateTime::to_napi_value` implementation. Replace the direct call to
`sys::napi_create_date` with a call to the `date_from_millis` helper function,
passing the result of `val.timestamp_millis()` as the argument. This ensures the
helper's timeClip range validation is applied to catch out-of-range timestamps
and maintains consistency across implementations.

In `@examples/napi/__tests__/values.spec.ts`:
- Around line 2069-2086: Add a new test case using Napi5Test to verify that
systemTimeToMillis correctly handles pre-epoch dates (negative timestamps).
Create a test that instantiates a Date with a negative value such as new
Date(-1) and verifies that systemTimeToMillis returns the expected result,
ensuring that the implementation at Line 36 in examples/napi/src/date.rs does
not regress into a panic path. This test should be placed alongside the existing
Date to SystemTime tests to provide comprehensive coverage for edge cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01336344-f8d2-40e0-97a5-d7b39bc45b7c

📥 Commits

Reviewing files that changed from the base of the PR and between 04e2a76 and 25cd897.

⛔ Files ignored due to path filters (1)
  • examples/napi/__tests__/__snapshots__/values.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (13)
  • crates/backend/src/typegen.rs
  • crates/napi/src/bindgen_runtime/js_values.rs
  • crates/napi/src/bindgen_runtime/js_values/date.rs
  • crates/napi/src/bindgen_runtime/js_values/date/chrono.rs
  • crates/napi/src/bindgen_runtime/js_values/date/common.rs
  • crates/napi/src/bindgen_runtime/js_values/date/time.rs
  • examples/napi/__tests__/__snapshots__/values.spec.ts.md
  • examples/napi/__tests__/values.spec.ts
  • examples/napi/example.wasi-browser.js
  • examples/napi/example.wasi.cjs
  • examples/napi/index.cjs
  • examples/napi/index.d.cts
  • examples/napi/src/date.rs

Comment thread crates/napi/src/bindgen_runtime/js_values/date.rs Outdated
Comment thread crates/napi/src/bindgen_runtime/js_values/date/common.rs
Comment thread examples/napi/src/date.rs
@Brooooooklyn

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 3ae9480140

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Brooooooklyn

Copy link
Copy Markdown
Member

Reviewed this against latest main and resolved the merge locally. Summary below.

Merge conflict

Merging main (currently at 441ae7a7) surfaced one conflict: examples/napi/__tests__/__snapshots__/values.spec.ts.snap — the binary AVA snapshot, which can't be text-merged. The other 53 touched files auto-merged cleanly. Resolution is just to regenerate it:

yarn workspace @examples/napi build
yarn workspace @examples/napi test __tests__/values.spec.ts -u

The re-gate of mod date from chrono_datenapi5 is correct: napi_create_date / napi_is_date / napi_get_date_value all live in napi-sys's #[cfg(feature = "napi5")] mod napi5, so the date module lines up exactly with the underlying sys functions.

Review — verdict: Approve with nits

Did a multi-lens adversarial review (conversion correctness, refactor fidelity, feature gating, codegen/tests, API consistency). The SystemTime sign logic is correct in both directions (hand-traced the pre-epoch round-trip), chrono behavior is preserved byte-for-byte, and the date_from_millis TimeClip guard is sound. No correctness bug, panic, or compile error. Four minor items:

# Sev Location Issue
A low values.spec.ts (new tests) Pre-epoch / negative SystemTime branches (time.rs:27 Err path, time.rs:40-41 checked_sub) have no test coverage — a sign flip would still pass CI
B low values.spec.ts (NaN test) The error message is passed as t.throws's 3rd positional arg (AVA display label), not a matcher — so the message string is never actually asserted, only { code }
C nit chrono.rs DateTime::to_napi_value Routes NaiveDateTime + SystemTime through the guarded date_from_millis, but leaves DateTime<Tz> on raw napi_create_date — inconsistent (guard is unreachable for chrono, so zero runtime impact)
D nit chrono.rs DateTime::from_napi_value Error-context string changed via the shared get_date_millis helper — validation-unreachable path, no test depends on it. Fine to keep (the new shared message is arguably clearer)

Fixes ready to land

I've resolved the merge + addressed A/B/C on a local branch (D kept as-is — reverting it would re-duplicate what the refactor de-duplicated). The changes:

A — cover pre-epoch conversion in both directions
Napi5Test('Pre-epoch Date to SystemTime test', (t) => {
  const fixture = new Date(-1000)
  t.is(systemTimeToMillis(fixture), -1000)
})

Napi5Test('Pre-epoch Date from SystemTime test', (t) => {
  const fixture = systemTimeReturnNegative()
  t.true(fixture instanceof Date)
  t.is(fixture?.toISOString(), '1969-12-31T23:59:59.000Z')
})
// examples/napi/src/date.rs
#[napi]
pub fn system_time_return_negative() -> SystemTime {
  // 1969-12-31T23:59:59.000, i.e. one second before the UNIX epoch
  UNIX_EPOCH - std::time::Duration::from_secs(1)
}
B — actually assert the error message
Napi5Test('NaN Date to SystemTime test', (t) => {
  const fixture = new Date(NaN)
  t.throws(() => systemTimeToMillis(fixture), {
    code: 'DateExpected',
    message: 'Date cannot be represented as rust type `SystemTime`',
  })
})
C — make all three Date conversions consistent
impl<Tz: TimeZone> ToNapiValue for DateTime<Tz> {
  unsafe fn to_napi_value(env: sys::napi_env, val: DateTime<Tz>) -> Result<sys::napi_value> {
    let millis_since_epoch_utc = val.timestamp_millis() as f64;
    unsafe { date_from_millis(env, millis_since_epoch_utc) }
  }
}

With these applied: 169 tests pass (was 167), cargo fmt + clippy clean under napi5 / napi5,chrono_date / napi4.

I couldn't push these directly since "Allow edits by maintainers" is off on this PR. If you enable it I'll push the resolved branch; otherwise the diffs above apply cleanly on top of a main merge + snapshot regen.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/napi/CHANGELOG.md`:
- Line 191: The changelog entry in CHANGELOG.md uses awkward subsystem wording,
so update the text in the release note entry for the non-dyn-symbols change to
use the hyphenated form consistently. Locate the existing “add back non
dyn-symbols behavior” entry and revise only the wording so it reads as a proper
hyphenated subsystem name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5edd666a-0601-41ee-a029-feb77aad20ad

📥 Commits

Reviewing files that changed from the base of the PR and between 379a312 and 19b22b6.

⛔ Files ignored due to path filters (1)
  • examples/napi/__tests__/__snapshots__/values.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (7)
  • crates/napi/CHANGELOG.md
  • examples/napi/__tests__/__snapshots__/values.spec.ts.md
  • examples/napi/__tests__/values.spec.ts
  • examples/napi/example.wasi-browser.js
  • examples/napi/example.wasi.cjs
  • examples/napi/index.cjs
  • examples/napi/index.d.cts
✅ Files skipped from review due to trivial changes (1)
  • examples/napi/tests/snapshots/values.spec.ts.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • examples/napi/example.wasi-browser.js
  • examples/napi/index.d.cts
  • examples/napi/example.wasi.cjs
  • examples/napi/index.cjs
  • examples/napi/tests/values.spec.ts

Comment thread crates/napi/CHANGELOG.md Outdated
@bg-softcom

Copy link
Copy Markdown
Author

@Brooooooklyn, Could you look at this again, please?

My colleague had applied the patches while I was on vacation, and I have rebased the changes on top of main as GitHub wanted all commits to be properly signed for some reason.

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.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.