├── .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 | [![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](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 | ![Large bundle size](./screenshot.png) 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) | ![stars](https://flat.badgen.net/github/stars/moment/moment) | ![raw](https://flat.badgen.net/bundlephobia/min/moment?label=min) ![size](https://flat.badgen.net/bundlephobia/minzip/moment?label=zip&color=purple) | 26 | | Luxon | No | High | OO | - | Good (Intl) | ![stars](https://flat.badgen.net/github/stars/moment/luxon) | ![raw](https://flat.badgen.net/bundlephobia/min/luxon?label=min) ![size](https://flat.badgen.net/bundlephobia/minzip/luxon?label=zip&color=purple) | 27 | | date-fns | Yes | High | Functional | 64 | Good (date-fns-tz) | ![stars](https://flat.badgen.net/github/stars/date-fns/date-fns) | ![raw](https://flat.badgen.net/bundlephobia/min/date-fns?label=min) ![size](https://flat.badgen.net/bundlephobia/minzip/date-fns?label=zip&color=purple) | 28 | | dayjs | No | High | OO | 138 | Good (Intl) | ![stars](https://flat.badgen.net/github/stars/iamkun/dayjs) | ![raw](https://flat.badgen.net/bundlephobia/min/dayjs?label=min) ![size](https://flat.badgen.net/bundlephobia/minzip/dayjs?label=zip&color=purple) | 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 | NPM Version 50 | 51 | 52 | Downloads 54 | 55 | 56 | Build Status 58 | 59 | 60 | Coverage Status 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 --------------------------------------------------------------------------------