├── .gitignore
├── .prettierrc
├── .travis.yml
├── LICENSE
├── README.md
├── __tests__
├── index.js
└── performance.js
├── docs
├── .nojekyll
├── README.md
└── index.html
├── lib
├── __tests__
│ └── index.js
├── index.js
└── rules
│ ├── methods.json
│ ├── no-dynamic-import-moment.js
│ ├── no-import-moment.js
│ ├── no-moment-constructor.js
│ ├── no-moment-methods.js
│ └── no-require-moment.js
├── package-lock.json
├── package.json
└── screenshot.png
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 |
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | .vscode
9 |
10 | coverage
11 | yarn.lock
12 |
13 | .history
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "es5"
4 | }
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 12
4 | - 13
5 | - 14
6 | after_success:
7 | - npm run coveralls
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 You-Dont-Need
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [You don't (may not) need Moment.js](https://you-dont-need.github.io/You-Dont-Need-Momentjs/#/)
2 |
3 | [](https://dev.to/t/momentjs)
4 |
5 | [Moment.js](https://momentjs.com/) is a fantastic time & date library with lots of great features and utilities. However, if you are working on a performance sensitive web application, it might cause a huge performance overhead because of its complex APIs and large bundle size.
6 |
7 | 
8 |
9 | Problems with Moment.js:
10 |
11 | - It is highly based on OOP APIs, which makes it fail to work with tree-shaking, thus leading to a huge bundle size and performance issues.
12 | - It is mutable and it causes bugs:
13 | - [clone](https://momentjs.com/docs/#/parsing/moment-clone/)
14 | - [How do I work around mutability in moment.js?](https://stackoverflow.com/questions/30979178/how-do-i-work-around-mutability-in-moment-js)
15 | - Complex OOP API (which doubles mutability problem). Here is an example:
16 | https://github.com/moment/moment/blob/develop/src/test/moment/add_subtract.js#L244-L286
17 | Moment.js allows to use `a.subtract('ms', 50)`, `a.subtract(50, 'ms')` and even `a.subtract('s', '50')`.
18 |
19 | If you are not using timezone but only a few simple functions from moment.js, this might bloat your app, and therefore is considered overkill. [dayjs](https://github.com/iamkun/dayjs) has a smaller core and has very similar APIs so it makes it very easy to migrate. [date-fns](https://github.com/date-fns/date-fns) enables [tree-shaking and other benefits](https://github.com/date-fns/date-fns/issues/275#issuecomment-264934189) so that it works great with React, Sinon.js, and webpack, etc. See https://github.com/moment/moment/issues/2373 for more ideas on why and how people switch from moment.js to other solutions.
20 |
21 | ## Brief Comparison
22 |
23 | | Name | Tree-shaking | Methods richness | Pattern | Locale | Timezone Support | Popularity (stars) | Sizes |
24 | | --------- | ------------ | ---------------- | ---------- | ------ | ---------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
25 | | Moment.js | No | High | OO | 123 | Good (moment-timezone) |  |   |
26 | | Luxon | No | High | OO | - | Good (Intl) |  |   |
27 | | date-fns | Yes | High | Functional | 64 | Good (date-fns-tz) |  |   |
28 | | dayjs | No | High | OO | 138 | Good (Intl) |  |   |
29 |
30 | ## Voice of Developers
31 |
32 | > [Removed moment.js to replace with date-fns - build output reduced by 40%](https://github.com/oysterprotocol/webnode/pull/116)
33 |
34 | > —Jared Farago from [webnode](https://github.com/oysterprotocol/webnode/pull/116) project.
35 |
36 | > [Good library if you’re looking to replace Moment.js for one reason or another. Immutable too.](https://twitter.com/dan_abramov/status/805030922785525760)
37 |
38 | > —Dan Abramov, Author of [Redux](https://github.com/reduxjs/redux) and co-author of [Create React App](https://github.com/facebook/create-react-app). Building tools for humans.
39 |
40 | > [I strongly recommend using date-fns over Moment.js, it's has a nicer API and you can include only parts you need!](https://twitter.com/silvenon/status/804946772690923520)
41 |
42 | > —Matija Marohnić, a design-savvy frontend developer from Croatia.
43 |
44 | ## ESLint Plugin
45 |
46 |
47 |
48 |
50 |
51 |
52 |
54 |
55 |
56 |
58 |
59 |
60 |
62 |
63 |
64 |
65 | If you're using [ESLint](http://eslint.org/), you can install a
66 | [plugin](http://eslint.org/docs/user-guide/configuring#using-the-configuration-from-a-plugin) that
67 | will help you identify places in your codebase where you don't (may not) need Moment.js.
68 |
69 | Install the plugin...
70 |
71 | ```sh
72 | npm install --save-dev eslint-plugin-you-dont-need-momentjs
73 | ```
74 |
75 | ...then update your config
76 |
77 | ```js
78 | "extends" : ["plugin:you-dont-need-momentjs/recommended"],
79 | ```
80 |
81 | ## Quick Links
82 |
83 | **[Parse](#parse)**
84 |
85 | 1. [String + Date Format](#string--date-format)
86 | 1. [String + Time Format](#string--time-format)
87 | 1. [String + Format + locale](#string--format--locale)
88 |
89 | **[Get + Set](#get--set)**
90 |
91 | 1. [Millisecond/Second/Minute/Hour](#millisecond--second--minute--hour)
92 | 1. [Date of Month](#date-of-month)
93 | 1. [Day of Week](#day-of-week)
94 | 1. [Day of Year](#day-of-year)
95 | 1. [Week of Year](#week-of-year)
96 | 1. [Days in Month](#days-in-month)
97 | 1. [Weeks in Year](#weeks-in-year)
98 | 1. [Maximum of the given dates](#maximum-of-the-given-dates)
99 | 1. [Minimum of the given dates](#minimum-of-the-given-dates)
100 |
101 | **[Manipulate](#manipulate)**
102 |
103 | 1. [Add](#add)
104 | 1. [Subtract](#subtract)
105 | 1. [Start of Time](#start-of-time)
106 | 1. [End of Time](#end-of-time)
107 |
108 | **[Display](#display)**
109 |
110 | 1. [Format](#format)
111 | 1. [Time from now](#time-from-now)
112 | 1. [Time from X](#time-from-x)
113 | 1. [Difference](#difference)
114 |
115 | **[Query](#query)**
116 |
117 | 1. [Is Before](#is-before)
118 | 1. [Is Same](#is-same)
119 | 1. [Is After](#is-after)
120 | 1. [Is Between](#is-between)
121 | 1. [Is Leap Year](#is-leap-year)
122 | 1. [Is a Date](#is-a-date)
123 |
124 | # Feature Parity
125 |
126 | ⚠️ Indicates other packages or work are needed. See individual functions [above](#quick-links).
127 |
128 | | | Native | Luxon | date-fns | dayjs | Temporal |
129 | | ------------------------------ | ------ | ----- | -------- | ----- | -------- |
130 | | **Parse** | | | | | |
131 | | String + Date Format | ✅ | ✅ | ✅ | ✅ | ✅ |
132 | | String + Time Format | ✅ | ✅ | ✅ | ⚠️ | ✅ |
133 | | String + Format + locale | ❌ | ⚠️ | ✅ | ⚠️ | ❌ |
134 | | | | | | | |
135 | | **Get + Set** | | | | | |
136 | | Millisecond/Second/Minute/Hour | ✅ | ✅ | ✅ | ✅ | ✅ |
137 | | Date of Month | ✅ | ✅ | ✅ | ✅ | ✅ |
138 | | Day of Week | ✅ | ✅ | ✅ | ✅ | ✅ |
139 | | Day of Year | ✅ | ✅ | ✅ | ✅ | ✅ |
140 | | Week of Year | ✅ | ✅ | ✅ | ⚠️ | ✅ |
141 | | Days in Month | ✅ | ✅ | ✅ | ✅ | ✅ |
142 | | Weeks in Year | ❌ | ❌ | ✅ | ⚠️ | ✅ |
143 | | Maximum of the given dates | ✅ | ✅ | ✅ | ⚠️ | ✅ |
144 | | Minimum of the given dates | ✅ | ✅ | ✅ | ⚠️ | ✅ |
145 | | | | | | | |
146 | | **Manipulate** | | | | | |
147 | | Add | ✅ | ✅ | ✅ | ✅ | ✅ |
148 | | Subtract | ✅ | ✅ | ✅ | ✅ | ✅ |
149 | | Start of Time | ❌ | ✅ | ✅ | ✅ | ✅ |
150 | | End of Time | ✅ | ✅ | ✅ | ✅ | ✅ |
151 | | | | | | | |
152 | | **Display** | | | | | |
153 | | Format | ✅ | ✅ | ✅ | ✅ | ✅ |
154 | | Time from now | ✅ | ❌ | ✅ | ⚠️ | ✅ |
155 | | Time from X | ❌ | ❌ | ✅ | ⚠️ | ✅ |
156 | | Difference | ✅ | ✅ | ✅ | ✅ | ✅ |
157 | | | | | | | |
158 | | **Query** | | | | | |
159 | | Is Before | ✅ | ✅ | ✅ | ✅ | ✅ |
160 | | Is Same | ✅ | ✅ | ✅ | ✅ | ✅ |
161 | | Is After | ✅ | ✅ | ✅ | ✅ | ✅ |
162 | | Is Between | ❌ | ✅ | ✅ | ⚠️ | ❌ |
163 | | Is Leap Year | ✅ | ✅ | ✅ | ⚠️ | ✅ |
164 | | Is a Date | ✅ | ✅ | ✅ | ✅ | ✅ |
165 |
166 | ## Parse
167 |
168 | ### String + Date Format
169 |
170 | Return the date parsed from date string using the given format string.
171 |
172 | ```js
173 | // Moment.js
174 | moment('12-25-1995', 'MM-DD-YYYY');
175 | // => "1995-12-24T13:00:00.000Z"
176 |
177 | // Native
178 | const datePattern = /^(\d{2})-(\d{2})-(\d{4})$/;
179 | const [, month, day, year] = datePattern.exec('12-25-1995');
180 | new Date(`${month}, ${day} ${year}`);
181 | // => "1995-12-24T13:00:00.000Z"
182 |
183 | // date-fns
184 | import parse from 'date-fns/parse';
185 | parse('12-25-1995', 'MM-dd-yyyy', new Date());
186 | // => "1995-12-24T13:00:00.000Z"
187 |
188 | // dayjs
189 | dayjs('12-25-1995');
190 | // => "1995-12-24T13:00:00.000Z"
191 |
192 | // luxon
193 | DateTime.fromFormat('12-25-1995', 'MM-dd-yyyy').toJSDate();
194 | // => "1995-12-24T13:00:00.000Z"
195 |
196 | // Temporal
197 | const datePattern = /^(\d{2})-(\d{2})-(\d{4})$/;
198 | const [, month, day, year] = datePattern.exec('12-25-1995');
199 | new Temporal.ZonedDateTime.from({year, month, day, timeZone: Temporal.Now.timeZone()});
200 | // => "1995-12-24T13:00:00.000Z"
201 | ```
202 |
203 | **[⬆ back to top](#quick-links)**
204 |
205 | ### String + Time Format
206 |
207 | Return the date parsed from time string using the given format string.
208 |
209 | ```js
210 | // Moment.js
211 | moment('2010-10-20 4:30', 'YYYY-MM-DD HH:mm');
212 | // => "2010-10-19T17:30:00.000Z"
213 |
214 | // Native
215 | const datePattern = /^(\d{4})-(\d{2})-(\d{2})\s(\d{1,2}):(\d{2})$/;
216 | const [, year, month, day, rawHour, min] = datePattern.exec('2010-10-20 4:30');
217 | new Date(`${year}-${month}-${day}T${('0' + rawHour).slice(-2)}:${min}:00`);
218 | // => "2010-10-19T17:30:00.000Z"
219 |
220 | // date-fns
221 | import parse from 'date-fns/parse';
222 | parse('2010-10-20 4:30', 'yyyy-MM-dd H:mm', new Date());
223 | // => "2010-10-19T17:30:00.000Z"
224 |
225 | // dayjs ⚠️ requires customParseFormat plugin
226 | import customParseFormat from 'dayjs/plugin/customParseFormat';
227 | dayjs.extend(customParseFormat);
228 | dayjs('2010-10-20 4:30', 'YYYY-MM-DD HH:mm');
229 | // => "2010-10-19T17:30:00.000Z"
230 |
231 | // luxon
232 | DateTime.fromFormat('2010-10-20 4:30', 'yyyy-MM-dd H:mm').toJSDate();
233 | // => "2010-10-19T17:30:00.000Z"
234 |
235 | // Temporal
236 | const datePattern = /^(\d{4})-(\d{2})-(\d{2})\s(\d{1,2}):(\d{2})$/;
237 | const [, year, month, day, hour, minute] = datePattern.exec('2010-10-20 4:30');
238 | new Temporal.ZonedDateTime.from({year, month, day, hour, minute, timeZone: Temporal.Now.timeZone()});
239 | // => "2010-10-19T17:30:00.000Z"
240 | ```
241 |
242 | **[⬆ back to top](#quick-links)**
243 |
244 | ### String + Format + locale
245 |
246 | Return the date parsed from string using the given format string and locale.
247 |
248 | ```js
249 | // Moment.js
250 | moment('2012 mars', 'YYYY MMM', 'fr');
251 | // => "2012-02-29T13:00:00.000Z"
252 |
253 | // date-fns
254 | import parse from 'date-fns/parse';
255 | import fr from 'date-fns/locale/fr';
256 | parse('2012 mars', 'yyyy MMMM', new Date(), { locale: fr });
257 | // => "2012-02-29T13:00:00.000Z"
258 |
259 | // dayjs ⚠️ requires customParseFormat plugin
260 | import customParseFormat from 'dayjs/plugin/customParseFormat';
261 | import 'dayjs/locale/fr';
262 | dayjs.extend(customParseFormat);
263 | dayjs('2012 mars', 'YYYY MMM', 'fr');
264 | // => "2012-02-29T13:00:00.000Z"
265 |
266 | // Luxon ❌ does not support Locale for node unless https://moment.github.io/luxon/docs/manual/install.html#node
267 | DateTime.fromFormat('2012 mars', 'yyyy MMMM', { locale: 'fr' });
268 | // => "2012-02-29T13:00:00.000Z"
269 | ```
270 |
271 | **[⬆ back to top](#quick-links)**
272 |
273 | ## Get + Set
274 |
275 | ### Millisecond / Second / Minute / Hour
276 |
277 | Get the `Millisecond/Second/Minute/Hour` of the given date.
278 |
279 | ```js
280 | // Moment.js
281 | moment().seconds();
282 | // => 49
283 | moment().hours();
284 | // => 19
285 |
286 | // Native
287 | new Date().getSeconds();
288 | // => 49
289 | new Date().getHours();
290 | // => 19
291 |
292 | // date-fns
293 | import getSeconds from 'date-fns/getSeconds';
294 | import getHours from 'date-fns/getHours';
295 | getSeconds(new Date());
296 | // => 49
297 | getHours(new Date());
298 | // => 19
299 |
300 | // dayjs
301 | dayjs().second();
302 | // => 49
303 | dayjs().hour();
304 | // => 19
305 |
306 | // Luxon
307 | DateTime.local().second;
308 | // => 49
309 | DateTime.local().hour;
310 | // => 19
311 |
312 | // Temporal
313 | Temporal.Now.zonedDateTimeISO().second;
314 | // => 49
315 | Temporal.Now.zonedDateTimeISO().hour;
316 | // => 19
317 | ```
318 |
319 | ### Performance tests
320 |
321 | | Library | Time |
322 | | -------- | ---------- |
323 | | Moment | 1500.703ms |
324 | | Native | 348.411ms |
325 | | DateFns | 520.670ms |
326 | | DayJs | 494.234ms |
327 | | Luxon | 1208.368ms |
328 | | Temporal | - |
329 |
330 | Set the `Millisecond/Second/Minute/Hour` of the given date.
331 |
332 | ```js
333 | // Moment.js
334 | moment().seconds(30);
335 | // => "2018-09-09T09:12:30.695Z"
336 | moment().hours(13);
337 | // => "2018-09-09T03:12:49.695Z"
338 |
339 | // Native
340 | new Date(new Date().setSeconds(30));
341 | // => "2018-09-09T09:12:30.695Z"
342 | new Date(new Date().setHours(13));
343 | // => "2018-09-09T03:12:49.695Z"
344 |
345 | // date-fns
346 | import setSeconds from 'date-fns/setSeconds';
347 | import setHours from 'date-fns/setHours';
348 | setSeconds(new Date(), 30);
349 | // => "2018-09-09T09:12:30.695Z"
350 | setHours(new Date(), 13);
351 | // => "2018-09-09T03:12:49.695Z"
352 |
353 | // dayjs
354 | dayjs().set('second', 30);
355 | // => "2018-09-09T09:12:30.695Z"
356 | dayjs().set('hour', 13);
357 | // => "2018-09-09T03:12:49.695Z"
358 |
359 | // luxon
360 | DateTime.utc()
361 | .set({ second: 30 })
362 | .toJSDate();
363 | // => "2018-09-09T09:12:30.695Z"
364 | DateTime.utc()
365 | .set({ hour: 13 })
366 | .toJSDate();
367 | // => "2018-09-09T03:12:49.695Z"
368 |
369 | // Temporal
370 | Temporal.Now.zonedDateTimeISO().with({ second: 30 });
371 | // => "2018-09-09T09:12:30.695Z"
372 | Temporal.Now.zonedDateTimeISO().with({ hour: 13 });
373 | // => "2018-09-09T03:12:49.695Z"
374 | ```
375 |
376 | ### Performance tests
377 |
378 | | Library | Time |
379 | | -------- | ---------- |
380 | | Moment | 1689.744ms |
381 | | Native | 636.741ms |
382 | | DateFns | 714.148ms |
383 | | DayJs | 2037.603ms |
384 | | Luxon | 2897.571ms |
385 | | Temporal | - |
386 |
387 | **[⬆ back to top](#quick-links)**
388 |
389 | ### Date of Month
390 |
391 | Gets or sets the day of the month.
392 |
393 | ```js
394 | // Moment.js
395 | moment().date();
396 | // => 9
397 | moment().date(4);
398 | // => "2018-09-04T09:12:49.695Z"
399 |
400 | // Native
401 | new Date().getDate();
402 | // => 9
403 | new Date().setDate(4);
404 | // => "2018-09-04T09:12:49.695Z"
405 |
406 | // date-fns
407 | import getDate from 'date-fns/getDate';
408 | import setDate from 'date-fns/setDate';
409 | getDate(new Date());
410 | // => 9
411 | setDate(new Date(), 4);
412 | // => "2018-09-04T09:12:49.695Z"
413 |
414 | // dayjs
415 | dayjs().date();
416 | // => 9
417 | dayjs().set('date', 4);
418 | // => "2018-09-04T09:12:49.695Z"
419 |
420 | // luxon
421 | DateTime.utc().day;
422 | // => 9
423 | DateTime.utc()
424 | .set({ day: 4 })
425 | .toString();
426 | // => "2018-09-04T09:12:49.695Z"
427 |
428 | // Temporal
429 | Temporal.Now.zonedDateTimeISO().day;
430 | // => 9
431 | Temporal.Now.zonedDateTimeISO().with({ day: 4 });
432 | // => "2018-09-04T09:12:49.695Z"
433 | ```
434 |
435 | ### Performance tests
436 |
437 | | Library | Time |
438 | | -------- | ---------- |
439 | | Moment | 1381.669ms |
440 | | Native | 397.415ms |
441 | | DateFns | 588.004ms |
442 | | DayJs | 1218.025ms |
443 | | Luxon | 2705.606ms |
444 | | Temporal | - |
445 |
446 | **[⬆ back to top](#quick-links)**
447 |
448 | ### Day of Week
449 |
450 | Gets or sets the day of the week.
451 |
452 | ```js
453 | // Moment.js
454 | moment().day();
455 | // => 0 (Sunday)
456 | moment().day(-14);
457 | // => "2018-08-26T09:12:49.695Z"
458 |
459 | // Native
460 | new Date().getDay();
461 | // => 0 (Sunday)
462 | new Date().setDate(new Date().getDate() - 14);
463 | // => "2018-08-26T09:12:49.695Z"
464 |
465 | // date-fns
466 | import getDay from 'date-fns/getDay';
467 | import setDay from 'date-fns/setDay';
468 | getDay(new Date());
469 | // => 0 (Sunday)
470 | setDay(new Date(), -14);
471 | // => "2018-08-26T09:12:49.695Z"
472 |
473 | // dayjs
474 | dayjs().day();
475 | // => 0 (Sunday)
476 | dayjs().set('day', -14);
477 | // => "2018-08-26T09:12:49.695Z"
478 |
479 | // Luxon
480 | DateTime.local().weekday;
481 | // => 7 (Sunday)
482 | DateTime.local()
483 | .minus({ day: 14 })
484 | .toJSDate();
485 | // => "2018-08-26T09:12:49.695Z"
486 |
487 | // Temporal
488 | Temporal.Now.zonedDateTimeISO().dayOfWeek;
489 | // => 7 (Sunday)
490 | Temporal.Now.zonedDateTimeISO().subtract(Temporal.Duration.from({ days: 14 }));
491 | // => "2018-09-04T09:12:49.695Z"
492 | ```
493 |
494 | | Library | Time |
495 | | -------- | ---------- |
496 | | Moment | 1919.404ms |
497 | | Native | 543.466ms |
498 | | DateFns | 841.436ms |
499 | | DayJs | 1229.475ms |
500 | | Luxon | 3936.282ms |
501 | | Temporal | - |
502 |
503 | **[⬆ back to top](#quick-links)**
504 |
505 | ### Day of Year
506 |
507 | Gets or sets the day of the year.
508 |
509 | ```js
510 | // Moment.js
511 | moment().dayOfYear();
512 | // => 252
513 | moment().dayOfYear(256);
514 | // => "2018-09-13T09:12:49.695Z"
515 |
516 | // Native
517 | Math.floor(
518 | (new Date() - new Date(new Date().getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24
519 | );
520 | // => 252
521 |
522 | // date-fns
523 | import getDayOfYear from 'date-fns/getDayOfYear';
524 | import setDayOfYear from 'date-fns/setDayOfYear';
525 | getDayOfYear(new Date());
526 | // => 252
527 | setDayOfYear(new Date(), 256);
528 | // => "2018-09-13T09:12:49.695Z"
529 |
530 | // dayjs ⚠️ requires dayOfYear plugin
531 | import dayOfYear from 'dayjs/plugin/dayOfYear';
532 | dayjs.extend(dayOfYear);
533 | dayjs().dayOfYear();
534 | // => 252
535 | dayjs().dayOfYear(256);
536 | // => "2018-09-13T09:12:49.695Z"
537 |
538 | // Luxon
539 | DateTime.local().ordinal;
540 | // => 252
541 | DateTime.local()
542 | .set({ ordinal: 256 })
543 | .toString();
544 | // => "2018-09-13T09:12:49.695Z"
545 |
546 | // Temporal
547 | Temporal.Now.zonedDateTimeISO().dayOfYear;
548 | // => 252
549 | Temporal.Now.zonedDateTimeISO().with({month: 1, day: 1}).add(Temporal.Duration.from({days: 256}));
550 | // => "2018-09-04T09:12:49.695Z"
551 | ```
552 |
553 | | Library | Time |
554 | | -------- | ---------- |
555 | | Moment | 5511.172ms |
556 | | Native | 530.592ms |
557 | | DateFns | 2079.043ms |
558 | | DayJs | - |
559 | | Luxon | 3540.810ms |
560 | | Temporal | - |
561 |
562 | **[⬆ back to top](#quick-links)**
563 |
564 | ### Week of Year
565 |
566 | Gets or sets the week of the year.
567 |
568 | ```js
569 | // Moment.js
570 | moment().week();
571 | // => 37
572 | moment().week(24);
573 | // => "2018-06-10T09:12:49.695Z"
574 |
575 | // date-fns
576 | import getWeek from 'date-fns/getWeek';
577 | import setWeek from 'date-fns/setWeek';
578 | getWeek(new Date());
579 | // => 37
580 | setWeek(new Date(), 24);
581 | // => "2018-06-10T09:12:49.695Z"
582 |
583 | // native getWeek
584 | const day = new Date();
585 | const MILLISECONDS_IN_WEEK = 604800000;
586 | const firstDayOfWeek = 1; // monday as the first day (0 = sunday)
587 | const startOfYear = new Date(day.getFullYear(), 0, 1);
588 | startOfYear.setDate(
589 | startOfYear.getDate() + (firstDayOfWeek - (startOfYear.getDay() % 7))
590 | );
591 | const dayWeek = Math.round((day - startOfYear) / MILLISECONDS_IN_WEEK) + 1;
592 | // => 37
593 |
594 | // native setWeek
595 | const day = new Date();
596 | const week = 24;
597 | const MILLISECONDS_IN_WEEK = 604800000;
598 | const firstDayOfWeek = 1; // monday as the first day (0 = sunday)
599 | const startOfYear = new Date(day.getFullYear(), 0, 1);
600 | startOfYear.setDate(
601 | startOfYear.getDate() + (firstDayOfWeek - (startOfYear.getDay() % 7))
602 | );
603 | const dayWeek = Math.round((day - startOfYear) / MILLISECONDS_IN_WEEK) + 1;
604 | day.setDate(day.getDate() - (dayWeek - week) * 7);
605 | day.toISOString();
606 | // => "2018-06-10T09:12:49.794Z
607 |
608 | // dayjs ⚠️ requires weekOfYear plugin
609 | import weekOfYear from 'dayjs/plugin/weekOfYear';
610 | dayjs.extend(weekOfYear);
611 | dayjs().week();
612 | // => 37
613 | dayjs().week(24);
614 | // => "2018-06-10T09:12:49.695Z"
615 |
616 | // Luxon
617 | DateTime.local().weekNumber;
618 | // => 37
619 | DateTime.local()
620 | .set({ weekNumber: 23 })
621 | .toString();
622 | // => "2018-06-10T09:12:49.794Z
623 |
624 | // Temporal
625 | Temporal.Now.zonedDateTimeISO().weekOfYear;
626 | // => 252
627 | Temporal.Now.zonedDateTimeISO().with({month: 1, day: 1}).add(Temporal.Duration.from({weeks: 23}));
628 | // => "2018-09-04T09:12:49.695Z"
629 | ```
630 |
631 | | Library | Time |
632 | | -------- | ---------- |
633 | | Moment | 7147.201ms |
634 | | Native | 1371.631ms |
635 | | DateFns | 5834.815ms |
636 | | DayJs | - |
637 | | Luxon | 4514.771ms |
638 | | Temporal | - |
639 |
640 | **[⬆ back to top](#quick-links)**
641 |
642 | ### Days in Month
643 |
644 | Get the number of days in the current month.
645 |
646 | ```js
647 | // Moment.js
648 | moment('2012-02', 'YYYY-MM').daysInMonth();
649 | // => 29
650 |
651 | // Native
652 | new Date(2012, 02, 0).getDate();
653 | // => 29
654 |
655 | // date-fns
656 | import getDaysInMonth from 'date-fns/getDaysInMonth';
657 | getDaysInMonth(new Date(2012, 1));
658 | // => 29
659 |
660 | // dayjs
661 | dayjs('2012-02').daysInMonth();
662 | // => 29
663 |
664 | // Luxon
665 | DateTime.local(2012, 2).daysInMonth;
666 | // => 29
667 |
668 | // Temporal
669 | (new Temporal.PlainYearMonth(2012, 2)).daysInMonth
670 | // or
671 | Temporal.PlainYearMonth.from('2012-02').daysInMonth
672 | // => 29
673 | ```
674 |
675 | | Library | Time |
676 | | -------- | ---------- |
677 | | Moment | 4415.065ms |
678 | | Native | 186.196ms |
679 | | DateFns | 634.084ms |
680 | | DayJs | 1922.774ms |
681 | | Luxon | 1403.032ms |
682 | | Temporal | - |
683 |
684 | **[⬆ back to top](#quick-links)**
685 |
686 | ### Weeks in Year
687 |
688 | Gets the number of weeks in the current year, according to ISO weeks.
689 |
690 | ```js
691 | // Moment.js
692 | moment().isoWeeksInYear();
693 | // => 52
694 |
695 | // Native
696 | const year = new Date().getFullYear();
697 | const MILLISECONDS_IN_WEEK = 604800000;
698 | const firstMondayThisYear = new Date(+year, 0, 5-(new Date(+year, 0, 4).getDay()||7));
699 | const firstMondayNextYear = new Date(+year+1, 0, 5-(new Date(+year+1, 0, 4).getDay()||7));
700 | (firstMondayNextYear - firstMondayThisYear) / MILLISECONDS_IN_WEEK;
701 | // => 52
702 |
703 | // date-fns
704 | import getISOWeeksInYear from 'date-fns/getISOWeeksInYear';
705 | getISOWeeksInYear(new Date());
706 | // => 52
707 |
708 | // dayjs ⚠️ requires isoWeeksInYear plugin
709 | import isoWeeksInYear from 'dayjs/plugin/isoWeeksInYear';
710 | dayjs.extend(isoWeeksInYear);
711 | dayjs().isoWeeksInYear();
712 | // => 52
713 |
714 | // Luxon
715 | DateTime.local().weeksInWeekYear;
716 | // => 52
717 |
718 | // Temporal
719 | Temporal.PlainDate.from({day:31, month:12, year: Temporal.Now.plainDateISO()}).weekOfYear
720 | // => 52
721 | ```
722 |
723 | | Library | Time |
724 | | -------- | ---------- |
725 | | Moment | 1065.247ms |
726 | | Native | - |
727 | | DateFns | 4954.042ms |
728 | | DayJs | - |
729 | | Luxon | 1134.483ms |
730 | | Temporal | - |
731 |
732 | **[⬆ back to top](#quick-links)**
733 |
734 | ### Maximum of the given dates
735 |
736 | Returns the maximum (most distant future) of the given date.
737 |
738 | ```js
739 | const array = [
740 | new Date(2017, 4, 13),
741 | new Date(2018, 2, 12),
742 | new Date(2016, 0, 10),
743 | new Date(2016, 0, 9),
744 | ];
745 | // Moment.js
746 | moment.max(array.map(a => moment(a)));
747 | // => "2018-03-11T13:00:00.000Z"
748 |
749 | // Native
750 | new Date(Math.max.apply(null, array)).toISOString();
751 | // => "2018-03-11T13:00:00.000Z"
752 |
753 | // date-fns
754 | import max from 'date-fns/max';
755 | max(array);
756 | // => "2018-03-11T13:00:00.000Z"
757 |
758 | // dayjs ⚠️ requires minMax plugin
759 | import minMax from 'dayjs/plugin/minMax';
760 | dayjs.extend(minMax);
761 | dayjs.max(array.map(a => dayjs(a)));
762 | // => "2018-03-11T13:00:00.000Z"
763 |
764 | // Luxon
765 | DateTime.max(...array.map(a => DateTime.fromJSDate(a))).toJSDate();
766 | // => "2018-03-11T13:00:00.000Z"
767 |
768 | // Temporal
769 | Temporal.Instant.fromEpochMilliseconds(Math.max.apply(null, array))
770 | // => "2018-03-11T13:00:00.000Z"
771 | ```
772 |
773 | | Library | Time |
774 | | -------- | ---------- |
775 | | Moment | 1780.075ms |
776 | | Native | 828.332ms |
777 | | DateFns | 980.938ms |
778 | | DayJs | - |
779 | | Luxon | 2694.702ms |
780 | | Temporal | - |
781 |
782 | **[⬆ back to top](#quick-links)**
783 |
784 | ### Minimum of the given dates
785 |
786 | Returns the minimum (most distant future) of the given date.
787 |
788 | ```js
789 | const array = [
790 | new Date(2017, 4, 13),
791 | new Date(2018, 2, 12),
792 | new Date(2016, 0, 10),
793 | new Date(2016, 0, 9),
794 | ];
795 | // Moment.js
796 | moment.min(array.map(a => moment(a)));
797 | // => "2016-01-08T13:00:00.000Z"
798 |
799 | // Native
800 | new Date(Math.min.apply(null, array)).toISOString();
801 | // => "2016-01-08T13:00:00.000Z"
802 |
803 | // date-fns
804 | import min from 'date-fns/min';
805 | min(array);
806 | // => "2016-01-08T13:00:00.000Z"
807 |
808 | // dayjs ⚠️ requires minMax plugin
809 | import minMax from 'dayjs/plugin/minMax';
810 | dayjs.extend(minMax);
811 | dayjs.min(array.map(a => dayjs(a)));
812 | // => "2016-01-08T13:00:00.000Z"
813 |
814 | // Luxon
815 | DateTime.min(...array.map(a => DateTime.fromJSDate(a))).toJSDate();
816 | // => "2016-01-08T13:00:00.000Z"
817 |
818 | // Temporal
819 | Temporal.Instant.fromEpochMilliseconds(Math.min.apply(null, array))
820 | // => "2018-03-11T13:00:00.000Z"
821 | ```
822 |
823 | | Library | Time |
824 | | -------- | ---------- |
825 | | Moment | 1744.459ms |
826 | | Native | 819.646ms |
827 | | DateFns | 841.249ms |
828 | | DayJs | - |
829 | | Luxon | 2720.462ms |
830 | | Temporal | - |
831 |
832 | **[⬆ back to top](#quick-links)**
833 |
834 | ## Manipulate
835 |
836 | ### Add
837 |
838 | Add the specified number of days to the given date.
839 |
840 | ```js
841 | // Moment.js
842 | moment().add(7, 'days');
843 | // => "2018-09-16T09:12:49.695Z"
844 |
845 | // Native
846 | const now = new Date();
847 | now.setDate(now.getDate() + 7);
848 | // => "Sun Sep 16 2018 09:12:49"
849 |
850 | // date-fns
851 | import addDays from 'date-fns/addDays';
852 | addDays(new Date(), 7);
853 | // => "2018-09-16T09:12:49.695Z"
854 |
855 | // dayjs
856 | dayjs().add(7, 'day');
857 | // => "2018-09-16T09:12:49.695Z"
858 |
859 | // Luxon
860 | DateTime.local()
861 | .plus({ day: 7 })
862 | .toJSDate();
863 | // => "2018-09-16T09:12:49.695Z"
864 |
865 | // Temporal
866 | Temporal.Now.zonedDateTimeISO().add(Temporal.Duration.from({days: 7}));
867 | // => "2018-09-16T09:12:49.695Z"
868 | ```
869 |
870 | | Library | Time |
871 | | -------- | ---------- |
872 | | Moment | 1309.485ms |
873 | | Native | 259.932ms |
874 | | DateFns | 385.394ms |
875 | | DayJs | 1911.881ms |
876 | | Luxon | 3919.797ms |
877 | | Temporal | - |
878 |
879 | **[⬆ back to top](#quick-links)**
880 |
881 | ### Subtract
882 |
883 | Subtract the specified number of days from the given date.
884 |
885 | ```js
886 | // Moment.js
887 | moment().subtract(7, 'days');
888 | // => "2018-09-02T09:12:49.695Z"
889 |
890 | // Native
891 | const now = new Date();
892 | now.setDate(now.getDate() - 7);
893 | // => Sun Sep 09 2018 09:12:49
894 |
895 | // date-fns
896 | import subDays from 'date-fns/subDays';
897 | subDays(new Date(), 7);
898 | // => "2018-09-02T09:12:49.695Z"
899 |
900 | // dayjs
901 | dayjs().subtract(7, 'day');
902 | // => "2018-09-02T09:12:49.695Z"
903 |
904 | // Luxon
905 | DateTime.local()
906 | .minus({ day: 7 })
907 | .toJSDate();
908 | // => "2018-09-02T09:12:49.695Z"
909 |
910 | // Temporal
911 | Temporal.Now.zonedDateTimeISO().subtract(Temporal.Duration.from({days: 7}));
912 | // => "2018-09-02T09:12:49.695Z"
913 | ```
914 |
915 | | Library | Time |
916 | | -------- | ---------- |
917 | | Moment | 1278.384ms |
918 | | Native | 215.255ms |
919 | | DateFns | 379.057ms |
920 | | DayJs | 1772.593ms |
921 | | Luxon | 4028.866ms |
922 | | Temporal | - |
923 |
924 | **[⬆ back to top](#quick-links)**
925 |
926 | ### Start of Time
927 |
928 | Return the start of a unit of time for the given date.
929 |
930 | ```js
931 | // Moment.js
932 | moment().startOf('month');
933 | // => "2018-08-31T14:00:00.000Z"
934 |
935 | // date-fns
936 | import startOfMonth from 'date-fns/startOfMonth';
937 | startOfMonth(new Date());
938 | // => "2018-08-31T14:00:00.000Z"
939 |
940 | // dayjs
941 | dayjs().startOf('month');
942 | // => "2018-08-31T14:00:00.000Z"
943 |
944 | // Luxon
945 | DateTime.local().startOf('month');
946 | // => "2018-09-02T09:12:49.695Z"
947 |
948 | // Temporal
949 | Temporal.Now.zonedDateTimeISO().with({day: 1});
950 | // => "2018-09-01T14:00:00.000Z"
951 | ```
952 |
953 | | Library | Time |
954 | | -------- | ---------- |
955 | | Moment | 1078.948ms |
956 | | Native | - |
957 | | DateFns | 398.107ms |
958 | | DayJs | 765.358ms |
959 | | Luxon | 2306.765ms |
960 | | Temporal | - |
961 |
962 | **[⬆ back to top](#quick-links)**
963 |
964 | ### End of Time
965 |
966 | Return the end of a unit of time for the given date.
967 |
968 | ```js
969 | // Moment.js
970 | moment().endOf('day');
971 | // => "2018-09-09T13:59:59.999Z"
972 |
973 | // Native
974 | const end = new Date();
975 | end.setHours(23, 59, 59, 999);
976 | end.toISOString();
977 | // => "2018-09-09T16:59:59.999Z"
978 |
979 | // date-fns
980 | import endOfDay from 'date-fns/endOfDay';
981 | endOfDay(new Date());
982 | // => "2018-09-09T13:59:59.999Z"
983 |
984 | // dayjs
985 | dayjs().endOf('day');
986 | // => "2018-09-09T13:59:59.999Z"
987 |
988 | // Luxon
989 | DateTime.local().endOf('day');
990 | // => "2018-09-02T09:12:49.695Z"
991 |
992 | // Temporal
993 | Temporal.Now.zonedDateTimeISO().withPlainTime(new Temporal.PlainTime(23,59,59,999,999,999));
994 | // => "2018-09-09T16:59:59.999999999Z"
995 | ```
996 |
997 | | Library | Time |
998 | | -------- | ---------- |
999 | | Moment | 1241.304ms |
1000 | | Native | 225.519ms |
1001 | | DateFns | 319.773ms |
1002 | | DayJs | 914.425ms |
1003 | | Luxon | 9920.529ms |
1004 | | Temporal | - |
1005 |
1006 | **[⬆ back to top](#quick-links)**
1007 |
1008 | ## Display
1009 |
1010 | ### Format
1011 |
1012 | Return the formatted date string in the given format.
1013 |
1014 | ```js
1015 | // Moment.js
1016 | moment().format('dddd, MMMM Do YYYY, h:mm:ss A');
1017 | // => "Sunday, September 9th 2018, 7:12:49 PM"
1018 | moment().format('ddd, hA');
1019 | // => "Sun, 7PM"
1020 |
1021 | // Native
1022 | new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'medium' }).format(new Date())
1023 | // => "Sunday, September 9, 2018 at 7:12:49 PM"
1024 | new Intl.DateTimeFormat('en-US', { weekday: 'short', hour: 'numeric' }).format(new Date())
1025 | // => "Sun, 7 PM"
1026 |
1027 | // date-fns
1028 | import { intlFormat } from 'date-fns'
1029 | intlFormat(new Date(), { dateStyle: 'full', timeStyle: 'medium' }, { locale: 'en-US', })
1030 | // => "Sunday, September 9, 2018 at 7:12:49 PM"
1031 | intlFormat(new Date(), { weekday: 'short', hour: 'numeric' }, { locale: 'en-US', })
1032 | // => "Sun, 7 PM"
1033 |
1034 | // dayjs
1035 | dayjs().format('dddd, MMMM D YYYY, h:mm:ss A');
1036 | // => "Sunday, September 9 2018, 7:12:49 PM"
1037 | dayjs().format('ddd, hA');
1038 | // => "Sun, 7PM"
1039 | // dayjs ⚠️ requires advancedFormat plugin to support more format tokens
1040 | import advancedFormat from 'dayjs/plugin/advancedFormat';
1041 | dayjs.extend(advancedFormat);
1042 | dayjs().format('dddd, MMMM Do YYYY, h:mm:ss A');
1043 | // => "Sunday, September 9th 2018, 7:12:49 PM"
1044 |
1045 | // Luxon
1046 | DateTime.fromMillis(time).toFormat('EEEE, MMMM dd yyyy, h:mm:ss a');
1047 | // => "Sunday, September 9 2018, 7:12:49 PM" ⚠️ not support 9th
1048 | DateTime.fromMillis(time).toFormat('EEE, ha');
1049 | // => "Sun, 7PM"
1050 |
1051 | // Temporal
1052 | new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'medium' }).format(Temporal.Now.zonedDateTimeISO())
1053 | // => "Sunday, September 9, 2018 at 7:12:49 PM"
1054 | new Intl.DateTimeFormat('en-US', { weekday: 'short', hour: 'numeric' }).format(Temporal.Now.zonedDateTimeISO())
1055 | // => "Sun, 7 PM"
1056 | ```
1057 |
1058 | **[⬆ back to top](#quick-links)**
1059 |
1060 | ### Time from now
1061 |
1062 | Return time from now.
1063 |
1064 | ```js
1065 | // Moment.js
1066 | moment(1536484369695).fromNow();
1067 | // => "4 days ago"
1068 |
1069 | // Native
1070 | new Intl.RelativeTimeFormat().format(-4, 'day');
1071 | // => "4 days ago"
1072 |
1073 | // date-fns
1074 | import formatDistance from 'date-fns/formatDistance';
1075 | formatDistance(new Date(1536484369695), new Date(), { addSuffix: true });
1076 | // => "4 days ago"
1077 |
1078 | // dayjs ⚠️ requires relativeTime plugin
1079 | import relativeTime from 'dayjs/plugin/relativeTime';
1080 | dayjs.extend(relativeTime);
1081 |
1082 | dayjs(1536484369695).fromNow();
1083 | // => "5 days ago" ⚠️ the rounding method of this plugin is different from moment.js and date-fns, use with care.
1084 |
1085 | // luxon requires Intl.RelativeTimeFormat
1086 | DateTime.local(2022, 1, 27).toRelative({ base: this })
1087 | // => "in 4 months"
1088 |
1089 | // Temporal
1090 | new Intl.RelativeTimeFormat().format(-4, 'day');
1091 | // => "4 days ago"
1092 | ```
1093 |
1094 | **[⬆ back to top](#quick-links)**
1095 |
1096 | ### Time from x
1097 |
1098 | Return time from x.
1099 |
1100 | ```js
1101 | // Moment.js
1102 | moment([2007, 0, 27]).to(moment([2007, 0, 29]));
1103 | // => "in 2 days"
1104 |
1105 | // date-fns
1106 | import formatDistance from 'date-fns/formatDistance';
1107 | formatDistance(new Date(2007, 0, 27), new Date(2007, 0, 29));
1108 | // => "2 days"
1109 |
1110 | // dayjs ⚠️ requires relativeTime plugin
1111 | import relativeTime from 'dayjs/plugin/relativeTime';
1112 | dayjs.extend(relativeTime);
1113 | dayjs('2007-01-27').to(dayjs('2007-01-29'));
1114 | // => "in 2 days"
1115 |
1116 | // luxon ❌ does not support relative time
1117 |
1118 | // Temporal
1119 | Temporal.PlainDate.from('2007-01-27').until('2007-01-29');
1120 | // => Temporal.Duration('P2D')
1121 | ```
1122 |
1123 | **[⬆ back to top](#quick-links)**
1124 |
1125 | ### Difference
1126 |
1127 | Get the unit of time between the given dates.
1128 |
1129 | ```js
1130 | // Moment.js
1131 | moment([2007, 0, 27]).diff(moment([2007, 0, 29]));
1132 | // => -172800000
1133 | moment([2007, 0, 27]).diff(moment([2007, 0, 29]), 'days');
1134 | // => -2
1135 |
1136 | // Native
1137 | new Date(2007, 0, 27) - new Date(2007, 0, 29);
1138 | // => -172800000
1139 | Math.ceil(
1140 | (new Date(2007, 0, 27) - new Date(2007, 0, 29)) / 1000 / 60 / 60 / 24
1141 | );
1142 | // => -2
1143 |
1144 | // date-fns
1145 | import differenceInMilliseconds from 'date-fns/differenceInMilliseconds';
1146 | differenceInMilliseconds(new Date(2007, 0, 27), new Date(2007, 0, 29));
1147 | // => -172800000
1148 | import differenceInDays from 'date-fns/differenceInDays';
1149 | differenceInDays(new Date(2007, 0, 27), new Date(2007, 0, 29));
1150 | // => -2
1151 |
1152 | // dayjs
1153 | dayjs('2007-01-27').diff(dayjs('2007-01-29'), 'milliseconds');
1154 | // => -172800000
1155 | dayjs('2007-01-27').diff(dayjs('2007-01-29'), 'days');
1156 | // => -2
1157 |
1158 | // luxon
1159 | DateTime.local(2007, 1, 27).diff(DateTime.local(2007, 1, 29)).milliseconds;
1160 | // => -172800000
1161 | DateTime.local(2007, 1, 27).diff(DateTime.local(2007, 1, 29), 'days').days;
1162 | // => -2
1163 |
1164 | // Temporal
1165 | Temporal.PlainDate.from('2007-01-27').since('2007-01-29').total({unit: 'millisecond'});
1166 | // => -172800000
1167 | Temporal.PlainDate.from('2007-01-27').since('2007-01-29').total({unit: 'day'});
1168 | // => -2
1169 | ```
1170 |
1171 | **[⬆ back to top](#quick-links)**
1172 |
1173 | ## Query
1174 |
1175 | ### Is Before
1176 |
1177 | Check if a date is before another date.
1178 |
1179 | ```js
1180 | // Moment.js
1181 | moment('2010-10-20').isBefore('2010-10-21');
1182 | // => true
1183 |
1184 | // Native
1185 | new Date(2010, 10, 20) < new Date(2010, 10, 21);
1186 | // => true
1187 |
1188 | // date-fns
1189 | import isBefore from 'date-fns/isBefore';
1190 | isBefore(new Date(2010, 9, 20), new Date(2010, 9, 21));
1191 | // => true
1192 |
1193 | // dayjs
1194 | dayjs('2010-10-20').isBefore('2010-10-21');
1195 | // => true
1196 |
1197 | // luxon
1198 | DateTime.fromISO('2010-10-20') < DateTime.fromISO('2010-10-21');
1199 | // => true
1200 |
1201 | // Temporal
1202 | Temporal.PlainDate.compare('2010-10-20', '2010-10-21') === -1;
1203 | // => true
1204 | ```
1205 |
1206 | **[⬆ back to top](#quick-links)**
1207 |
1208 | ### Is Same
1209 |
1210 | Check if a date is the same as another date.
1211 |
1212 | ```js
1213 | // Moment.js
1214 | moment('2010-10-20').isSame('2010-10-21');
1215 | // => false
1216 | moment('2010-10-20').isSame('2010-10-20');
1217 | // => true
1218 | moment('2010-10-20').isSame('2010-10-21', 'month');
1219 | // => true
1220 |
1221 | // Native
1222 | new Date(2010, 9, 20).valueOf() === new Date(2010, 9, 21).valueOf();
1223 | // => false
1224 | new Date(2010, 9, 20).valueOf() === new Date(2010, 9, 20).valueOf();
1225 | // => true
1226 | new Date(2010, 9, 20).getTime() === new Date(2010, 9, 20).getTime();
1227 | // => true
1228 | new Date(2010, 9, 20).valueOf() === new Date(2010, 9, 20).getTime();
1229 | // => true
1230 | new Date(2010, 9, 20).toDateString().substring(4, 7) ===
1231 | new Date(2010, 9, 21).toDateString().substring(4, 7);
1232 | // => true
1233 |
1234 | // date-fns
1235 | import isSameDay from 'date-fns/isSameDay';
1236 | import isSameMonth from 'date-fns/isSameMonth';
1237 | isSameDay(new Date(2010, 9, 20), new Date(2010, 9, 21));
1238 | // => false
1239 | isSameDay(new Date(2010, 9, 20), new Date(2010, 9, 20));
1240 | // => true
1241 | isSameMonth(new Date(2010, 9, 20), new Date(2010, 9, 21));
1242 | // => true
1243 |
1244 | // dayjs
1245 | dayjs('2010-10-20').isSame('2010-10-21');
1246 | // => false
1247 | dayjs('2010-10-20').isSame('2010-10-20');
1248 | // => true
1249 | dayjs('2010-10-20').isSame('2010-10-21', 'month');
1250 | // => true
1251 |
1252 | // luxon
1253 | (+DateTime.fromISO('2010-10-20') ===
1254 | +DateTime.fromISO('2010-10-21') +
1255 | // => false
1256 | DateTime.fromISO('2010-10-20')) ===
1257 | +DateTime.fromISO('2010-10-20');
1258 | // => true
1259 | DateTime.fromISO('2010-10-20').hasSame(DateTime.fromISO('2010-10-21'), 'month');
1260 | // => true
1261 |
1262 | // Temporal
1263 | Temporal.PlainDate.from('2010-10-20').equals('2010-10-21');
1264 | // => false
1265 | Temporal.PlainDate.from('2010-10-20').equals('2010-10-20');
1266 | // => true
1267 | Temporal.PlainDate.from('2010-10-20').month === Temporal.PlainDate.from('2010-10-21').month;
1268 | // => true
1269 | ```
1270 |
1271 | **[⬆ back to top](#quick-links)**
1272 |
1273 | ### Is After
1274 |
1275 | Check if a date is after another date.
1276 |
1277 | ```js
1278 | // Moment.js
1279 | moment('2010-10-20').isAfter('2010-10-19');
1280 | // => true
1281 |
1282 | // Native
1283 | new Date(2010, 9, 20) > new Date(2010, 9, 19);
1284 | // => true
1285 |
1286 | // date-fns
1287 | import isAfter from 'date-fns/isAfter';
1288 | isAfter(new Date(2010, 9, 20), new Date(2010, 9, 19));
1289 | // => true
1290 |
1291 | // dayjs
1292 | dayjs('2010-10-20').isAfter('2010-10-19');
1293 | // => true
1294 |
1295 | // luxon
1296 | DateTime.fromISO('2010-10-20') > DateTime.fromISO('2010-10-19');
1297 | // => true
1298 |
1299 | // Temporal
1300 | Temporal.PlainDate.compare('2010-10-20', '2010-10-19') === 1;
1301 | // => true
1302 | ```
1303 |
1304 | **[⬆ back to top](#quick-links)**
1305 |
1306 | ### Is Between
1307 |
1308 | Check if a date is between two other dates.
1309 |
1310 | ```js
1311 | // Moment.js
1312 | moment('2010-10-20').isBetween('2010-10-19', '2010-10-25');
1313 | // => true
1314 |
1315 | // date-fns
1316 | import isWithinInterval from 'date-fns/isWithinInterval';
1317 | isWithinInterval(new Date(2010, 9, 20), {
1318 | start: new Date(2010, 9, 19),
1319 | end: new Date(2010, 9, 25),
1320 | });
1321 | // => true
1322 |
1323 | // dayjs ⚠️ requires isBetween plugin
1324 | import isBetween from 'dayjs/plugin/isBetween';
1325 | dayjs.extend(isBetween);
1326 | dayjs('2010-10-20').isBetween('2010-10-19', '2010-10-25');
1327 | // => true
1328 |
1329 | // luxon
1330 | Interval.fromDateTimes(
1331 | DateTime.fromISO('2010-10-19'),
1332 | DateTime.fromISO('2010-10-25')
1333 | ).contains(DateTime.fromISO('2010-10-20'));
1334 | // => true
1335 | ```
1336 |
1337 | **[⬆ back to top](#quick-links)**
1338 |
1339 | ### Is Leap Year
1340 |
1341 | Check if a year is a leap year.
1342 |
1343 | ```js
1344 | // Moment.js
1345 | moment([2000]).isLeapYear();
1346 | // => true
1347 |
1348 | // Native
1349 | new Date(2000, 1, 29).getDate() === 29;
1350 | // => true
1351 |
1352 | // date-fns
1353 | import isLeapYear from 'date-fns/isLeapYear';
1354 | isLeapYear(new Date(2000, 0, 1));
1355 | // => true
1356 |
1357 | // dayjs ⚠️ requires isLeapYear plugin
1358 | import isLeapYear from 'dayjs/plugin/isLeapYear';
1359 | dayjs.extend(isLeapYear);
1360 | dayjs('2000-01-01').isLeapYear();
1361 | // => true
1362 |
1363 | // luxon
1364 | expect(DateTime.local(2000).isInLeapYear).toBeTruthy();
1365 | // => true
1366 |
1367 | // Temporal
1368 | Temporal.PlainDate.from('2000-01-01').inLeapYear;
1369 | // => true
1370 | ```
1371 |
1372 | **[⬆ back to top](#quick-links)**
1373 |
1374 | ### Is a Date
1375 |
1376 | Check if a variable is a native js Date object.
1377 |
1378 | ```js
1379 | // Moment.js
1380 | moment.isDate(new Date());
1381 | // => true
1382 |
1383 | // Native
1384 | new Date() instanceof Date;
1385 | // => true
1386 |
1387 | // date-fns
1388 | import isDate from 'date-fns/isDate';
1389 | isDate(new Date());
1390 | // => true
1391 |
1392 | // dayjs
1393 | dayjs(new Date()).isValid();
1394 |
1395 | // luxon
1396 | DateTime.local().isValid;
1397 | // => true
1398 |
1399 | // Temporal
1400 | new Date() instanceof Date;
1401 | Temporal.Now.plainTimeISO() instanceof Temporal.PlainTime;
1402 | Temporal.Now.plainDateISO() instanceof Temporal.PlainDate;
1403 | Temporal.Now.plainDateTimeISO() instanceof Temporal.PlainDateTime;
1404 | Temporal.Now.zonedDateTimeISO() instanceof Temporal.ZonedDateTime;
1405 | // => true
1406 | ```
1407 |
1408 | **[⬆ back to top](#quick-links)**
1409 |
1410 | # License
1411 |
1412 | MIT
1413 |
--------------------------------------------------------------------------------
/__tests__/index.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment');
2 | const { DateTime, Interval } = require('luxon');
3 | const date = require('date-fns');
4 | const fr = require('date-fns/locale/fr');
5 | const dayjs = require('dayjs');
6 | const relativeTime = require('dayjs/plugin/relativeTime'); // load on demand
7 | const weekOfYear = require('dayjs/plugin/weekOfYear'); // load on demand
8 | const isBetween = require('dayjs/plugin/isBetween'); // load on demand
9 | const isLeapYear = require('dayjs/plugin/isLeapYear'); // load on demand
10 | dayjs.extend(relativeTime);
11 | dayjs.extend(weekOfYear);
12 | dayjs.extend(isBetween);
13 | dayjs.extend(isLeapYear);
14 |
15 | const time = 1536484369695;
16 |
17 | describe('Parse', () => {
18 | it('String + Date Format', () => {
19 | const m = moment('12-25-1995', 'MM-DD-YYYY');
20 |
21 | const [, mm, dd, yyyy] = /^(\d{2})-(\d{2})-(\d{4})$/.exec('12-25-1995');
22 | const n = new Date(`${mm}, ${dd} ${yyyy}`);
23 | expect(m.valueOf()).toBe(n.getTime());
24 |
25 | const d = date.parse('12-25-1995', 'MM-dd-yyyy', new Date());
26 | expect(m.valueOf()).toBe(d.getTime());
27 |
28 | const day = dayjs('12-25-1995');
29 | expect(m.valueOf()).toBe(day.valueOf());
30 |
31 | const luxon = DateTime.fromFormat('12-25-1995', 'MM-dd-yyyy');
32 | expect(m.valueOf()).toBe(luxon.ts);
33 | });
34 | it('String + Time Format', () => {
35 | const m = moment('2010-10-20 4:30', 'YYYY-MM-DD HH:mm');
36 |
37 | const [
38 | ,
39 | yyyy,
40 | mm,
41 | dd,
42 | hh,
43 | mi,
44 | ] = /^(\d{4})-(\d{2})-(\d{2})\s(\d{1,2}):(\d{2})$/.exec('2010-10-20 4:30');
45 | const n = new Date(`${yyyy}-${mm}-${dd}T${('0' + hh).slice(-2)}:${mi}:00`);
46 | expect(m.valueOf()).toBe(n.getTime());
47 |
48 | const d = date.parse('2010-10-20 4:30', 'yyyy-MM-dd H:mm', new Date());
49 | expect(m.valueOf()).toBe(d.getTime());
50 |
51 | const luxon = DateTime.fromFormat('2010-10-20 4:30', 'yyyy-MM-dd H:mm');
52 | expect(m.valueOf()).toBe(luxon.ts);
53 | });
54 | it('String + Format + locale', () => {
55 | const m = moment('2012 mars', 'YYYY MMM', 'fr');
56 |
57 | const d = date.parse('2012 mars', 'yyyy MMMM', new Date(), { locale: fr });
58 | expect(m.valueOf()).toBe(d.getTime());
59 | });
60 | });
61 |
62 | describe('Get + Set', () => {
63 | it('get Second', () => {
64 | const m = moment(time).seconds();
65 |
66 | const n = new Date(time).getSeconds();
67 | expect(m).toBe(n);
68 |
69 | const d = date.getSeconds(new Date(time));
70 | expect(m).toBe(d);
71 |
72 | const day = dayjs(time).second();
73 | expect(m).toBe(day);
74 |
75 | const luxon = DateTime.fromMillis(time).second;
76 | expect(m).toBe(luxon);
77 | });
78 | it('set Second', () => {
79 | const m = moment(time)
80 | .seconds(30)
81 | .valueOf();
82 | const n = new Date(time).setSeconds(30);
83 | expect(m).toBe(n);
84 |
85 | const d = date.setSeconds(new Date(time), 30).getTime();
86 | expect(m).toBe(d);
87 |
88 | const day = dayjs(time)
89 | .set('second', 30)
90 | .valueOf();
91 | expect(m).toBe(day);
92 |
93 | const luxon = DateTime.fromMillis(time).set({ second: 30 });
94 | expect(m).toBe(luxon.ts);
95 | });
96 |
97 | it('get Hour', () => {
98 | const m = moment(time).hours();
99 | const n = new Date(time).getHours();
100 | expect(m).toBe(n);
101 |
102 | const d = date.getHours(new Date(time));
103 | expect(m).toBe(d);
104 |
105 | const day = dayjs(time).hour();
106 | expect(m).toBe(day);
107 |
108 | const luxon = DateTime.fromMillis(time).hour;
109 | expect(m).toBe(luxon);
110 | });
111 | it('set Hour', () => {
112 | const m = moment(time)
113 | .hour(13)
114 | .valueOf();
115 | const n = new Date(time).setHours(13);
116 | expect(m).toBe(n);
117 |
118 | const d = date.setHours(new Date(time), 13).getTime();
119 | expect(m).toBe(d);
120 |
121 | const day = dayjs(time)
122 | .set('hour', 13)
123 | .valueOf();
124 | expect(m).toBe(day);
125 |
126 | const luxon = DateTime.fromMillis(time).set({ hour: 13 });
127 | expect(m).toBe(luxon.ts);
128 | });
129 |
130 | it('get Date of Month', () => {
131 | const m = moment(time).date();
132 | const n = new Date(time).getDate();
133 | expect(m).toBe(n);
134 |
135 | const d = date.getDate(new Date(time));
136 | expect(m).toBe(d);
137 |
138 | const day = dayjs(time).date();
139 | expect(m).toBe(day);
140 |
141 | const luxon = DateTime.fromMillis(time).day;
142 | expect(m).toBe(luxon);
143 | });
144 |
145 | it('set Date of Month', () => {
146 | const m = moment(time)
147 | .date(4)
148 | .valueOf();
149 | const n = new Date(time).setDate(4);
150 | expect(m).toBe(n);
151 |
152 | const d = date.setDate(new Date(time), 4).getTime();
153 | expect(m).toBe(d);
154 |
155 | const day = dayjs(time)
156 | .set('date', 4)
157 | .valueOf();
158 | expect(m).toBe(day);
159 |
160 | const luxon = DateTime.fromMillis(time).set({ day: 4 });
161 | expect(m).toBe(luxon.ts);
162 | });
163 |
164 | it('get Day of Week', () => {
165 | const m = moment(time).day();
166 | const n = new Date(time).getDay();
167 | expect(m).toBe(n);
168 |
169 | const d = date.getDay(new Date(time));
170 | expect(m).toBe(d);
171 |
172 | const day = dayjs(time).day();
173 | expect(m).toBe(day);
174 |
175 | const luxon = DateTime.fromMillis(time).weekday;
176 | expect(m).toBe(luxon % 7);
177 | });
178 |
179 | it('set Day of Week', () => {
180 | const m = moment(time)
181 | .day(-14)
182 | .valueOf();
183 | const n = new Date(time).setDate(new Date(time).getDate() - 14);
184 | expect(m).toBe(n);
185 |
186 | const d = date.setDay(new Date(time), -14).getTime();
187 | expect(m).toBe(d);
188 |
189 | const day = dayjs(time)
190 | .set('day', -14)
191 | .valueOf();
192 | expect(m).toBe(day);
193 |
194 | const luxon = DateTime.fromMillis(time).minus({ day: 14 });
195 | expect(m).toBe(luxon.ts);
196 | });
197 |
198 | it('get Day of Year', () => {
199 | const m = moment(time).dayOfYear();
200 | const n = Math.floor(
201 | (new Date(time) - new Date(new Date(time).getFullYear(), 0, 0)) /
202 | 1000 /
203 | 60 /
204 | 60 /
205 | 24
206 | );
207 | expect(m).toBe(n);
208 |
209 | const d = date.getDayOfYear(new Date(time));
210 | expect(m).toBe(d);
211 |
212 | const luxon = DateTime.fromMillis(time).ordinal;
213 | expect(m).toBe(luxon);
214 | });
215 |
216 | it('set Day of Year', () => {
217 | const m = moment(time)
218 | .dayOfYear(256)
219 | .valueOf();
220 | const d = date.setDayOfYear(new Date(time), 256).getTime();
221 | expect(m).toBe(d);
222 |
223 | const luxon = DateTime.fromMillis(time).set({ ordinal: 256 }).ts;
224 | expect(m).toBe(luxon);
225 | });
226 |
227 | it('get Week of Year', () => {
228 | const m = moment(time).week();
229 |
230 | const MILLISECONDS_IN_WEEK = 604800000;
231 | const firstDayOfWeek = 1; // monday as the first day (0 = sunday)
232 | const t = new Date(time);
233 | const s = new Date(t.getFullYear(), 0, 1);
234 | s.setDate(s.getDate() + ((firstDayOfWeek - s.getDay()) % 7));
235 | const n = Math.round((t - s) / MILLISECONDS_IN_WEEK) + 1;
236 | expect(m).toBe(n);
237 |
238 | const d = date.getWeek(new Date(time));
239 | expect(m).toBe(d);
240 |
241 | const day = dayjs(time).week(); // plugin
242 | expect(m).toBe(day);
243 |
244 | const luxon = DateTime.fromMillis(time).weekNumber + 1;
245 | expect(m).toBe(luxon);
246 | });
247 |
248 | it('set Week of Year', () => {
249 | const MILLISECONDS_IN_WEEK = 604800000;
250 | const firstDayOfWeek = 1; // monday as the first day (0 = sunday)
251 |
252 | const m = moment(time)
253 | .week(24)
254 | .valueOf();
255 | const n = new Date(time);
256 | const s = new Date(n.getFullYear(), 0, 1);
257 | s.setDate(s.getDate() + ((firstDayOfWeek - s.getDay()) % 7));
258 | const w = Math.round((n - s) / MILLISECONDS_IN_WEEK) + 1;
259 | n.setDate(n.getDate() - (w - 24) * 7);
260 | const d = date.setWeek(new Date(time), 24).getTime();
261 | expect(m).toBe(d);
262 | expect(m).toBe(n.getTime());
263 | expect(n.getTime()).toBe(d);
264 |
265 | const luxon = DateTime.fromMillis(time).set({ weekNumber: 23 });
266 | expect(m).toBe(luxon.ts);
267 | });
268 |
269 | it('Days in Month', () => {
270 | const m = moment('2012-02', 'YYYY-MM').daysInMonth();
271 | const d = date.getDaysInMonth(new Date(2012, 1));
272 | expect(m).toBe(d);
273 |
274 | const day = dayjs('2012-02').daysInMonth();
275 | expect(m).toBe(day);
276 |
277 | const n = new Date(2012, 2, 0).getDate();
278 | expect(m).toBe(n);
279 |
280 | const luxon = DateTime.local(2012, 2).daysInMonth;
281 | expect(m).toBe(luxon);
282 | });
283 |
284 | it('get Weeks In Year', () => {
285 | const m = moment(time).isoWeeksInYear();
286 | const d = date.getISOWeeksInYear(new Date(time));
287 | expect(m).toBe(d);
288 |
289 | const luxon = DateTime.fromMillis(time).weeksInWeekYear;
290 | expect(m).toBe(luxon);
291 | });
292 |
293 | it('Maximum of the given dates', () => {
294 | const array = [
295 | new Date(2017, 4, 13),
296 | new Date(2018, 2, 12),
297 | new Date(2016, 0, 10),
298 | new Date(2016, 0, 9),
299 | ];
300 | const m = moment.max(array.map(a => moment(a)));
301 | const d = date.max(array);
302 | expect(m.valueOf()).toBe(d.getTime());
303 | expect(d).toEqual(new Date(2018, 2, 12));
304 |
305 | const n = new Date(Math.max.apply(null, array));
306 | expect(n).toEqual(new Date(2018, 2, 12));
307 |
308 | const luxon = DateTime.max(
309 | ...array.map(a => DateTime.fromJSDate(a))
310 | ).toJSDate();
311 | expect(luxon).toEqual(new Date(2018, 2, 12));
312 | });
313 |
314 | it('Minimum of the given dates', () => {
315 | const array = [
316 | new Date(2017, 4, 13),
317 | new Date(2018, 2, 12),
318 | new Date(2016, 0, 10),
319 | new Date(2016, 0, 9),
320 | ];
321 | const n = new Date(Math.min.apply(null, array));
322 | expect(n).toEqual(new Date(2016, 0, 9));
323 |
324 | const m = moment.min(array.map(a => moment(a)));
325 | const d = date.min(array);
326 | expect(m.valueOf()).toBe(d.getTime());
327 | expect(d).toEqual(new Date(2016, 0, 9));
328 |
329 | const luxon = DateTime.min(
330 | ...array.map(a => DateTime.fromJSDate(a))
331 | ).toJSDate();
332 | expect(luxon).toEqual(new Date(2016, 0, 9));
333 | });
334 | });
335 |
336 | describe('Manipulate', () => {
337 | it('Add', () => {
338 | const m = moment(time).add(7, 'days');
339 | const d = date.addDays(new Date(time), 7);
340 | expect(m.valueOf()).toBe(d.getTime());
341 |
342 | const n = new Date(time);
343 | n.setDate(n.getDate() + 7);
344 | expect(n.valueOf()).toBe(m.valueOf());
345 |
346 | const day = dayjs(time).add(7, 'day');
347 | expect(m.valueOf()).toBe(day.valueOf());
348 |
349 | const luxon = DateTime.fromMillis(time).plus({ day: 7 });
350 | expect(m.valueOf()).toBe(luxon.ts);
351 | });
352 |
353 | it('Subtract', () => {
354 | const m = moment(time).subtract(7, 'days');
355 | const n = new Date(new Date(time).getTime() - 1000 * 60 * 60 * 24 * 7);
356 | expect(n.valueOf()).toBe(m.valueOf());
357 |
358 | const d = date.subDays(new Date(time), 7);
359 | expect(m.valueOf()).toBe(d.getTime());
360 |
361 | const day = dayjs(time).subtract(7, 'day');
362 | expect(m.valueOf()).toBe(day.valueOf());
363 |
364 | const luxon = DateTime.fromMillis(time).minus({ day: 7 });
365 | expect(m.valueOf()).toBe(luxon.ts);
366 | });
367 |
368 | it('Start of Time', () => {
369 | const m = moment(time).startOf('month');
370 | const d = date.startOfMonth(new Date(time));
371 | expect(m.valueOf()).toBe(d.getTime());
372 |
373 | const day = dayjs(time).startOf('month');
374 | expect(m.valueOf()).toBe(day.valueOf());
375 |
376 | const luxon = DateTime.fromMillis(time).startOf('month');
377 | expect(m.valueOf()).toBe(luxon.ts);
378 | });
379 |
380 | it('End of Time', () => {
381 | const m = moment(time).endOf('day');
382 | const n = new Date(time).setHours(23, 59, 59, 999);
383 | expect(m.valueOf()).toBe(n);
384 |
385 | const d = date.endOfDay(new Date(time));
386 | expect(m.valueOf()).toBe(d.getTime());
387 |
388 | const day = dayjs(time).endOf('day');
389 | expect(m.valueOf()).toBe(day.valueOf());
390 |
391 | const luxon = DateTime.fromMillis(time).endOf('day');
392 | expect(m.valueOf()).toBe(luxon.ts);
393 | });
394 | });
395 |
396 | describe('Display', () => {
397 | it('Format', () => {
398 | const m = moment(time).format('dddd, MMMM D YYYY, h:mm:ss A');
399 | const d = date.format(new Date(time), 'eeee, MMMM d yyyy, h:mm:ss aa', {
400 | awareOfUnicodeTokens: true,
401 | });
402 | const day = dayjs(time).format('dddd, MMMM D YYYY, h:mm:ss A');
403 | const l = DateTime.fromMillis(time).toFormat(
404 | 'EEEE, MMMM d yyyy, h:mm:ss a'
405 | );
406 | expect(m).toBe(d);
407 | expect(m).toBe(day);
408 | expect(m).toBe(l);
409 |
410 | const m2 = moment(time).format('ddd, hA');
411 | const d2 = date.format(new Date(time), 'eee, ha');
412 | const day2 = dayjs(time).format('ddd, hA');
413 | const l2 = DateTime.fromMillis(time).toFormat('EEE, ha');
414 | expect(m2).toBe(d2);
415 | expect(m2).toBe(day2);
416 | expect(m2).toBe(l2);
417 | });
418 |
419 | it('Time from now', () => {
420 | const month3 = 1000 * 3600 * 24 * 30 * 3; // ms * hour * day * month * 3
421 | const timeDistance = new Date().getTime() - month3;
422 |
423 | moment.relativeTimeThreshold(
424 | 'd',
425 | new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0).getDate()
426 | );
427 | const m = moment(timeDistance).fromNow();
428 | const n = new Intl.RelativeTimeFormat().format(-3, 'month');
429 | const d = date.formatDistanceStrict(new Date(timeDistance), new Date(), {
430 | addSuffix: true,
431 | });
432 | const day = dayjs(timeDistance).fromNow(); // plugin
433 | expect(m).toBe(d);
434 | expect(m).toBe(day);
435 | expect(m).toBe(n);
436 | });
437 |
438 | it('Time from X', () => {
439 | const m = moment([2007, 0, 27]).to(moment([2007, 0, 29]));
440 | const d = date.formatDistance(new Date(2007, 0, 27), new Date(2007, 0, 29));
441 | const day = dayjs('2007-01-27').to(dayjs('2007-01-29'));
442 | expect(m).toContain(d);
443 | expect(m).toBe(day);
444 | });
445 |
446 | it('Difference', () => {
447 | const m = moment([2007, 0, 27]).diff(moment([2007, 0, 29]));
448 | const n = new Date(2007, 0, 27) - new Date(2007, 0, 29);
449 | const d = date.differenceInMilliseconds(
450 | new Date(2007, 0, 27),
451 | new Date(2007, 0, 29)
452 | );
453 | const day = dayjs('2007-01-27').diff(dayjs('2007-01-29'), 'milliseconds');
454 | const luxon = DateTime.local(2007, 1, 27).diff(DateTime.local(2007, 1, 29))
455 | .milliseconds;
456 | expect(m).toBe(d);
457 | expect(m).toBe(day);
458 | expect(n).toBe(d);
459 | expect(n).toBe(m);
460 | expect(n).toBe(day);
461 | expect(n).toBe(luxon);
462 |
463 | const m2 = moment([2007, 0, 27]).diff(moment([2007, 0, 29]), 'days');
464 | const n2 = Math.ceil(
465 | (new Date(2007, 0, 27) - new Date(2007, 0, 29)) / 1000 / 60 / 60 / 24
466 | );
467 | const d2 = date.differenceInDays(
468 | new Date(2007, 0, 27),
469 | new Date(2007, 0, 29)
470 | );
471 | const day2 = dayjs('2007-01-27').diff(dayjs('2007-01-29'), 'days');
472 | const luxon2 = DateTime.local(2007, 1, 27).diff(
473 | DateTime.local(2007, 1, 29),
474 | 'days'
475 | ).days;
476 |
477 | expect(m2).toBe(d2);
478 | expect(m2).toBe(day2);
479 | expect(n2).toBe(m2);
480 | expect(n2).toBe(d2);
481 | expect(n2).toBe(day2);
482 | expect(n2).toBe(luxon2);
483 | });
484 | });
485 |
486 | describe('Query', () => {
487 | it('Is Before', () => {
488 | const m = moment('2010-10-20').isBefore('2010-10-21');
489 | const n = new Date(2010, 10, 20) < new Date(2010, 10, 21);
490 | const d = date.isBefore(new Date(2010, 9, 20), new Date(2010, 9, 21));
491 | const day = dayjs('2010-10-20').isBefore('2010-10-21'); //plugin
492 | const luxon =
493 | DateTime.fromISO('2010-10-20') < DateTime.fromISO('2010-10-21');
494 | expect(m).toBeTruthy();
495 | expect(d).toBeTruthy();
496 | expect(day).toBeTruthy();
497 | expect(n).toBeTruthy();
498 | expect(luxon).toBeTruthy();
499 | });
500 |
501 | it('Is Same', () => {
502 | expect(moment('2010-10-20').isSame('2010-10-21')).toBeFalsy();
503 | expect(new Date(2010, 9, 20) === new Date(2010, 9, 21)).toBeFalsy();
504 | expect(
505 | date.isSameDay(new Date(2010, 9, 20), new Date(2010, 9, 21))
506 | ).toBeFalsy();
507 | expect(dayjs('2010-10-20').isSame('2010-10-21')).toBeFalsy();
508 | expect(
509 | +DateTime.fromISO('2010-10-20') === +DateTime.fromISO('2010-10-21')
510 | ).toBeFalsy();
511 |
512 | expect(moment('2010-10-20').isSame('2010-10-21', 'month')).toBeTruthy();
513 | expect(
514 | new Date(2010, 9, 20).valueOf() === new Date(2010, 9, 20).valueOf()
515 | ).toBeTruthy();
516 | expect(
517 | new Date(2010, 9, 20).getTime() === new Date(2010, 9, 20).getTime()
518 | ).toBeTruthy();
519 | expect(
520 | new Date(2010, 9, 20).valueOf() === new Date(2010, 9, 20).getTime()
521 | ).toBeTruthy();
522 | expect(
523 | new Date(2010, 9, 20).toDateString().substring(4, 7) ===
524 | new Date(2010, 9, 21).toDateString().substring(4, 7)
525 | ).toBeTruthy();
526 | expect(
527 | date.isSameMonth(new Date(2010, 9, 20), new Date(2010, 9, 21))
528 | ).toBeTruthy();
529 | expect(
530 | DateTime.fromISO('2010-10-20').hasSame(
531 | DateTime.fromISO('2010-10-21'),
532 | 'month'
533 | )
534 | ).toBeTruthy();
535 | });
536 |
537 | it('Is After', () => {
538 | const m = moment('2010-10-20').isAfter('2010-10-19');
539 | const n = new Date(2010, 10, 20) > new Date(2010, 10, 19);
540 | const d = date.isAfter(new Date(2010, 9, 20), new Date(2010, 9, 19));
541 | const day = dayjs('2010-10-20').isAfter('2010-10-19');
542 | const luxon =
543 | DateTime.fromISO('2010-10-20') > DateTime.fromISO('2010-10-19');
544 | expect(m).toBeTruthy();
545 | expect(n).toBeTruthy();
546 | expect(d).toBeTruthy();
547 | expect(day).toBeTruthy();
548 | expect(luxon).toBeTruthy();
549 | });
550 |
551 | it('Is Between', () => {
552 | const m = moment('2010-10-20').isBetween('2010-10-19', '2010-10-25');
553 | const d = date.isWithinInterval(new Date(2010, 9, 20), {
554 | start: new Date(2010, 9, 19),
555 | end: new Date(2010, 9, 25),
556 | });
557 | const day = dayjs('2010-10-20').isBetween('2010-10-19', '2010-10-25'); //plugin
558 | const luxon = Interval.fromDateTimes(
559 | DateTime.fromISO('2010-10-19'),
560 | DateTime.fromISO('2010-10-25')
561 | ).contains(DateTime.fromISO('2010-10-20'));
562 |
563 | expect(m).toBeTruthy();
564 | expect(d).toBeTruthy();
565 | expect(day).toBeTruthy();
566 | expect(luxon).toBeTruthy();
567 | });
568 |
569 | it('Is Leap Year', () => {
570 | expect(moment([2000]).isLeapYear()).toBeTruthy();
571 | expect(moment([2001]).isLeapYear()).toBeFalsy();
572 | expect(new Date(2000, 1, 29).getDate() === 29).toBeTruthy();
573 | expect(date.isLeapYear(new Date(2000, 0, 1))).toBeTruthy();
574 | expect(date.isLeapYear(new Date(2001, 0, 1))).toBeFalsy();
575 | expect(dayjs('2000-01-01').isLeapYear()).toBeTruthy();
576 | expect(dayjs('2001-01-01').isLeapYear()).toBeFalsy();
577 | expect(DateTime.local(2000).isInLeapYear).toBeTruthy();
578 | expect(DateTime.local(2001).isInLeapYear).toBeFalsy();
579 | });
580 |
581 | it('Is a Date', () => {
582 | expect(moment.isDate(new Date())).toBeTruthy();
583 | expect(new Date() instanceof Date).toBeTruthy();
584 | expect(date.isDate(new Date())).toBeTruthy();
585 | expect(dayjs.isDayjs(dayjs())).toBeTruthy();
586 | expect(DateTime.local().isValid).toBeTruthy();
587 | });
588 | });
589 |
--------------------------------------------------------------------------------
/__tests__/performance.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment');
2 | const {
3 | getSeconds,
4 | getHours,
5 | setHours,
6 | endOfDay,
7 | startOfMonth,
8 | setSeconds,
9 | max,
10 | min,
11 | getDate,
12 | addDays,
13 | subDays,
14 | setDate,
15 | getDay,
16 | setDay,
17 | getISOWeeksInYear,
18 | getDayOfYear,
19 | setDayOfYear,
20 | setWeek,
21 | getWeek,
22 | getDaysInMonth,
23 | } = require('date-fns');
24 | const dayjs = require('dayjs');
25 | const { DateTime } = require('luxon');
26 | const iterations = 1000000;
27 | const array = [
28 | new Date(2017, 4, 13),
29 | new Date(2018, 2, 12),
30 | new Date(2016, 0, 10),
31 | new Date(2016, 0, 9),
32 | ];
33 |
34 | const performanceTest = (type, testFunction) => {
35 | console.time(type);
36 | for (let i = 0; i < iterations; i++) {
37 | testFunction();
38 | }
39 | console.timeEnd(type);
40 | };
41 |
42 | const runTests = object => {
43 | for (const key in object) {
44 | if (typeof object[key] === 'function') {
45 | object[key]();
46 | }
47 | }
48 | };
49 |
50 | const Get = {
51 | moment: () => {
52 | performanceTest('Moment', () => {
53 | moment().seconds();
54 | moment().hours();
55 | });
56 | },
57 | native: () => {
58 | performanceTest('Native', () => {
59 | new Date().getSeconds();
60 | new Date().getHours();
61 | });
62 | },
63 | dateFns: () => {
64 | performanceTest('DateFns', () => {
65 | getSeconds(new Date());
66 | getHours(new Date());
67 | });
68 | },
69 | dayJs: () => {
70 | performanceTest('DayJs', () => {
71 | dayjs().second();
72 | dayjs().hour();
73 | });
74 | },
75 | luxon: () => {
76 | performanceTest('Luxon', () => {
77 | DateTime.local().second;
78 | DateTime.local().hour;
79 | });
80 | },
81 | };
82 |
83 | // runTests(Get);
84 |
85 | const Set = {
86 | moment: () => {
87 | performanceTest('Moment', () => {
88 | moment().seconds(30);
89 | moment().hours(13);
90 | });
91 | },
92 | native: () => {
93 | performanceTest('Native', () => {
94 | new Date(new Date().setSeconds(30));
95 | new Date(new Date().setHours(13));
96 | });
97 | },
98 | dateFns: () => {
99 | performanceTest('DateFns', () => {
100 | setSeconds(new Date(), 30);
101 | setHours(new Date(), 13);
102 | });
103 | },
104 | dayJs: () => {
105 | performanceTest('DayJs', () => {
106 | dayjs().set('second', 30);
107 | dayjs().set('hour', 13);
108 | });
109 | },
110 | luxon: () => {
111 | performanceTest('Luxon', () => {
112 | DateTime.utc().set({ second: 30 });
113 | DateTime.utc().set({ hour: 13 });
114 | });
115 | },
116 | };
117 |
118 | // runTests(Set);
119 |
120 | const DateOfMonth = {
121 | moment: () => {
122 | performanceTest('Moment', () => {
123 | moment().date();
124 | moment().date(4);
125 | });
126 | },
127 | native: () => {
128 | performanceTest('Native', () => {
129 | new Date().getDate();
130 | new Date().setDate(4);
131 | });
132 | },
133 | dateFns: () => {
134 | performanceTest('DateFns', () => {
135 | getDate(new Date());
136 | setDate(new Date(), 4);
137 | });
138 | },
139 | dayJs: () => {
140 | performanceTest('DayJs', () => {
141 | dayjs().date();
142 | dayjs().set('date', 4);
143 | });
144 | },
145 | luxon: () => {
146 | performanceTest('Luxon', () => {
147 | DateTime.local().day;
148 | DateTime.local().set({ day: 4 });
149 | });
150 | },
151 | };
152 |
153 | // runTests(DateOfMonth);
154 |
155 | const DayOfWeek = {
156 | moment: () => {
157 | performanceTest('Moment', () => {
158 | moment().day();
159 | moment().day(-14);
160 | });
161 | },
162 | native: () => {
163 | performanceTest('Native', () => {
164 | new Date().getDay();
165 | new Date().setDate(new Date().getDate() - 14);
166 | });
167 | },
168 | dateFns: () => {
169 | performanceTest('DateFns', () => {
170 | getDay(new Date());
171 | setDay(new Date(), -14);
172 | });
173 | },
174 | dayJs: () => {
175 | performanceTest('DayJs', () => {
176 | dayjs().day();
177 | dayjs().set('day', -14);
178 | });
179 | },
180 | luxon: () => {
181 | performanceTest('Luxon', () => {
182 | DateTime.local().weekday;
183 | DateTime.local().set({ weekday: -14 });
184 | });
185 | },
186 | };
187 |
188 | // runTests(DayOfWeek);
189 |
190 | const DayOfYear = {
191 | moment: () => {
192 | performanceTest('Moment', () => {
193 | moment().dayOfYear();
194 | moment().dayOfYear(256);
195 | });
196 | },
197 | native: () => {
198 | performanceTest('Native', () => {
199 | Math.floor(
200 | (new Date() - new Date(new Date().getFullYear(), 0, 0)) /
201 | 1000 /
202 | 60 /
203 | 60 /
204 | 24
205 | );
206 | });
207 | },
208 | dateFns: () => {
209 | performanceTest('DateFns', () => {
210 | getDayOfYear(new Date());
211 | setDayOfYear(new Date(), 256);
212 | });
213 | },
214 | luxon: () => {
215 | performanceTest('Luxon', () => {
216 | DateTime.local().ordinal;
217 | DateTime.local().set({ ordinal: 256 });
218 | });
219 | },
220 | };
221 |
222 | // runTests(DayOfYear);
223 |
224 | const WeekOfYear = {
225 | moment: () => {
226 | performanceTest('Moment', () => {
227 | moment().week();
228 | moment().week(24);
229 | });
230 | },
231 | native: () => {
232 | performanceTest('Native', () => {
233 | const MILLISECONDS_IN_WEEK = 604800000;
234 | const firstDayOfWeek = 1;
235 |
236 | const t = new Date();
237 | const s = new Date(t.getFullYear(), 0, 1);
238 | s.setDate(s.getDate() + ((firstDayOfWeek - s.getDay()) % 7));
239 | Math.round((t - s) / MILLISECONDS_IN_WEEK) + 1;
240 |
241 | const d = new Date();
242 | const f = new Date(d.getFullYear(), 0, 1);
243 | f.setDate(f.getDate() + ((firstDayOfWeek - f.getDay()) % 7));
244 | d.setDate(
245 | d.getDate() - (Math.round((d - f) / MILLISECONDS_IN_WEEK) + 1 - 24) * 7
246 | );
247 | });
248 | },
249 | dateFns: () => {
250 | performanceTest('DateFns', () => {
251 | getWeek(new Date());
252 | setWeek(new Date(), 24);
253 | });
254 | },
255 | luxon: () => {
256 | performanceTest('Luxon', () => {
257 | DateTime.local().weekYear;
258 | DateTime.local().set({ weekYear: 24 });
259 | });
260 | },
261 | };
262 |
263 | // runTests(WeekOfYear);
264 |
265 | const DaysInMonth = {
266 | moment: () => {
267 | performanceTest('Moment', () => {
268 | moment('2012-02', 'YYYY-MM').daysInMonth();
269 | });
270 | },
271 | native: () => {
272 | performanceTest('Native', () => {
273 | new Date(2012, 2, 0).getDate();
274 | });
275 | },
276 | dateFns: () => {
277 | performanceTest('DateFns', () => {
278 | getDaysInMonth(new Date(2012, 2));
279 | });
280 | },
281 | dayJs: () => {
282 | performanceTest('DayJs', () => {
283 | dayjs('2012-02').daysInMonth();
284 | });
285 | },
286 | luxon: () => {
287 | performanceTest('Luxon', () => {
288 | DateTime.local(2012, 2).day;
289 | });
290 | },
291 | };
292 |
293 | // runTests(DaysInMonth);
294 |
295 | const WeeksInYear = {
296 | moment: () => {
297 | performanceTest('Moment', () => {
298 | moment().isoWeeksInYear();
299 | });
300 | },
301 | dateFns: () => {
302 | performanceTest('DateFns', () => {
303 | getISOWeeksInYear(new Date());
304 | });
305 | },
306 | luxon: () => {
307 | performanceTest('Luxon', () => {
308 | DateTime.local().weeksInWeekYear;
309 | });
310 | },
311 | };
312 |
313 | // runTests(WeeksInYear);
314 |
315 | const MaximumOfGivenDates = {
316 | moment: () => {
317 | performanceTest('Moment', () => {
318 | moment.max(array.map(a => moment(a)));
319 | });
320 | },
321 | native: () => {
322 | performanceTest('Native', () => {
323 | new Date(Math.max.apply(null, array)).toISOString();
324 | });
325 | },
326 | dateFns: () => {
327 | performanceTest('DateFns', () => {
328 | max(array);
329 | });
330 | },
331 | luxon: () => {
332 | performanceTest('Luxon', () => {
333 | const dates = array.map(a => DateTime.fromJSDate(a));
334 | DateTime.max(...dates);
335 | });
336 | },
337 | };
338 |
339 | // runTests(MaximumOfGivenDates);
340 |
341 | const MinimumOfGivenDates = {
342 | moment: () => {
343 | performanceTest('Moment', () => {
344 | moment.min(array.map(a => moment(a)));
345 | });
346 | },
347 | native: () => {
348 | performanceTest('Native', () => {
349 | new Date(Math.min.apply(null, array)).toISOString();
350 | });
351 | },
352 | dateFns: () => {
353 | performanceTest('DateFns', () => {
354 | min(array);
355 | });
356 | },
357 | luxon: () => {
358 | performanceTest('Luxon', () => {
359 | const dates = array.map(a => DateTime.fromJSDate(a));
360 | DateTime.min(...dates);
361 | });
362 | },
363 | };
364 |
365 | // runTests(MinimumOfGivenDates);
366 |
367 | const Add = {
368 | moment: () => {
369 | performanceTest('Moment', () => {
370 | moment().add(7, 'days');
371 | });
372 | },
373 | native: () => {
374 | performanceTest('Native', () => {
375 | const now = new Date();
376 | now.setDate(now.getDate() + 7);
377 | });
378 | },
379 | dateFns: () => {
380 | performanceTest('DateFns', () => {
381 | addDays(new Date(), 7);
382 | });
383 | },
384 | dayJs: () => {
385 | performanceTest('DayJs', () => {
386 | dayjs().add(7, 'day');
387 | });
388 | },
389 | luxon: () => {
390 | performanceTest('Luxon', () => {
391 | DateTime.local().plus({ day: 7 });
392 | });
393 | },
394 | };
395 |
396 | // runTests(Add);
397 |
398 | const Subtract = {
399 | moment: () => {
400 | performanceTest('Moment', () => {
401 | moment().subtract(7, 'days');
402 | });
403 | },
404 | native: () => {
405 | performanceTest('Native', () => {
406 | new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 7);
407 | });
408 | },
409 | dateFns: () => {
410 | performanceTest('DateFns', () => {
411 | subDays(new Date(), 7);
412 | });
413 | },
414 | dayJs: () => {
415 | performanceTest('DayJs', () => {
416 | dayjs().subtract(7, 'day');
417 | });
418 | },
419 | luxon: () => {
420 | performanceTest('Luxon', () => {
421 | DateTime.local().minus({ day: 7 });
422 | });
423 | },
424 | };
425 |
426 | // runTests(Subtract);
427 |
428 | const StartOfTime = {
429 | moment: () => {
430 | performanceTest('Moment', () => {
431 | moment().startOf('month');
432 | });
433 | },
434 | dateFns: () => {
435 | performanceTest('DateFns', () => {
436 | startOfMonth(new Date());
437 | });
438 | },
439 | dayJs: () => {
440 | performanceTest('DayJs', () => {
441 | dayjs().startOf('month');
442 | });
443 | },
444 | luxon: () => {
445 | performanceTest('Luxon', () => {
446 | DateTime.local().startOf('month');
447 | });
448 | },
449 | };
450 |
451 | // runTests(StartOfTime);
452 |
453 | const EndOfTime = {
454 | moment: () => {
455 | performanceTest('Moment', () => {
456 | moment().endOf('day');
457 | });
458 | },
459 | native: () => {
460 | performanceTest('Native', () => {
461 | new Date().setHours(23, 59, 59, 999);
462 | });
463 | },
464 | dateFns: () => {
465 | performanceTest('DateFns', () => {
466 | endOfDay(new Date());
467 | });
468 | },
469 | dayJs: () => {
470 | performanceTest('DayJs', () => {
471 | dayjs().endOf('day');
472 | });
473 | },
474 | luxon: () => {
475 | performanceTest('Luxon', () => {
476 | DateTime.local().endOf('day');
477 | });
478 | },
479 | };
480 |
481 | // runTests(EndOfTime);
482 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/you-dont-need/You-Dont-Need-Momentjs/43977cf990a4af509a758a4e9a6af40f4485d34d/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | [remoteMarkdownUrl](https://raw.githubusercontent.com/you-dont-need/You-Dont-Need-Momentjs/master/README.md)
2 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | You don't (may not) need Moment.js
6 |
7 |
8 |
12 |
16 |
17 |
18 |
19 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/__tests__/index.js:
--------------------------------------------------------------------------------
1 | const rule = require('../').rules;
2 | const RuleTester = require('eslint').RuleTester;
3 |
4 | RuleTester.setDefaultConfig({
5 | parser: 'babel-eslint',
6 | parserOptions: {
7 | ecmaVersion: 6,
8 | sourceType: 'module',
9 | },
10 | });
11 |
12 | var ruleTester = new RuleTester();
13 |
14 | describe('ESLint plugin', () => {
15 | ruleTester.run('no-dynamic-import-moment', rule['no-dynamic-import-moment'], {
16 | valid: ["import('date-fns').then()"],
17 | invalid: [
18 | {
19 | code: "import('moment').then(()=>{})",
20 | errors: [
21 | {
22 | message: 'Use date-fns or Native Date methods instead of moment.js',
23 | type: 'CallExpression',
24 | },
25 | ],
26 | },
27 | {
28 | code: "(async () => { const m = await import('moment'); })()",
29 | errors: [
30 | {
31 | message: 'Use date-fns or Native Date methods instead of moment.js',
32 | type: 'CallExpression',
33 | },
34 | ],
35 | },
36 | ],
37 | });
38 |
39 | ruleTester.run('no-import-moment', rule['no-import-moment'], {
40 | valid: [
41 | "import { format, formatDistance, formatRelative, subDays } from 'date-fns'",
42 | ],
43 | invalid: [
44 | {
45 | code: "import moment from 'moment'",
46 | errors: [
47 | {
48 | message: 'Use date-fns or Native Date methods instead of moment.js',
49 | type: 'ImportDeclaration',
50 | },
51 | ],
52 | },
53 | {
54 | code: "import * as moment from 'moment'",
55 | errors: [
56 | {
57 | message: 'Use date-fns or Native Date methods instead of moment.js',
58 | type: 'ImportDeclaration',
59 | },
60 | ],
61 | },
62 | ],
63 | });
64 |
65 | ruleTester.run('no-moment-constructor', rule['no-moment-constructor'], {
66 | valid: ['new Date()', 'Date()'],
67 | invalid: [
68 | {
69 | code: 'moment()',
70 | errors: [
71 | {
72 | message: 'Consider using Native new Date().',
73 | type: 'CallExpression',
74 | },
75 | ],
76 | },
77 | {
78 | code: "moment('12-25-1995')",
79 | errors: [
80 | {
81 | message: 'Consider using Native new Date().',
82 | type: 'CallExpression',
83 | },
84 | ],
85 | },
86 | {
87 | code: "moment('12-25-1995', 'MM-DD-YYYY')",
88 | errors: [
89 | {
90 | message:
91 | 'Consider using date-fns, e.g. parse("2010-10-20 4:30", "yyyy-MM-dd H:mm", new Date()).',
92 | type: 'CallExpression',
93 | },
94 | ],
95 | },
96 | ],
97 | });
98 |
99 | ruleTester.run('no-moment-methods/seconds', rule['seconds'], {
100 | valid: [],
101 | invalid: [
102 | {
103 | code: 'moment().seconds()',
104 | errors: [
105 | {
106 | message:
107 | 'Consider using new Date().getSeconds() or new Date().setSeconds()',
108 | type: 'CallExpression',
109 | },
110 | ],
111 | },
112 | ],
113 | });
114 |
115 | ruleTester.run('no-moment-methods/seconds', rule['seconds'], {
116 | valid: [],
117 | invalid: [
118 | {
119 | code: "moment()['seconds']();",
120 | errors: [
121 | {
122 | message:
123 | 'Consider using new Date().getSeconds() or new Date().setSeconds()',
124 | type: 'CallExpression',
125 | },
126 | ],
127 | },
128 | ],
129 | });
130 |
131 | ruleTester.run('no-moment-methods/is-after', rule['is-after'], {
132 | valid: [],
133 | invalid: [
134 | {
135 | code: "const m = moment('2010-10-20').isAfter('2010-10-19')",
136 | errors: [
137 | {
138 | message:
139 | 'Consider using date-fns isAfter(date, dateToCompare) or dayjs().isAfter()',
140 | type: 'CallExpression',
141 | },
142 | ],
143 | },
144 | ],
145 | });
146 |
147 | ruleTester.run('no-require-moment', rule['no-require-moment'], {
148 | valid: [
149 | "var { format, formatDistance, formatRelative, subDays } = require('date-fns')",
150 | 'var empty = require()',
151 | 'var other = r()',
152 | ],
153 | invalid: [
154 | {
155 | code: "var moment = require('moment')",
156 | errors: [
157 | {
158 | message: 'Use date-fns or Native Date methods instead of moment.js',
159 | type: 'CallExpression',
160 | },
161 | ],
162 | },
163 | {
164 | code: "var format = require('moment').format",
165 | errors: [
166 | {
167 | message: 'Use date-fns or Native Date methods instead of moment.js',
168 | type: 'CallExpression',
169 | },
170 | ],
171 | },
172 | ],
173 | });
174 | });
175 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | const rules = Object.assign(
2 | {},
3 | {
4 | 'no-dynamic-import-moment': require('./rules/no-dynamic-import-moment'),
5 | 'no-import-moment': require('./rules/no-import-moment'),
6 | 'no-moment-constructor': require('./rules/no-moment-constructor'),
7 | 'no-require-moment': require('./rules/no-require-moment'),
8 | },
9 | require('./rules/no-moment-methods')
10 | );
11 |
12 | module.exports.rules = rules;
13 |
14 | const configure = (list, level) => {
15 | const r = {};
16 | Object.keys(list).map(rule => (r['you-dont-need-momentjs/' + rule] = level));
17 | return r;
18 | };
19 |
20 | module.exports.configs = {
21 | recommended: {
22 | plugins: ['you-dont-need-momentjs'],
23 | rules: configure(rules, 2),
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/lib/rules/methods.json:
--------------------------------------------------------------------------------
1 | {
2 | "seconds": {
3 | "alternative": "new Date().getSeconds() or new Date().setSeconds()"
4 | },
5 | "hours": {
6 | "alternative": "new Date().getHours() or new Date().setHours()"
7 | },
8 | "date": {
9 | "alternative": "new Date().getDate() or new Date().setDate()"
10 | },
11 | "day": {
12 | "alternative": "new Date().getDay() or new Date().setDate()"
13 | },
14 | "dayOfYear": {
15 | "alternative": "date-fns getDayOfYear(date) or setDayOfYear(date, dayOfYear)"
16 | },
17 | "week": {
18 | "alternative": "date-fns getWeek(date) or setWeek(date, week)"
19 | },
20 | "isoWeeksInYear": {
21 | "alternative": "date-fns getISOWeeksInYear(date)"
22 | },
23 | "max": {
24 | "alternative": "date-fns max(date)"
25 | },
26 | "min": {
27 | "alternative": "date-fns min(date)"
28 | },
29 | "add": {
30 | "alternative": "date-fns addDays(date, amount) or dayjs().add(number, unit)"
31 | },
32 | "subtract": {
33 | "alternative": "date-fns subDays(date, amount) or dayjs().subtract(number, unit)"
34 | },
35 | "startOf": {
36 | "alternative": "date-fns startOfMonth(date) or dayjs().startOf(unit)"
37 | },
38 | "endOf": {
39 | "alternative": "date-fns endOfDay(date) or dayjs().endOf(unit)"
40 | },
41 | "format": {
42 | "alternative": "date-fns format(date, format) or dayjs().format()"
43 | },
44 | "fromNow": {
45 | "alternative": "date-fns formatDistance(date, baseDate)"
46 | },
47 | "to": {
48 | "alternative": "date-fns formatDistance(date, baseDate)"
49 | },
50 | "diff": {
51 | "alternative": "date-fns differenceInMilliseconds(dateLeft, dateRight) or dayjs().diff()"
52 | },
53 | "daysInMonth": {
54 | "alternative": "date-fns getDaysInMonth(date) or dayjs().daysInMonth();"
55 | },
56 | "isBefore": {
57 | "alternative": "date-fns isBefore(date, dateToCompare) or dayjs().isBefore()"
58 | },
59 | "isSame": {
60 | "alternative": "date-fns isSameMonth(dateLeft, dateRight) or dayjs().isSame()"
61 | },
62 | "isAfter": {
63 | "alternative": "date-fns isAfter(date, dateToCompare) or dayjs().isAfter()"
64 | },
65 | "isBetween": {
66 | "alternative": "date-fns isWithinInterval(date, interval)"
67 | },
68 | "isLeapYear": {
69 | "alternative": "date-fns isLeapYear(date)"
70 | },
71 | "isDate": {
72 | "alternative": "date-fns isDate(date)"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/rules/no-dynamic-import-moment.js:
--------------------------------------------------------------------------------
1 | var message = 'Use date-fns or Native Date methods instead of moment.js';
2 |
3 | module.exports = function(context) {
4 | return {
5 | CallExpression: function(node) {
6 | if (node.callee.type !== 'Import') {
7 | return;
8 | }
9 |
10 | var arg = node.arguments[0];
11 |
12 | if (arg.type === 'Literal' && arg.value === 'moment') {
13 | context.report(node, message);
14 | }
15 | },
16 | };
17 | };
18 |
--------------------------------------------------------------------------------
/lib/rules/no-import-moment.js:
--------------------------------------------------------------------------------
1 | module.exports = function(context) {
2 | return {
3 | ImportDeclaration: function(node) {
4 | node.specifiers.forEach(function(specifier) {
5 | if (
6 | (specifier.type === 'ImportDefaultSpecifier' ||
7 | specifier.type === 'ImportNamespaceSpecifier') &&
8 | specifier.local.type === 'Identifier' &&
9 | specifier.local.name === 'moment'
10 | ) {
11 | context.report(
12 | node,
13 | 'Use date-fns or Native Date methods instead of moment.js'
14 | );
15 | }
16 | });
17 | },
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/lib/rules/no-moment-constructor.js:
--------------------------------------------------------------------------------
1 | module.exports = function(context) {
2 | return {
3 | CallExpression(node) {
4 | if (node.callee.name === 'moment') {
5 | if (node.arguments.length === 0 || node.arguments.length === 1) {
6 | context.report({
7 | node,
8 | message: `Consider using Native new Date().`,
9 | });
10 | } else {
11 | context.report({
12 | node,
13 | message: `Consider using date-fns, e.g. parse("2010-10-20 4:30", "yyyy-MM-dd H:mm", new Date()).`,
14 | });
15 | }
16 | }
17 | },
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/lib/rules/no-moment-methods.js:
--------------------------------------------------------------------------------
1 | const kebabCase = require('kebab-case');
2 | const rules = require('./methods');
3 |
4 | for (const rule in rules) {
5 | const alternative = rules[rule].alternative;
6 | const ruleName = rules[rule].ruleName || kebabCase(rule);
7 | module.exports[ruleName] = {
8 | create(context) {
9 | return {
10 | CallExpression(node) {
11 | const callee = node.callee;
12 | const objectName =
13 | callee.name ||
14 | (callee.object && callee.object.name) ||
15 | (callee.object &&
16 | callee.object.callee &&
17 | callee.object.callee.name);
18 | if (
19 | objectName === 'moment' &&
20 | callee.property &&
21 | (callee.property.name === rule || callee.property.value === rule)
22 | ) {
23 | context.report({
24 | node,
25 | message: `Consider using ${alternative}`,
26 | });
27 | }
28 | },
29 | };
30 | },
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/lib/rules/no-require-moment.js:
--------------------------------------------------------------------------------
1 | module.exports = function(context) {
2 | return {
3 | CallExpression: function(node) {
4 | if (node.callee.type === 'Identifier' && node.callee.name === 'require') {
5 | var arg = node.arguments[0];
6 |
7 | if (arg && arg.type === 'Literal' && arg.value === 'moment') {
8 | context.report(
9 | node,
10 | 'Use date-fns or Native Date methods instead of moment.js'
11 | );
12 | }
13 | }
14 | },
15 | };
16 | };
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-plugin-you-dont-need-momentjs",
3 | "description": "Check better alternatives you can use without momentjs",
4 | "version": "1.6.0",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/you-dont-need/You-Dont-Need-Momentjs.git"
8 | },
9 | "keywords": [
10 | "moment-js",
11 | "date-fns",
12 | "dayjs",
13 | "luxon",
14 | "eslint",
15 | "eslintplugin",
16 | "eslint-plugin",
17 | "tree-shaking",
18 | "you-dont-need"
19 | ],
20 | "author": "Andrew Yang ",
21 | "contributors": [
22 | "Robert Chang ",
23 | "Jago MF (https://github.com/jagomf)",
24 | "Steve Mao (https://github.com/stevemao)",
25 | "CY Lim (https://l.cy.my/github)"
26 | ],
27 | "main": "lib/index.js",
28 | "scripts": {
29 | "test:watch": "jest --watch",
30 | "test": "jest --coverage",
31 | "test:perf": "node ./__tests__/performance.js",
32 | "coveralls": "cat ./coverage/lcov.info | coveralls",
33 | "precommit": "pretty-quick --staged"
34 | },
35 | "devDependencies": {
36 | "babel-eslint": "^10.1.0",
37 | "coveralls": "^3.0.2",
38 | "date-fns": "^2.0.0",
39 | "dayjs": "^1.8.15",
40 | "eslint": "^5.5.0",
41 | "husky": "^0.14.3",
42 | "jest": "^29.6.2",
43 | "luxon": "^1.17.2",
44 | "moment": "^2.24.0",
45 | "prettier": "^1.14.2",
46 | "pretty-quick": "^1.6.0"
47 | },
48 | "dependencies": {
49 | "kebab-case": "^1.0.0"
50 | },
51 | "peerDependencies": {
52 | "eslint": "^5.5.0 || ^7.0.0"
53 | },
54 | "jest": {
55 | "collectCoverageFrom": [
56 | "lib/**/*.js"
57 | ],
58 | "testPathIgnorePatterns": [
59 | "/node_modules/",
60 | "./__tests__/performance.js"
61 | ]
62 | },
63 | "license": "MIT",
64 | "bugs": {
65 | "url": "https://github.com/you-dont-need/You-Dont-Need-Momentjs/issues"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/you-dont-need/You-Dont-Need-Momentjs/43977cf990a4af509a758a4e9a6af40f4485d34d/screenshot.png
--------------------------------------------------------------------------------