Skip to content

fs: expose Stats times as Numbers #13257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
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
30 changes: 18 additions & 12 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,14 @@ argument to `fs.createReadStream()`. If `path` is passed as a string, then
## Class: fs.Stats
<!-- YAML
added: v0.1.21
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/13173
description: Added times as numbers.
-->

Objects returned from [`fs.stat()`][], [`fs.lstat()`][] and [`fs.fstat()`][] and their
synchronous counterparts are of this type.
Objects returned from [`fs.stat()`][], [`fs.lstat()`][] and [`fs.fstat()`][] and
their synchronous counterparts are of this type.

- `stats.isFile()`
- `stats.isDirectory()`
Expand All @@ -323,20 +327,22 @@ Stats {
size: 527,
blksize: 4096,
blocks: 8,
atimeMs: 1318289051000.1,
mtimeMs: 1318289051000.1,
ctimeMs: 1318289051000.1,
birthtimeMs: 1318289051000.1,
atime: Mon, 10 Oct 2011 23:24:11 GMT,
mtime: Mon, 10 Oct 2011 23:24:11 GMT,
ctime: Mon, 10 Oct 2011 23:24:11 GMT,
birthtime: Mon, 10 Oct 2011 23:24:11 GMT }
```

Please note that `atime`, `mtime`, `birthtime`, and `ctime` are
instances of [`Date`][MDN-Date] object and appropriate methods should be used
to compare the values of these objects. For most general uses
[`getTime()`][MDN-Date-getTime] will return the number of milliseconds elapsed
since _1 January 1970 00:00:00 UTC_ and this integer should be sufficient for
any comparison, however there are additional methods which can be used for
displaying fuzzy information. More details can be found in the
[MDN JavaScript Reference][MDN-Date] page.
*Note*: `atimeMs`, `mtimeMs`, `ctimeMs`, `birthtimeMs` are [numbers][MDN-Number]
that hold the corresponding times in milliseconds. Their precision is platform
specific. `atime`, `mtime`, `ctime`, and `birthtime` are [`Date`][MDN-Date]
object alternate representations of the various times. The `Date` and number
values are not connected. Assigning a new number value, or mutating the `Date`
value, will not be reflected in the corresponding alternate representation.

### Stat Time Values

Expand Down Expand Up @@ -527,7 +533,7 @@ The "not recommended" examples above check for accessibility and then use the
file; the "recommended" examples are better because they use the file directly
and handle the error, if any.

In general, check for the accessibility of a file only if the file wont be
In general, check for the accessibility of a file only if the file won't be
used directly, for example when its accessibility is a signal from another
process.

Expand Down Expand Up @@ -959,7 +965,7 @@ The "not recommended" examples above check for existence and then use the
file; the "recommended" examples are better because they use the file directly
and handle the error, if any.

In general, check for the existence of a file only if the file wont be
In general, check for the existence of a file only if the file won't be
used directly, for example when its existence is a signal from another
process.

Expand Down
80 changes: 8 additions & 72 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,81 +196,17 @@ function Stats(
this.ino = ino;
this.size = size;
this.blocks = blocks;
this._atim_msec = atim_msec;
this._mtim_msec = mtim_msec;
this._ctim_msec = ctim_msec;
this._birthtim_msec = birthtim_msec;
this.atimeMs = atim_msec;
this.mtimeMs = mtim_msec;
this.ctimeMs = ctim_msec;
this.birthtimeMs = birthtim_msec;
this.atime = new Date(atim_msec);
this.mtime = new Date(mtim_msec);
this.ctime = new Date(ctim_msec);
this.birthtime = new Date(birthtim_msec);
}
fs.Stats = Stats;

// defining the properties in this fashion (explicitly with no loop or factory)
// has been shown to be the most performant on V8 contemp.
// Ref: https://github.com/nodejs/node/pull/12818
// + 0.5 is added to the Dates to protect values from being rounded down
// Ref: https://github.com/nodejs/node/pull/12607
Object.defineProperties(Stats.prototype, {
atime: {
configurable: true,
enumerable: true,
get() {
return this._atime !== undefined ?
this._atime :
(this._atime = new Date(this._atim_msec + 0.5));
},
set(value) { return this._atime = value; }
},
mtime: {
configurable: true,
enumerable: true,
get() {
return this._mtime !== undefined ?
this._mtime :
(this._mtime = new Date(this._mtim_msec + 0.5));
},
set(value) { return this._mtime = value; }
},
ctime: {
configurable: true,
enumerable: true,
get() {
return this._ctime !== undefined ?
this._ctime :
(this._ctime = new Date(this._ctim_msec + 0.5));
},
set(value) { return this._ctime = value; }
},
birthtime: {
configurable: true,
enumerable: true,
get() {
return this._birthtime !== undefined ?
this._birthtime :
(this._birthtime = new Date(this._birthtim_msec + 0.5));
},
set(value) { return this._birthtime = value; }
},
});

Stats.prototype.toJSON = function toJSON() {
return {
dev: this.dev,
mode: this.mode,
nlink: this.nlink,
uid: this.uid,
gid: this.gid,
rdev: this.rdev,
blksize: this.blksize,
ino: this.ino,
size: this.size,
blocks: this.blocks,
atime: this.atime,
ctime: this.ctime,
mtime: this.mtime,
birthtime: this.birthtime
};
};


Stats.prototype._checkModeProperty = function(property) {
return ((this.mode & S_IFMT) === property);
};
Expand Down
57 changes: 26 additions & 31 deletions test/parallel/test-fs-stat.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,60 +65,55 @@ fs.open('.', 'r', undefined, common.mustCall(function(err, fd) {
assert.fail(err);
}
if (stats) {
console.dir(stats);
assert.ok(stats.mtime instanceof Date);
}
fs.close(fd, assert.ifError);
}));

console.log(`stating: ${__filename}`);
fs.stat(__filename, common.mustCall(function(err, s) {
assert.ifError(err);

console.dir(s);

console.log(`isDirectory: ${JSON.stringify(s.isDirectory())}`);
assert.strictEqual(false, s.isDirectory());

console.log(`isFile: ${JSON.stringify(s.isFile())}`);
assert.strictEqual(true, s.isFile());

console.log(`isSocket: ${JSON.stringify(s.isSocket())}`);
assert.strictEqual(false, s.isSocket());

console.log(`isBlockDevice: ${JSON.stringify(s.isBlockDevice())}`);
assert.strictEqual(false, s.isBlockDevice());

console.log(`isCharacterDevice: ${JSON.stringify(s.isCharacterDevice())}`);
assert.strictEqual(false, s.isCharacterDevice());

console.log(`isFIFO: ${JSON.stringify(s.isFIFO())}`);
assert.strictEqual(false, s.isFIFO());

console.log(`isSymbolicLink: ${JSON.stringify(s.isSymbolicLink())}`);
assert.strictEqual(false, s.isSymbolicLink());

assert.ok(s.atime instanceof Date);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are these removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

New Line 99

assert.ok(s.mtime instanceof Date);
assert.ok(s.ctime instanceof Date);
assert.ok(s.birthtime instanceof Date);
}));

fs.stat(__filename, common.mustCall(function(err, s) {
const json = JSON.parse(JSON.stringify(s));
const keys = [
'dev', 'mode', 'nlink', 'uid',
'gid', 'rdev', 'ino',
'size', 'atime', 'mtime',
'ctime', 'birthtime'
'gid', 'rdev', 'ino', 'size',
'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs',
'atime', 'mtime', 'ctime', 'birthtime'
];
const dateFields = [ 'atime', 'mtime', 'ctime', 'birthtime' ];
const numberFields = [
'dev', 'mode', 'nlink', 'uid', 'gid', 'rdev', 'ino', 'size',
'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs'
];
if (!common.isWindows) {
keys.push('blocks', 'blksize');
numberFields.push('blocks', 'blksize');
}
const actualKeys = Object.keys(s);
keys.forEach((k) => assert.strictEqual(actualKeys.includes(k), true,
`${k} should a field of s`));
numberFields.forEach((k) => {
assert.strictEqual(typeof s[k], 'number', `${k} should be a number`);
});
dateFields.forEach((k) => {
assert.ok(s[k] instanceof Date, `${k} should be a Date`);
});
const parsed = JSON.parse(JSON.stringify(s));
keys.forEach(function(k) {
assert.ok(
json[k] !== undefined && json[k] !== null,
parsed[k] !== undefined && parsed[k] !== null,
k + ' should not be null or undefined'
);
});
numberFields.forEach((k) => {
assert.strictEqual(typeof parsed[k], 'number', `${k} should be a number`);
});
dateFields.forEach((k) => {
assert.strictEqual(typeof parsed[k], 'string', `${k} should be a string`);
});
}));