Skip to content

Commit 8cebcf4

Browse files
fiskersindresorhus
andauthoredJan 30, 2025··
Add consistent-date-clone rule (#2544)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
1 parent e5e17f8 commit 8cebcf4

8 files changed

+235
-0
lines changed
 

‎docs/rules/consistent-date-clone.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Prefer passing `Date` directly to the constructor when cloning
2+
3+
💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs).
4+
5+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6+
7+
<!-- end auto-generated rule header -->
8+
<!-- Do not manually modify this header. Run: `npm run fix:eslint-docs` -->
9+
10+
The [`Date` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date) can clone a `⁠Date` object directly when passed as an argument, making timestamp conversion unnecessary.
11+
12+
> Note: Before ES2015, `new Date(date)` converted `date` to a string first, so it's not safe to clone.
13+
14+
## Examples
15+
16+
```js
17+
//
18+
new Date(date.getTime());
19+
20+
//
21+
new Date(date);
22+
```

‎readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export default [
5959
| [better-regex](docs/rules/better-regex.md) | Improve regexes by making them shorter, consistent, and safer. | | 🔧 | |
6060
| [catch-error-name](docs/rules/catch-error-name.md) | Enforce a specific parameter name in catch clauses. || 🔧 | |
6161
| [consistent-assert](docs/rules/consistent-assert.md) | Enforce consistent assertion style with `node:assert`. || 🔧 | |
62+
| [consistent-date-clone](docs/rules/consistent-date-clone.md) | Prefer passing `Date` directly to the constructor when cloning. || 🔧 | |
6263
| [consistent-destructuring](docs/rules/consistent-destructuring.md) | Use destructured variables over properties. | | 🔧 | 💡 |
6364
| [consistent-empty-array-spread](docs/rules/consistent-empty-array-spread.md) | Prefer consistent types when spreading a ternary in an array literal. || 🔧 | |
6465
| [consistent-existence-index-check](docs/rules/consistent-existence-index-check.md) | Enforce consistent style for element existence checks with `indexOf()`, `lastIndexOf()`, `findIndex()`, and `findLastIndex()`. || 🔧 | |

‎rules/consistent-date-clone.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {isMethodCall, isNewExpression} from './ast/index.js';
2+
import {removeMethodCall} from './fix/index.js';
3+
4+
const MESSAGE_ID_ERROR = 'consistent-date-clone/error';
5+
const messages = {
6+
[MESSAGE_ID_ERROR]: 'Unnecessary `.getTime()` call.',
7+
};
8+
9+
/** @param {import('eslint').Rule.RuleContext} context */
10+
const create = context => ({
11+
NewExpression(newExpression) {
12+
if (!isNewExpression(newExpression, {name: 'Date', argumentsLength: 1})) {
13+
return;
14+
}
15+
16+
const [callExpression] = newExpression.arguments;
17+
18+
if (!isMethodCall(callExpression, {
19+
method: 'getTime',
20+
argumentsLength: 0,
21+
optionalCall: false,
22+
optionalMember: false,
23+
})) {
24+
return;
25+
}
26+
27+
return {
28+
node: callExpression,
29+
loc: {start: callExpression.callee.property.loc.start, end: callExpression.loc.end},
30+
messageId: MESSAGE_ID_ERROR,
31+
fix: fixer => removeMethodCall(fixer, callExpression, context.sourceCode),
32+
};
33+
},
34+
});
35+
36+
/** @type {import('eslint').Rule.RuleModule} */
37+
const config = {
38+
create,
39+
meta: {
40+
type: 'suggestion',
41+
docs: {
42+
description: 'Prefer passing `Date` directly to the constructor when cloning.',
43+
recommended: true,
44+
},
45+
fixable: 'code',
46+
messages,
47+
},
48+
};
49+
50+
export default config;

‎rules/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {createRule} from './utils/rule.js';
44
import betterRegex from './better-regex.js';
55
import catchErrorName from './catch-error-name.js';
66
import consistentAssert from './consistent-assert.js';
7+
import consistentDateClone from './consistent-date-clone.js';
78
import consistentDestructuring from './consistent-destructuring.js';
89
import consistentEmptyArraySpread from './consistent-empty-array-spread.js';
910
import consistentExistenceIndexCheck from './consistent-existence-index-check.js';
@@ -132,6 +133,7 @@ const rules = {
132133
'better-regex': createRule(betterRegex, 'better-regex'),
133134
'catch-error-name': createRule(catchErrorName, 'catch-error-name'),
134135
'consistent-assert': createRule(consistentAssert, 'consistent-assert'),
136+
'consistent-date-clone': createRule(consistentDateClone, 'consistent-date-clone'),
135137
'consistent-destructuring': createRule(consistentDestructuring, 'consistent-destructuring'),
136138
'consistent-empty-array-spread': createRule(consistentEmptyArraySpread, 'consistent-empty-array-spread'),
137139
'consistent-existence-index-check': createRule(consistentExistenceIndexCheck, 'consistent-existence-index-check'),

‎test/consistent-date-clone.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import outdent from 'outdent';
2+
import {getTester} from './utils/test.js';
3+
4+
const {test} = getTester(import.meta);
5+
6+
test.snapshot({
7+
valid: [
8+
'new Date(date)',
9+
'date.getTime()',
10+
'new Date(...date.getTime())',
11+
'new Date(getTime())',
12+
'new Date(date.getTime(), extraArgument)',
13+
'new Date(date.not_getTime())',
14+
'new Date(date?.getTime())',
15+
'new NotDate(date.getTime())',
16+
'new Date(date[getTime]())',
17+
'new Date(date.getTime(extraArgument))',
18+
'Date(date.getTime())',
19+
// We may support these cases in future, https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2437
20+
outdent`
21+
new Date(
22+
date.getFullYear(),
23+
date.getMonth(),
24+
date.getDate(),
25+
date.getHours(),
26+
date.getMinutes(),
27+
date.getSeconds(),
28+
date.getMilliseconds(),
29+
);
30+
`,
31+
outdent`
32+
new Date(
33+
date.getFullYear(),
34+
date.getMonth(),
35+
date.getDate(),
36+
date.getHours(),
37+
date.getMinutes(),
38+
date.getSeconds(),
39+
);
40+
`,
41+
],
42+
invalid: [
43+
'new Date(date.getTime())',
44+
'new Date(date.getTime(),)',
45+
'new Date((0, date).getTime())',
46+
'new Date(date.getTime(/* comment */))',
47+
'new Date(date./* comment */getTime())',
48+
],
49+
});

‎test/package.js

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const RULES_WITHOUT_PASS_FAIL_SECTIONS = new Set([
3737
'no-named-default',
3838
'consistent-assert',
3939
'no-accessor-recursion',
40+
'consistent-date-clone',
4041
]);
4142

4243
test('Every rule is defined in index file in alphabetical order', t => {
+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Snapshot report for `test/consistent-date-clone.js`
2+
3+
The actual snapshot is saved in `consistent-date-clone.js.snap`.
4+
5+
Generated by [AVA](https://avajs.dev).
6+
7+
## invalid(1): new Date(date.getTime())
8+
9+
> Input
10+
11+
`␊
12+
1 | new Date(date.getTime())␊
13+
`
14+
15+
> Output
16+
17+
`␊
18+
1 | new Date(date)␊
19+
`
20+
21+
> Error 1/1
22+
23+
`␊
24+
> 1 | new Date(date.getTime())␊
25+
| ^^^^^^^^^ Unnecessary \`.getTime()\` call.␊
26+
`
27+
28+
## invalid(2): new Date(date.getTime(),)
29+
30+
> Input
31+
32+
`␊
33+
1 | new Date(date.getTime(),)␊
34+
`
35+
36+
> Output
37+
38+
`␊
39+
1 | new Date(date,)␊
40+
`
41+
42+
> Error 1/1
43+
44+
`␊
45+
> 1 | new Date(date.getTime(),)␊
46+
| ^^^^^^^^^ Unnecessary \`.getTime()\` call.␊
47+
`
48+
49+
## invalid(3): new Date((0, date).getTime())
50+
51+
> Input
52+
53+
`␊
54+
1 | new Date((0, date).getTime())␊
55+
`
56+
57+
> Output
58+
59+
`␊
60+
1 | new Date((0, date))␊
61+
`
62+
63+
> Error 1/1
64+
65+
`␊
66+
> 1 | new Date((0, date).getTime())␊
67+
| ^^^^^^^^^ Unnecessary \`.getTime()\` call.␊
68+
`
69+
70+
## invalid(4): new Date(date.getTime(/* comment */))
71+
72+
> Input
73+
74+
`␊
75+
1 | new Date(date.getTime(/* comment */))␊
76+
`
77+
78+
> Output
79+
80+
`␊
81+
1 | new Date(date)␊
82+
`
83+
84+
> Error 1/1
85+
86+
`␊
87+
> 1 | new Date(date.getTime(/* comment */))␊
88+
| ^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`.getTime()\` call.␊
89+
`
90+
91+
## invalid(5): new Date(date./* comment */getTime())
92+
93+
> Input
94+
95+
`␊
96+
1 | new Date(date./* comment */getTime())␊
97+
`
98+
99+
> Output
100+
101+
`␊
102+
1 | new Date(date)␊
103+
`
104+
105+
> Error 1/1
106+
107+
`␊
108+
> 1 | new Date(date./* comment */getTime())␊
109+
| ^^^^^^^^^ Unnecessary \`.getTime()\` call.␊
110+
`
402 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.