From 801d081a8761a6e2184b866012faa94438f25bd0 Mon Sep 17 00:00:00 2001 From: Vanessa Busch Date: Fri, 3 Nov 2023 09:56:53 -0400 Subject: [PATCH] refactor(dateHelpers): sw-260 remove moment.js (#1220) --- CONTRIBUTING.md | 2 +- src/common/README.md | 117 +++++++++++- .../__snapshots__/dateHelpers.test.js.snap | 10 + src/common/__tests__/dateHelpers.test.js | 50 ++++- src/common/dateHelpers.js | 175 ++++++++++++++---- 5 files changed, 312 insertions(+), 42 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55ec294d2..268bd752d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -399,7 +399,7 @@ The dotenv files are structured to cascade each additional dotenv file settings | DEV_PORT | A local proxy build modification for running against a custom port | | DEV_BRANCH | A local proxy build modification for running against a custom environment branch. Available options include `stage-beta`, `stage-stable`, `prod-beta`, `prod-stable` | | GENERATE_SOURCEMAP | A static boolean that disables local run source map generation only. May speed up local development re-compiles. May eventually be moved into `.env.development`. | -| REACT_APP_DEBUG_DEFAULT_DATETIME | A static string associated with overriding the assumed UI/application date in the form of `YYYYMMDD` | +| REACT_APP_DEBUG_DEFAULT_DATETIME | A static string associated with overriding the assumed UI/application date in the form of `YYYY-MM-DD` | | REACT_APP_DEBUG_MIDDLEWARE | A static boolean that activates the console state debugging messages associated with Redux. | | REACT_APP_DEBUG_ORG_ADMIN | A static boolean associated with local development only that overrides the organization admin. Useful in determining UI/application behavior when permissions are missing. | | REACT_APP_DEBUG_PERMISSION_APP_ONE | A static string associated with local development only that overrides RBAC associated permissions. Useful in determining UI/application behavior when permissions are missing. | diff --git a/src/common/README.md b/src/common/README.md index b4061f12a..824725777 100644 --- a/src/common/README.md +++ b/src/common/README.md @@ -14,16 +14,62 @@ ## Dates * [Dates](#Helpers.module_Dates) + * [~currentDateTime](#Helpers.module_Dates..currentDateTime) : Object + * [~defaultDateTime](#Helpers.module_Dates..defaultDateTime) : Object + * [~weeklyDateTime](#Helpers.module_Dates..weeklyDateTime) : Object + * [~monthlyDateTime](#Helpers.module_Dates..monthlyDateTime) : Object + * [~quarterlyDateTime](#Helpers.module_Dates..quarterlyDateTime) : Object + * [~rangedYearDateTime](#Helpers.module_Dates..rangedYearDateTime) : Object * [~timestampDayFormats](#Helpers.module_Dates..timestampDayFormats) : Object * [~timestampMonthFormats](#Helpers.module_Dates..timestampMonthFormats) : Object * [~timestampQuarterFormats](#Helpers.module_Dates..timestampQuarterFormats) : Object * [~timestampTimeFormats](#Helpers.module_Dates..timestampTimeFormats) : Object * [~timestampUTCTimeFormats](#Helpers.module_Dates..timestampUTCTimeFormats) : Object * [~getCurrentDate()](#Helpers.module_Dates..getCurrentDate) ⇒ string \| Date + * [~setEndOfDay(date)](#Helpers.module_Dates..setEndOfDay) ⇒ Date + * [~setStartOfDay(date)](#Helpers.module_Dates..setStartOfDay) ⇒ Date + * [~setEndOfMonth(date)](#Helpers.module_Dates..setEndOfMonth) ⇒ Date * [~setRangedDateTime(params)](#Helpers.module_Dates..setRangedDateTime) ⇒ Object * [~getRangedDateTime(granularity)](#Helpers.module_Dates..getRangedDateTime) ⇒ Object - * [~getRangedMonthDateTime(month)](#Helpers.module_Dates..getRangedMonthDateTime) ⇒ Object \| \* \| undefined + * [~getRangedMonthDateTime(month, defaultLocale)](#Helpers.module_Dates..getRangedMonthDateTime) ⇒ Object \| \* \| undefined + + +### Dates~currentDateTime : Object +Generates the date range, starting at the beginning of getCurrentDate, and ending at the end of getCurrentDate. + +**Kind**: inner constant of [Dates](#Helpers.module_Dates) + + +### Dates~defaultDateTime : Object +Generates the date range, starting 30 days prior to getCurrentDate, and ending at the end of the getCurrentDate. + +**Kind**: inner constant of [Dates](#Helpers.module_Dates) + + +### Dates~weeklyDateTime : Object +Generates the date range, starting on Sunday 12 weeks prior to getCurrentDate, + and ending at the end of the previous Saturday. + +**Kind**: inner constant of [Dates](#Helpers.module_Dates) + + +### Dates~monthlyDateTime : Object +Generates the date range, starting 12 months prior to getCurrentDate, and ending at the end of the getCurrentDate. + +**Kind**: inner constant of [Dates](#Helpers.module_Dates) + + +### Dates~quarterlyDateTime : Object +Generates the date range, starting 36 months prior to getCurrentDate, and ending at the end of getCurrentDate. + +**Kind**: inner constant of [Dates](#Helpers.module_Dates) + + +### Dates~rangedYearDateTime : Object +Generates the date range, starting a year prior, and ending at the end of the previous month. + +**Kind**: inner constant of [Dates](#Helpers.module_Dates) ### Dates~timestampDayFormats : Object @@ -60,6 +106,66 @@ Consistent UTC timestamp time formats. Return a date. **Kind**: inner method of [Dates](#Helpers.module_Dates) + + +### Dates~setEndOfDay(date) ⇒ Date +Sets the UTC time to the end of day. + +**Kind**: inner method of [Dates](#Helpers.module_Dates) +**Returns**: Date - The date with the time set to the last millisecond of that day. + + + + + + + + + + +
ParamTypeDescription
dateDate

The date tp use

+
+ + + +### Dates~setStartOfDay(date) ⇒ Date +Sets UTC time to beginning of the day. + +**Kind**: inner method of [Dates](#Helpers.module_Dates) +**Returns**: Date - Returns the date with the time set to the start of that day. + + + + + + + + + + +
ParamTypeDescription
dateDate

The date tp use

+
+ + + +### Dates~setEndOfMonth(date) ⇒ Date +Sets the UTC date and time to the end of tha month. + +**Kind**: inner method of [Dates](#Helpers.module_Dates) +**Returns**: Date - The date with the date and time set to the last millisecond of that month. + + + + + + + + + + +
ParamTypeDescription
dateDate

The date tp use

+
+ ### Dates~setRangedDateTime(params) ⇒ Object @@ -82,10 +188,7 @@ Set a date range based on a granularity type. params.subtractnumber

Number of granularity type to subtract from the current date.

- params.measurementstring

Granularity type.

- - - params.endOfMeasurementstring

Granularity type.

+ params.measurement'days' | 'weeks' | 'months' | 'years'

Granularity type .

@@ -110,7 +213,7 @@ Return a range of time based on known granularity types. -### Dates~getRangedMonthDateTime(month) ⇒ Object \| \* \| undefined +### Dates~getRangedMonthDateTime(month, defaultLocale) ⇒ Object \| \* \| undefined Generate a list of months for use in a select list. **Kind**: inner method of [Dates](#Helpers.module_Dates) @@ -123,6 +226,8 @@ Generate a list of months for use in a select list. monthstring + + defaultLocalestring diff --git a/src/common/__tests__/__snapshots__/dateHelpers.test.js.snap b/src/common/__tests__/__snapshots__/dateHelpers.test.js.snap index d9db3ebef..59531821d 100644 --- a/src/common/__tests__/__snapshots__/dateHelpers.test.js.snap +++ b/src/common/__tests__/__snapshots__/dateHelpers.test.js.snap @@ -25,7 +25,10 @@ exports[`DateHelpers should have specific functions: dateHelpers 1`] = ` "endDate": 2019-07-31T23:59:59.999Z, "startDate": 2018-08-01T00:00:00.000Z, }, + "setEndOfDay": [Function], + "setEndOfMonth": [Function], "setRangedDateTime": [Function], + "setStartOfDay": [Function], "timestampDayFormats": { "long": "MMMM D", "short": "MMM D", @@ -434,3 +437,10 @@ exports[`DateHelpers should return a predictable range of time: range of time 1` }, } `; + +exports[`DateHelpers should return a predictable range of time: range of time for today 1`] = ` +{ + "endDate": 2019-07-20T23:59:59.999Z, + "startDate": 2019-07-20T00:00:00.000Z, +} +`; diff --git a/src/common/__tests__/dateHelpers.test.js b/src/common/__tests__/dateHelpers.test.js index cb134a1e6..9cdf54c87 100644 --- a/src/common/__tests__/dateHelpers.test.js +++ b/src/common/__tests__/dateHelpers.test.js @@ -31,12 +31,39 @@ describe('DateHelpers', () => { it('should return a predictable range of time', () => { const currentDate = getCurrentDate(); - const rangeDateTime = setRangedDateTime({ date: getCurrentDate(), subtract: 5, measurement: 'days' }); + + expect(dateHelpers.setRangedDateTime()).toMatchSnapshot('range of time for today'); + + const rangeDateTime = setRangedDateTime({ subtract: 5, measurement: 'days' }); expect({ currentDate, rangeDateTime }).toMatchSnapshot('range of time'); + + const expectedProvidedDate = new Date('2023-9-24'); + const rangeDateTimeWithProvidedDate = setRangedDateTime({ + date: expectedProvidedDate, + subtract: 0, + measurement: 'days' + }); + + expect(rangeDateTimeWithProvidedDate).toEqual({ + endDate: new Date(expectedProvidedDate.setUTCHours(23, 59, 59, 999)), + startDate: new Date(expectedProvidedDate.setUTCHours(0, 0, 0, 0)) + }); + + const nonSundayStartDate = new Date(Date.UTC(2023, 9, 24, 18, 6, 8, 3)); + const expectedSundayStartDate = new Date(Date.UTC(2023, 6, 30, 0, 0, 0, 0)); + const expectedSaturdayEndDate = new Date(Date.UTC(2023, 9, 22, 23, 59, 59, 999)); + + expect( + dateHelpers.setRangedDateTime({ + date: nonSundayStartDate, + subtract: 12, + measurement: 'weeks' + }) + ).toEqual({ startDate: expectedSundayStartDate, endDate: expectedSaturdayEndDate }); }); it('should return a predictable range based on granularity', () => { @@ -53,4 +80,25 @@ describe('DateHelpers', () => { expect(getRangedMonthDateTime('april')).toMatchSnapshot('get a specific month by name'); expect(getRangedMonthDateTime(3)).toMatchSnapshot('get a specific month by number'); }); + + it('should change date to end of day', () => { + const startDate = new Date(Date.UTC(2020, 7, 15, 3, 6, 8, 3)); + const expectedDate = new Date(Date.UTC(2020, 7, 15, 23, 59, 59, 999)); + + expect(dateHelpers.setEndOfDay(startDate).getTime()).toEqual(expectedDate.getTime()); + }); + + it('should change date to start of day', () => { + const startDate = new Date(Date.UTC(2020, 7, 15, 3, 6, 8, 3)); + const expectedDate = new Date(Date.UTC(2020, 7, 15, 0, 0, 0, 0)); + + expect(dateHelpers.setStartOfDay(startDate).getTime()).toEqual(expectedDate.getTime()); + }); + + it('should change date to end of Month', () => { + const startDate = new Date(Date.UTC(2020, 7, 15, 3, 6, 8, 3)); + const expectedDate = new Date(Date.UTC(2020, 7, 31, 23, 59, 59, 999)); + + expect(dateHelpers.setEndOfMonth(startDate).getTime()).toEqual(expectedDate.getTime()); + }); }); diff --git a/src/common/dateHelpers.js b/src/common/dateHelpers.js index 49df25db8..70ffa527f 100644 --- a/src/common/dateHelpers.js +++ b/src/common/dateHelpers.js @@ -1,4 +1,3 @@ -import moment from 'moment/moment'; import { helpers } from './helpers'; import { RHSM_API_QUERY_GRANULARITY_TYPES as GRANULARITY_TYPES } from '../services/rhsm/rhsmConstants'; import { translate } from '../components/i18n/i18n'; @@ -13,12 +12,39 @@ import { translate } from '../components/i18n/i18n'; * * @returns {string|Date} */ -const getCurrentDate = () => - (helpers.TEST_MODE && moment.utc('20190720').toDate()) || - (helpers.DEV_MODE && - process.env.REACT_APP_DEBUG_DEFAULT_DATETIME && - moment.utc(process.env.REACT_APP_DEBUG_DEFAULT_DATETIME).toDate()) || - moment.utc().toDate(); +const getCurrentDate = () => { + if (helpers.TEST_MODE) { + return new Date(new Date('2019-07-20').setUTCHours(0, 0, 0, 0)); + } + if (helpers.DEV_MODE && process.env.REACT_APP_DEBUG_DEFAULT_DATETIME) { + return new Date(new Date(process.env.REACT_APP_DEBUG_DEFAULT_DATETIME).setUTCHours(0, 0, 0, 0)); + } + return new Date(); +}; + +/** + * Sets the UTC time to the end of day. + * + * @param {Date} date The date tp use + * @returns {Date} The date with the time set to the last millisecond of that day. + */ +const setEndOfDay = date => new Date(date.setUTCHours(23, 59, 59, 999)); + +/** + * Sets UTC time to beginning of the day. + * + * @param {Date} date The date tp use + * @returns {Date} Returns the date with the time set to the start of that day. + */ +const setStartOfDay = date => new Date(date.setUTCHours(0, 0, 0, 0)); + +/** + *Sets the UTC date and time to the end of tha month. + * + * @param {Date} date The date tp use + * @returns {Date} The date with the date and time set to the last millisecond of that month. + */ +const setEndOfMonth = date => new Date(setEndOfDay(date).setUTCMonth(date.getUTCMonth() + 1, 0)); /** * Set a date range based on a granularity type. @@ -26,25 +52,96 @@ const getCurrentDate = () => * @param {object} params * @param {Date} params.date Start date, typically the current date. * @param {number} params.subtract Number of granularity type to subtract from the current date. - * @param {string} params.measurement Granularity type. - * @param {string} params.endOfMeasurement Granularity type. + * @param {'days' | 'weeks' | 'months' | 'years'} params.measurement Granularity type . * @returns {{endDate: Date, startDate: Date}} */ -const setRangedDateTime = ({ date, subtract, measurement, endOfMeasurement = 'days' }) => ({ - startDate: moment.utc(date).startOf(measurement).subtract(subtract, measurement).toDate(), - endDate: moment.utc(date).startOf(measurement).endOf(endOfMeasurement).toDate() +const setRangedDateTime = ({ date = getCurrentDate(), subtract = 0, measurement = 'days' } = {}) => { + switch (measurement) { + case 'weeks': + return { + startDate: new Date(setStartOfDay(date).setUTCDate(date.getUTCDate() - date.getUTCDay() - subtract * 7)), + endDate: new Date(setEndOfDay(date).setUTCDate(date.getUTCDate() - date.getUTCDay())) + }; + case 'months': + return { + startDate: new Date(setStartOfDay(date).setUTCMonth(date.getUTCMonth() - subtract, 1)), + endDate: new Date(setEndOfDay(date).setUTCDate(1)) + }; + case 'years': + return { + startDate: new Date( + setStartOfDay(date).setUTCFullYear(date.getUTCFullYear() - subtract, date.getUTCMonth() + 1, 1) + ), + endDate: setEndOfMonth(date) + }; + case 'days': + default: + return { + startDate: new Date(setStartOfDay(date).setUTCDate(date.getUTCDate() - subtract)), + endDate: setEndOfDay(date) + }; + } +}; + +/** + * Generates the date range, starting at the beginning of getCurrentDate, and ending at the end of getCurrentDate. + * + * @type {{endDate: Date, startDate: Date}} + */ +const currentDateTime = setRangedDateTime({ + subtract: 1, + measurement: 'days' +}); + +/** + * Generates the date range, starting 30 days prior to getCurrentDate, and ending at the end of the getCurrentDate. + * + * @type {{endDate: Date, startDate: Date}} + */ +const defaultDateTime = setRangedDateTime({ + subtract: 30, + measurement: 'days' +}); + +/** + * Generates the date range, starting on Sunday 12 weeks prior to getCurrentDate, + * and ending at the end of the previous Saturday. + * + * @type {{endDate: Date, startDate: Date}} + */ +const weeklyDateTime = setRangedDateTime({ + subtract: 12, + measurement: 'weeks' }); -const currentDateTime = setRangedDateTime({ date: getCurrentDate(), subtract: 1, measurement: 'days' }); -const defaultDateTime = setRangedDateTime({ date: getCurrentDate(), subtract: 30, measurement: 'days' }); -const weeklyDateTime = setRangedDateTime({ date: getCurrentDate(), subtract: 12, measurement: 'weeks' }); -const monthlyDateTime = setRangedDateTime({ date: getCurrentDate(), subtract: 12, measurement: 'months' }); -const quarterlyDateTime = setRangedDateTime({ date: getCurrentDate(), subtract: 36, measurement: 'months' }); +/** + * Generates the date range, starting 12 months prior to getCurrentDate, and ending at the end of the getCurrentDate. + * + * @type {{endDate: Date, startDate: Date}} + */ +const monthlyDateTime = setRangedDateTime({ + subtract: 12, + measurement: 'months' +}); + +/** + * Generates the date range, starting 36 months prior to getCurrentDate, and ending at the end of getCurrentDate. + * + * @type {{endDate: Date, startDate: Date}} + */ +const quarterlyDateTime = setRangedDateTime({ + subtract: 36, + measurement: 'months' +}); + +/** + * Generates the date range, starting a year prior, and ending at the end of the previous month. + * + * @type {{endDate: Date, startDate: Date}} + */ const rangedYearDateTime = setRangedDateTime({ - date: getCurrentDate(), - subtract: 11, - measurement: 'months', - endOfMeasurement: 'months' + subtract: 1, + measurement: 'years' }); /** @@ -73,34 +170,38 @@ const getRangedDateTime = granularity => { * Generate a list of months for use in a select list. * * @param {string} month + * @param {string} defaultLocale * @returns {{keyDateTimeRanges: {}, listDateTimeRanges: Array}|*|undefined} */ -const getRangedMonthDateTime = month => { - const currentYear = Number.parseInt(moment.utc(getCurrentDate()).year(), 10); +const getRangedMonthDateTime = (month, defaultLocale = helpers.UI_LOCALE_DEFAULT) => { + const currentYear = getCurrentDate().getUTCFullYear(); const { startDate, endDate } = { ...rangedYearDateTime }; const keyDateTimeRanges = {}; let listDateTimeRanges = []; - const startDateUpdated = moment.utc(startDate); - const endDateUpdated = moment.utc(endDate); + const startDateUpdated = new Date(startDate); + const endDateUpdated = new Date(endDate); - while (endDateUpdated > startDateUpdated || startDateUpdated.format('M') === endDateUpdated.format('M')) { + while (endDateUpdated > startDateUpdated || startDateUpdated.getUTCMonth() === endDateUpdated.getUTCMonth()) { const dateTime = { value: { - startDate: startDateUpdated.toDate() + startDate: new Date(setStartOfDay(startDateUpdated)) } }; - const titleYear = startDateUpdated.format('MMMM YYYY'); - const title = startDateUpdated.format('MMMM'); - const titleIndex = startDateUpdated.format('M'); - const isNextYear = currentYear !== Number.parseInt(startDateUpdated.year(), 10); - + const titleYear = startDateUpdated.toLocaleString(defaultLocale, { + month: 'long', + year: 'numeric', + timeZone: 'UTC' + }); + const title = startDateUpdated.toLocaleString(defaultLocale, { month: 'long', timeZone: 'UTC' }); + const titleIndex = startDateUpdated.toLocaleString(defaultLocale, { month: 'numeric', timeZone: 'UTC' }); + const isNextYear = currentYear !== startDateUpdated.getUTCFullYear(); dateTime.title = (isNextYear && titleYear) || title; dateTime._title = title.toLowerCase(); - dateTime.value.endDate = moment.utc(startDateUpdated).endOf('month').toDate(); + dateTime.value.endDate = setEndOfMonth(startDateUpdated); - startDateUpdated.add(1, 'month'); + startDateUpdated.setUTCMonth(startDateUpdated.getUTCMonth() + 1); dateTime.title = translate('curiosity-toolbar.label', { context: ['granularityRangedMonthly', dateTime.title] }); keyDateTimeRanges[title.toLowerCase()] = { ...dateTime }; @@ -184,6 +285,9 @@ const timestampUTCTimeFormats = { const dateHelpers = { getCurrentDate, + setStartOfDay, + setEndOfDay, + setEndOfMonth, getRangedMonthDateTime, getRangedDateTime, setRangedDateTime, @@ -203,6 +307,9 @@ const dateHelpers = { export { dateHelpers as default, getCurrentDate, + setStartOfDay, + setEndOfDay, + setEndOfMonth, getRangedMonthDateTime, getRangedDateTime, setRangedDateTime,