From d88342bed6d9cafb21fa459e83a1d3d4df7eb020 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Tue, 23 May 2017 12:15:56 -0400 Subject: [PATCH] fs: expose Stats times as Numbers This also reverts commit 9836cf571708a82396218957cacb3ed1ed468d05. Fixes: https://github.com/npm/npm/issues/16734 Ref: https://github.com/nodejs/node/pull/12607 Ref: https://github.com/nodejs/node/pull/12818 --- doc/api/fs.md | 30 +++++++------ lib/fs.js | 80 ++++------------------------------- test/parallel/test-fs-stat.js | 57 ++++++++++++------------- 3 files changed, 52 insertions(+), 115 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index a3edb245ed1743..f28d08eaccd101 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -295,10 +295,14 @@ argument to `fs.createReadStream()`. If `path` is passed as a string, then ## Class: fs.Stats -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()` @@ -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 @@ -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 won’t 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. @@ -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 won’t 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. diff --git a/lib/fs.js b/lib/fs.js index 37dcba0b3bd517..c4da2672a49750 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -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); }; diff --git a/test/parallel/test-fs-stat.js b/test/parallel/test-fs-stat.js index 0769f79f1c502d..cc217c8e829296 100644 --- a/test/parallel/test-fs-stat.js +++ b/test/parallel/test-fs-stat.js @@ -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`); + }); }));