Generator-jhipster: Replace moment with day-js

Created on 23 Sep 2020  路  9Comments  路  Source: jhipster/generator-jhipster

Overview of the feature request

Moment is now in maintenance mode. For vue we have already date-fns, but unification across all frontends is desired.
To make the migration as easy as possible we could try day.js as try.

Motivation for or Use Case

Get rid of old moment library. This would benefit the longterm development of jhipster and reduce the bundle size for angular/react a lot.

Related issues or PR
$$ bug-bounty $$ $200 area angular react vue

Most helpful comment

For the moment, DayJS seems to be a better replacement solution to us:

  • The lib has more stars than Date fns
  • It's composable thanks to the plugins
  • DayJS's API is very close to moment's one
  • In our experience, DayJS is developer friendly

Don't hesitate to shate your perspective

All 9 comments

@atomfrede or @pascalgrimaud can you assign @qlereboursBS and me on this one to try to fix it please ?

@qlereboursBS needs to comment so I'd be able to assign to him too :)

Done @pascalgrimaud

Ok, so, we listed all the usages of moments or date fns in the project, to be able to find each very specific usage and check if DayJS or Date fns can replace them:

| File | Use case |Comment | With DayJS |
|----------------|-------------------------------|-----------------------------|--------|
|core.module.ts.ejs (15) |import | import * as moment from 'moment'; | import dayjs from 'dayjs' |
|core.module.ts.ejs (105) |Substract date| moment().year() - 100 | dayjs().subtract(100, 'year') |
|date-utils.ts.ejs (19) |import | import moment from 'moment'; | import dayjs from 'dayjs' |
|date-utils.ts.ejs (24) | format date |moment(date).format(APP_LOCAL_DATETIME_FORMAT) | dayjs(date).format(APP_LOCAL_DATETIME_FORMAT)|
|date-utils.ts.ejs (27) | parse date | moment(date, APP_LOCAL_DATETIME_FORMAT_Z).toDate() | dayjs(date, APP_LOCAL_DATETIME_FORMAT_Z).toDate() and don't forget to add dayjs.extend(customParseFormat) |
|date-utils.ts.ejs (29) | format |moment().startOf('day').format(APP_LOCAL_DATETIME_FORMAT)| dayjs().startOf('day').format(APP_LOCAL_DATETIME_FORMAT) |
|datepicker-adapter.ts.ejs (23) | import |import { Moment } from 'moment'; | There's a Dayjs type but we're not sure its exported. | |
|datepicker-adapter.ts.ejs (24) | import |import * as moment from 'moment'; | import * as dayjs from 'dayjs' |
|datepicker-adapter.ts.ejs (27) | Specific use |export class ... extends NgbDateAdapter<Moment> { | There's a Dayjs type but we're not sure its exported. | |
|datepicker-adapter.ts.ejs (28) | Typing |fromModel(date: Moment): NgbDateStruct | There's a Dayjs type but we're not sure its exported. | |
|datepicker-adapter.ts.ejs (29) | Check validity |moment.isMoment(date) && date.isValid() | dayjs.isDayjs(date) && date.isValid() |
|datepicker-adapter.ts.ejs (36) | typing |toModel(date: NgbDateStruct): Moment { | There's a Dayjs type but we're not sure its exported. |
|datepicker-adapter.ts.ejs (38) | Parse date |moment(date.year + '-' + date.month + '-' + date.day, 'YYYY-MM-DD')|dayjs(date.year + '-' + date.month + '-' + date.day, 'YYYY-MM-DD') |
|duration.pipe.ts.ejs (3) | import |import * as moment from 'moment'; | import * as dayjs from 'dayjs' |
|duration.pipe.ts.ejs (11) | Specific use (duration) |return moment.duration(value).humanize(); | return dayjs.duration(value).humanize(); |
|DurationFormat.tsx.ejs (3) | import |import * as moment from 'moment'; |import * as dayjs from 'dayjs' |
|DurationFormat.tsx.ejs (20) | Specific use (duration) |moment.duration(value).locale(locale).humanize(); | dayjs.duration(value).locale(locale).humanize(); |
|entity-management-update.component.ts.ejs (49)| import |import * as moment from 'moment'; | import * as dayjs from 'dayjs |
|entity-management-update.component.ts.ejs (184) | Get current date |const today = moment().startOf('day'); |const today = dayjs().startOf('day'); |
|entity-management-update.component.ts.ejs (299)| Parse date | moment(this.editForm.get(['<%= fieldName %>'])!.value, DATE_TIME_FORMAT)| dayjs(this.editForm.get(['<%= fieldName %>'])!.value, DATE_TIME_FORMAT) |
|entity-management.service.spec.ts.ejs (27) | import | import * as moment from 'moment'; | import * as dayjs from 'dayjs' |
|entity-management.service.spec.ts.ejs (44) | typing |moment.Moment |There's a Dayjs type but we're not sure its exported. |
|entity-management.service.spec.ts.ejs (58) | Current date |currentDate = moment(); | currentDate = dayjs(); |
|entity.model.ts.ejs - React (26) | typing |import { Moment } from 'moment'; | There's a Dayjs type but we're not sure its exported. |
|entity.model.ts.ejs - Angular (26) | typing |import { Moment } from 'moment'; | There's a Dayjs type but we're not sure its exported. |
|entity.service.ts.ejs - Angular (30) | import |import * as moment from 'moment'; | import * as dayjs from 'dayjs'; |
|entity.service.ts.ejs - Angular (118) | parse date |moment(res.body.<%= fields[idx].fieldName %>) | dayjs(res.body.<%= fields[idx].fieldName %>) |
|entity.service.ts.ejs - Angular (130) | parse date |moment(<%= entityInstance %>.<%= fields[idx].fieldName %>)| dayjs(<%= entityInstance %>.<%= fields[idx].fieldName %>)|
|generator-base-private.js (370)| Specific use |Selecting locales to keep in webpack.prod.js of Angular | DayJS doesn't work the same way because it doesn't have a webpack plugin to whitelist languages. By default only specified languages will be loaded. We'll create a file like dayjs.config.js that will contain as much import 'dayjs/locale/fr' as there are languages. |
|generator-base-private.js (406) |Specific use |Selecting locales to keep in webpack.prod.js of React | Removed according to the previous comment |
|generator-base.js (567) |Specific use| The function getMomentLocaleId() returns the given language id. Its using the field momentLocaleId from LANGUAGES of generator-constants because some specific languages have specific language id, i.e: hy-am for Armenian | Replace momentLocaleId by dateLibLocaleId and check if the list of localeId are the same on DayJS than on moment |
| package.json | Specific use | usage of moment and moment-locales-webpack-plugin| It will be removed because there's no need of a webpack plugin with DayJS |
| webpack.prod.js.ejs | Specific use | Uses moment-locales-webpack-plugin to load the locales | It will be removed because there's no need of a webpack plugin with DayJS |

Date fns usage
| File | Kind of use |Comment | With DayJS |
|----------------|-------------------------------|-----------------------------|------|
| entity-update.component.spec.ts.ejs (35) | format date | import format, to format date with DATE_TIME_LONG_FORMAT format (line 133) | import * as dayjs from 'dayjs'; then dayjs.format(date, DATE_TIME_LONG_FORMAT) |
| entity-update.component.spec.ts.ejs (36) | parse date | import parseISO unused | Remove import because it's not used |
| entity-update.component.ts.ejs (49) | format date | format date with DATE_TIME_LONG_FORMAT format (line 231) | replace import and then replace format(date, DATE_TIME_LONG_FORMAT) with dayjs(date).format(DATE_TIME_LONG_FORMAT) |
| entity-update.component.ts.ejs (50) | check validity | check if date is valid (line 230) | Remove the import and replace isValid(date) by dayjs(date).isValid()
| entity-update.component.ts.ejs (51) | parse date | parse date with DATE_TIME_LONG_FORMAT format (line 238, 246) | remove import and replace parse(event.target.value, DATE_TIME_LONG_FORMAT, new Date()) by dayjs(event.target.value, DATE_TIME_LONG_FORMAT) |
| entity-update.component.spec.ts.ejs (36) | parse date | import parseISO unused | Remove unused import |
| entity.service.spec.ts.ejs (26) | format date | format date with format DATE_FORMAT or DATE_TIME_FORMAT | Replace all format(a, b) by dayjs(a).format(b) |
| filters.ts.ejs (2) | format date | format date with format DATE_TIME_FORMAT | replace format(new Date(value), DATE_TIME_FORMAT) by dayjs(new Date(value)).format(DATE_TIME_FORMAT)|
| filters.ts.ejs (2) | parse date | parsing date | replace parseISO(value) by dayjs(value) |
| filters.ts.ejs (2) | format duration | formats a duration: https://date-fns.org/v2.15.0/docs/formatDuration | dayjs.duration(value).humanize(); |
| filters.ts.ejs (3) | parse duration | Import of duration-fns to parse a duration (line 27) | Removed the import and use the previous line |

For the moment, DayJS seems to be a better replacement solution to us:

  • The lib has more stars than Date fns
  • It's composable thanks to the plugins
  • DayJS's API is very close to moment's one
  • In our experience, DayJS is developer friendly

Don't hesitate to shate your perspective

We updated the table above, to add the replacement solutions with DayJS.
We're going to start implementing now

Good work. Looking forward to the result!

We have implemented DayJs in the generator.
We're starting to find all the use cases where a date is used to test the new solution :slightly_smiling_face:

Functionnal usages (checked if tested):

  • [x] date-utils.ts.ejs#convertDateTimeFromServer: Only for React, aims to convert a date from server to the client
  • [x] date-utils.ts.ejs#convertDateTimeToServer: Only for React, aims to convert a date from client to server
  • [x] date-utils.ts.ejs#displayDefaultDateTime: Only for React, aims to get the default date time for inputs
  • [x] core.module.ts.ejs: Only for Angular, aims to set the datePicker's config (NgbDatepickerConfig), to set minDate at -100 years from today
  • [x] datepicker-adapter.ts.ejs: Only for Angular, it's used to convert date to map date formats between boostrap datepicker component and the model
  • [x] duration.pipe.ts.ejs: Only for Angular, define the behavior of the duration pipe (| duration), used in entity-management and entity-manage-details screens
  • [x] DurationFormat.tsx.ejs: Only for Angular, aims to format a duration
  • [x] entity-management-update.component.ts.ejs Only for Angular, aims to create the data from the form value, before saving, on the entity screens
  • [x] entity-management.service.spec.ts.ejs: Only for Angular, initializes a date for testing purpose
  • [x] entity.service.ts.ejs#convertDateArrayFromServer: Only for Angular, in entity's service, creates a dayjs date from server when working with a list of entity
  • [x] entity-update.component.spec.ts.ejs: Only for Vue, when testing entity update screen, import vuejs only if there's a date field, and do assertions
  • [x] entity-update.component.ts.ejs: Only for Vue, in entity update screen, importing dayjs only if there's a date field in the entity
  • [x] entity.service.spec.ts.ejs: Only for Vue, testing the service
  • [x] entity.service.ts.ejs#convertDateFromServer: Only for Vue, in entity's service, creates a dayjs date from server
  • [x] filters.ts.ejs#formatDate: Only for Vue, this filter is used in multiple screens like admin/tracker, user-management, entity-details
  • [x] filters.ts.ejs#formatMillis: Only for Vue, this filter is only used in admin/metrics screen
  • [x] filters.ts.ejs#duration: Only for Vue, this filter is used in entity.vue screen, entity-details.vue
Was this page helpful?
0 / 5 - 0 ratings

Related issues

sdoxsee picture sdoxsee  路  4Comments

ahmedeldeeb25 picture ahmedeldeeb25  路  3Comments

frantzynicolas picture frantzynicolas  路  3Comments

chegola picture chegola  路  4Comments

dronavallisaikrishna picture dronavallisaikrishna  路  3Comments