Skip to content

Commit

Permalink
fs: expose Stats times as Numbers
Browse files Browse the repository at this point in the history
This also reverts commit 9836cf5.

Fixes: npm/npm#16734
Ref: nodejs#12607
Ref: nodejs#12818
  • Loading branch information
refack committed May 28, 2017
1 parent f84666f commit d88342b
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 115 deletions.
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);
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`);
});
}));

0 comments on commit d88342b

Please sign in to comment.