Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/bun.js/node/Stat.zig
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ pub fn StatType(comptime big: bool) type {
return @as(i64, sec * std.time.ms_per_s) +|
@as(i64, @divTrunc(nsec, std.time.ns_per_ms));
} else {
return @floatFromInt(bun.timespec.ms(&bun.timespec{
.sec = @intCast(tv_sec),
.nsec = @intCast(tv_nsec),
}));
// Use floating-point arithmetic to preserve sub-millisecond precision.
// Node.js returns fractional milliseconds (e.g. 1773248895434.0544).
const sec_ms: f64 = @as(f64, @floatFromInt(tv_sec)) * 1000.0;
const nsec_ms: f64 = @as(f64, @floatFromInt(tv_nsec)) / 1_000_000.0;
return sec_ms + nsec_ms;
}
}

Expand Down
34 changes: 34 additions & 0 deletions test/regression/issue/28017.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { expect, test } from "bun:test";
import { statSync, utimesSync } from "fs";
import { stat } from "fs/promises";
import { tempDir } from "harness";
import { join } from "path";

test("fs.stat *Ms properties preserve sub-millisecond precision", async () => {
using dir = tempDir("stat-ms-precision", {
"test.txt": "hello",
});

const filePath = join(String(dir), "test.txt");

// Set known fractional timestamps: 1700000000.5005 seconds = 1700000000500.5 ms
// Using .5005 (500.5ms) ensures clear sub-millisecond component (.5ms)
// that survives filesystem round-trip even with microsecond-precision utimes.
const fractionalTime = 1700000000.5005;
utimesSync(filePath, fractionalTime, fractionalTime);

const syncStats = statSync(filePath);
const asyncStats = await stat(filePath);

const expectedMs = fractionalTime * 1000; // 1700000000500.5

for (const s of [syncStats, asyncStats]) {
// atimeMs and mtimeMs should have fractional milliseconds
expect(Number.isInteger(s.atimeMs)).toBe(false);
expect(Number.isInteger(s.mtimeMs)).toBe(false);

// Verify the values match what we set (within 0.05ms / 50 microsecond tolerance)
expect(s.atimeMs).toBeCloseTo(expectedMs, 1);
expect(s.mtimeMs).toBeCloseTo(expectedMs, 1);
}
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
Loading