Commit 772ee696 authored by Nathan Friend's avatar Nathan Friend Committed by Jose Ivan Vargas

Add UTC support to date add/subtract utilities

This commit update all date addition/subtraction utilities to optionally
support perfoming the calculation with UTC dates, which ignores
Daylight Saving Time.
parent b1a936ba
...@@ -676,69 +676,127 @@ export const secondsToHours = (offset) => { ...@@ -676,69 +676,127 @@ export const secondsToHours = (offset) => {
}; };
/** /**
* Returns the date n days after the date provided * Returns the date `n` days after the date provided
* *
* @param {Date} date the initial date * @param {Date} date the initial date
* @param {Number} numberOfDays number of days after * @param {Number} numberOfDays number of days after
* @return {Date} the date following the date provided * @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Perform the calculation using UTC dates.
* This will cause Daylight Saving Time to be ignored. Defaults to `false`
* if not provided, which causes the calculation to be performed in the
* user's timezone.
*
* @return {Date} A `Date` object `n` days after the provided `Date`
*/ */
export const nDaysAfter = (date, numberOfDays) => export const nDaysAfter = (date, numberOfDays, { utc = false } = {}) => {
new Date(newDate(date)).setDate(date.getDate() + numberOfDays); const clone = newDate(date);
const cloneValue = utc
? clone.setUTCDate(date.getUTCDate() + numberOfDays)
: clone.setDate(date.getDate() + numberOfDays);
return new Date(cloneValue);
};
/** /**
* Returns the date n days before the date provided * Returns the date `n` days before the date provided
* *
* @param {Date} date the initial date * @param {Date} date the initial date
* @param {Number} numberOfDays number of days before * @param {Number} numberOfDays number of days before
* @return {Date} the date preceding the date provided * @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Perform the calculation using UTC dates.
* This will cause Daylight Saving Time to be ignored. Defaults to `false`
* if not provided, which causes the calculation to be performed in the
* user's timezone.
* @return {Date} A `Date` object `n` days before the provided `Date`
*/ */
export const nDaysBefore = (date, numberOfDays) => nDaysAfter(date, -numberOfDays); export const nDaysBefore = (date, numberOfDays, options) =>
nDaysAfter(date, -numberOfDays, options);
/** /**
* Returns the date n weeks after the date provided * Returns the date `n` weeks after the date provided
* *
* @param {Date} date the initial date * @param {Date} date the initial date
* @param {Number} numberOfWeeks number of weeks after * @param {Number} numberOfWeeks number of weeks after
* @return {Date} the date following the date provided * @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Perform the calculation using UTC dates.
* This will cause Daylight Saving Time to be ignored. Defaults to `false`
* if not provided, which causes the calculation to be performed in the
* user's timezone.
*
* @return {Date} A `Date` object `n` weeks after the provided `Date`
*/ */
export const nWeeksAfter = (date, numberOfWeeks) => export const nWeeksAfter = (date, numberOfWeeks, options) =>
new Date(newDate(date)).setDate(date.getDate() + DAYS_IN_WEEK * numberOfWeeks); nDaysAfter(date, DAYS_IN_WEEK * numberOfWeeks, options);
/** /**
* Returns the date n weeks before the date provided * Returns the date `n` weeks before the date provided
* *
* @param {Date} date the initial date * @param {Date} date the initial date
* @param {Number} numberOfWeeks number of weeks before * @param {Number} numberOfWeeks number of weeks before
* @return {Date} the date following the date provided * @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Perform the calculation using UTC dates.
* This will cause Daylight Saving Time to be ignored. Defaults to `false`
* if not provided, which causes the calculation to be performed in the
* user's timezone.
*
* @return {Date} A `Date` object `n` weeks before the provided `Date`
*/ */
export const nWeeksBefore = (date, numberOfWeeks) => nWeeksAfter(date, -numberOfWeeks); export const nWeeksBefore = (date, numberOfWeeks, options) =>
nWeeksAfter(date, -numberOfWeeks, options);
/** /**
* Returns the date n months after the date provided * Returns the date `n` months after the date provided
* *
* @param {Date} date the initial date * @param {Date} date the initial date
* @param {Number} numberOfMonths number of months after * @param {Number} numberOfMonths number of months after
* @return {Date} the date following the date provided * @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Perform the calculation using UTC dates.
* This will cause Daylight Saving Time to be ignored. Defaults to `false`
* if not provided, which causes the calculation to be performed in the
* user's timezone.
*
* @return {Date} A `Date` object `n` months after the provided `Date`
*/ */
export const nMonthsAfter = (date, numberOfMonths) => export const nMonthsAfter = (date, numberOfMonths, { utc = false } = {}) => {
new Date(newDate(date)).setMonth(date.getMonth() + numberOfMonths); const clone = newDate(date);
const cloneValue = utc
? clone.setUTCMonth(date.getUTCMonth() + numberOfMonths)
: clone.setMonth(date.getMonth() + numberOfMonths);
return new Date(cloneValue);
};
/** /**
* Returns the date n months before the date provided * Returns the date `n` months before the date provided
* *
* @param {Date} date the initial date * @param {Date} date the initial date
* @param {Number} numberOfMonths number of months before * @param {Number} numberOfMonths number of months before
* @return {Date} the date preceding the date provided * @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Perform the calculation using UTC dates.
* This will cause Daylight Saving Time to be ignored. Defaults to `false`
* if not provided, which causes the calculation to be performed in the
* user's timezone.
*
* @return {Date} A `Date` object `n` months before the provided `Date`
*/ */
export const nMonthsBefore = (date, numberOfMonths) => nMonthsAfter(date, -numberOfMonths); export const nMonthsBefore = (date, numberOfMonths, options) =>
nMonthsAfter(date, -numberOfMonths, options);
/** /**
* Returns the date after the date provided * Returns the date after the date provided
* *
* @param {Date} date the initial date * @param {Date} date the initial date
* @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Perform the calculation using UTC dates.
* This will cause Daylight Saving Time to be ignored. Defaults to `false`
* if not provided, which causes the calculation to be performed in the
* user's timezone.
*
* @return {Date} the date following the date provided * @return {Date} the date following the date provided
*/ */
export const dayAfter = (date) => new Date(newDate(date).setDate(date.getDate() + 1)); export const dayAfter = (date, options) => nDaysAfter(date, 1, options);
/** /**
* Mimics the behaviour of the rails distance_of_time_in_words function * Mimics the behaviour of the rails distance_of_time_in_words function
...@@ -933,3 +991,22 @@ export const isToday = (date) => { ...@@ -933,3 +991,22 @@ export const isToday = (date) => {
date.getFullYear() === today.getFullYear() date.getFullYear() === today.getFullYear()
); );
}; };
/**
* Returns the start of the provided day
*
* @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Perform the calculation using UTC time.
* If `true`, the time returned will be midnight UTC. If `false` (the default)
* the time returned will be midnight in the user's local time.
*
* @returns {Date} A new `Date` object that represents the start of the day
* of the provided date
*/
export const getStartOfDay = (date, { utc = false } = {}) => {
const clone = newDate(date);
const cloneValue = utc ? clone.setUTCHours(0, 0, 0, 0) : clone.setHours(0, 0, 0, 0);
return new Date(cloneValue);
};
...@@ -76,7 +76,7 @@ export default { ...@@ -76,7 +76,7 @@ export default {
query: getShiftsForRotations, query: getShiftsForRotations,
variables() { variables() {
const startsAt = this.timeframeStartDate; const startsAt = this.timeframeStartDate;
const endsAt = new Date(nWeeksAfter(startsAt, 2)); const endsAt = nWeeksAfter(startsAt, 2);
return { return {
projectPath: this.projectPath, projectPath: this.projectPath,
...@@ -116,7 +116,7 @@ export default { ...@@ -116,7 +116,7 @@ export default {
case PRESET_TYPES.WEEKS: { case PRESET_TYPES.WEEKS: {
const firstDayOfTheLastWeek = this.timeframe[this.timeframe.length - 1]; const firstDayOfTheLastWeek = this.timeframe[this.timeframe.length - 1];
const firstDayOfTheNextTimeframe = nWeeksAfter(firstDayOfTheLastWeek, 1); const firstDayOfTheNextTimeframe = nWeeksAfter(firstDayOfTheLastWeek, 1);
const lastDayOfTimeframe = nDaysBefore(new Date(firstDayOfTheNextTimeframe), 1); const lastDayOfTimeframe = nDaysBefore(firstDayOfTheNextTimeframe, 1);
return `${formatDate(this.timeframe[0], 'mmmm d')} - ${formatDate( return `${formatDate(this.timeframe[0], 'mmmm d')} - ${formatDate(
lastDayOfTimeframe, lastDayOfTimeframe,
...@@ -142,10 +142,10 @@ export default { ...@@ -142,10 +142,10 @@ export default {
updateToViewPreviousTimeframe() { updateToViewPreviousTimeframe() {
switch (this.presetType) { switch (this.presetType) {
case PRESET_TYPES.DAYS: case PRESET_TYPES.DAYS:
this.timeframeStartDate = new Date(nDaysBefore(this.timeframeStartDate, 1)); this.timeframeStartDate = nDaysBefore(this.timeframeStartDate, 1);
break; break;
case PRESET_TYPES.WEEKS: case PRESET_TYPES.WEEKS:
this.timeframeStartDate = new Date(nWeeksBefore(this.timeframeStartDate, 2)); this.timeframeStartDate = nWeeksBefore(this.timeframeStartDate, 2);
break; break;
default: default:
break; break;
...@@ -154,10 +154,10 @@ export default { ...@@ -154,10 +154,10 @@ export default {
updateToViewNextTimeframe() { updateToViewNextTimeframe() {
switch (this.presetType) { switch (this.presetType) {
case PRESET_TYPES.DAYS: case PRESET_TYPES.DAYS:
this.timeframeStartDate = new Date(nDaysAfter(this.timeframeStartDate, 1)); this.timeframeStartDate = nDaysAfter(this.timeframeStartDate, 1);
break; break;
case PRESET_TYPES.WEEKS: case PRESET_TYPES.WEEKS:
this.timeframeStartDate = new Date(nWeeksAfter(this.timeframeStartDate, 2)); this.timeframeStartDate = nWeeksAfter(this.timeframeStartDate, 2);
break; break;
default: default:
break; break;
......
...@@ -35,7 +35,7 @@ export default { ...@@ -35,7 +35,7 @@ export default {
}, },
computed: { computed: {
currentTimeframeEndsAt() { currentTimeframeEndsAt() {
return new Date(nDaysAfter(this.timeframeItem, 1)); return nDaysAfter(this.timeframeItem, 1);
}, },
hoursUntilEndOfTimeFrame() { hoursUntilEndOfTimeFrame() {
return HOURS_IN_DAY - new Date(this.shiftRangeOverlap.overlapStartDate).getHours(); return HOURS_IN_DAY - new Date(this.shiftRangeOverlap.overlapStartDate).getHours();
......
...@@ -35,7 +35,7 @@ export default { ...@@ -35,7 +35,7 @@ export default {
}, },
computed: { computed: {
currentTimeframeEndsAt() { currentTimeframeEndsAt() {
return new Date(nDaysAfter(this.timeframeItem, DAYS_IN_DATE_WEEK)); return nDaysAfter(this.timeframeItem, DAYS_IN_DATE_WEEK);
}, },
daysUntilEndOfTimeFrame() { daysUntilEndOfTimeFrame() {
return ( return (
......
...@@ -6,9 +6,9 @@ import { LAST_WEEK, LAST_MONTH, LAST_90_DAYS } from './constants'; ...@@ -6,9 +6,9 @@ import { LAST_WEEK, LAST_MONTH, LAST_90_DAYS } from './constants';
// Compute all relative dates based on the _beginning_ of today // Compute all relative dates based on the _beginning_ of today
const startOfToday = new Date(new Date().setHours(0, 0, 0, 0)); const startOfToday = new Date(new Date().setHours(0, 0, 0, 0));
const lastWeek = new Date(nDaysBefore(startOfToday, 7)); const lastWeek = nDaysBefore(startOfToday, 7);
const lastMonth = new Date(nMonthsBefore(startOfToday, 1)); const lastMonth = nMonthsBefore(startOfToday, 1);
const last90Days = new Date(nDaysBefore(startOfToday, 90)); const last90Days = nDaysBefore(startOfToday, 90);
const apiDateFormatString = 'isoDateTime'; const apiDateFormatString = 'isoDateTime';
const titleDateFormatString = 'mmm d'; const titleDateFormatString = 'mmm d';
const sharedRequestParams = { const sharedRequestParams = {
......
...@@ -584,22 +584,6 @@ describe('secondsToMilliseconds', () => { ...@@ -584,22 +584,6 @@ describe('secondsToMilliseconds', () => {
}); });
}); });
describe('dayAfter', () => {
const date = new Date('2019-07-16T00:00:00.000Z');
it('returns the following date', () => {
const nextDay = datetimeUtility.dayAfter(date);
const expectedNextDate = new Date('2019-07-17T00:00:00.000Z');
expect(nextDay).toStrictEqual(expectedNextDate);
});
it('does not modifiy the original date', () => {
datetimeUtility.dayAfter(date);
expect(date).toStrictEqual(new Date('2019-07-16T00:00:00.000Z'));
});
});
describe('secondsToDays', () => { describe('secondsToDays', () => {
it('converts seconds to days correctly', () => { it('converts seconds to days correctly', () => {
expect(datetimeUtility.secondsToDays(0)).toBe(0); expect(datetimeUtility.secondsToDays(0)).toBe(0);
...@@ -608,132 +592,214 @@ describe('secondsToDays', () => { ...@@ -608,132 +592,214 @@ describe('secondsToDays', () => {
}); });
}); });
describe('nDaysAfter', () => { describe('date addition/subtraction methods', () => {
const date = new Date('2019-07-16T00:00:00.000Z'); beforeEach(() => {
timezoneMock.register('US/Eastern');
});
afterEach(() => {
timezoneMock.unregister();
});
describe('dayAfter', () => {
const input = '2019-03-10T00:00:00.000Z';
const expectedLocalResult = '2019-03-10T23:00:00.000Z';
const expectedUTCResult = '2019-03-11T00:00:00.000Z';
it.each` it.each`
numberOfDays | expectedResult inputAsString | options | expectedAsString
${1} | ${new Date('2019-07-17T00:00:00.000Z').valueOf()} ${input} | ${undefined} | ${expectedLocalResult}
${90} | ${new Date('2019-10-14T00:00:00.000Z').valueOf()} ${input} | ${{}} | ${expectedLocalResult}
${-1} | ${new Date('2019-07-15T00:00:00.000Z').valueOf()} ${input} | ${{ utc: false }} | ${expectedLocalResult}
${0} | ${date.valueOf()} ${input} | ${{ utc: true }} | ${expectedUTCResult}
${0.9} | ${date.valueOf()}
`( `(
'returns the date $numberOfDays day(s) after the provided date', 'when the provided date is $inputAsString and the options parameter is $options, returns $expectedAsString',
({ numberOfDays, expectedResult }) => { ({ inputAsString, options, expectedAsString }) => {
expect(datetimeUtility.nDaysAfter(date, numberOfDays)).toBe(expectedResult); const inputDate = new Date(inputAsString);
const actual = datetimeUtility.dayAfter(inputDate, options);
expect(actual.toISOString()).toBe(expectedAsString);
}, },
); );
});
describe('nDaysBefore', () => { it('does not modifiy the original date', () => {
const date = new Date('2019-07-16T00:00:00.000Z'); const inputDate = new Date(input);
datetimeUtility.dayAfter(inputDate);
expect(inputDate.toISOString()).toBe(input);
});
});
describe('nDaysAfter', () => {
const input = '2019-07-16T00:00:00.000Z';
it.each` it.each`
numberOfDays | expectedResult inputAsString | numberOfDays | options | expectedAsString
${1} | ${new Date('2019-07-15T00:00:00.000Z').valueOf()} ${input} | ${1} | ${undefined} | ${'2019-07-17T00:00:00.000Z'}
${90} | ${new Date('2019-04-17T00:00:00.000Z').valueOf()} ${input} | ${-1} | ${undefined} | ${'2019-07-15T00:00:00.000Z'}
${-1} | ${new Date('2019-07-17T00:00:00.000Z').valueOf()} ${input} | ${0} | ${undefined} | ${'2019-07-16T00:00:00.000Z'}
${0} | ${date.valueOf()} ${input} | ${0.9} | ${undefined} | ${'2019-07-16T00:00:00.000Z'}
${0.9} | ${new Date('2019-07-15T00:00:00.000Z').valueOf()} ${input} | ${120} | ${undefined} | ${'2019-11-13T01:00:00.000Z'}
${input} | ${120} | ${{}} | ${'2019-11-13T01:00:00.000Z'}
${input} | ${120} | ${{ utc: false }} | ${'2019-11-13T01:00:00.000Z'}
${input} | ${120} | ${{ utc: true }} | ${'2019-11-13T00:00:00.000Z'}
`( `(
'returns the date $numberOfDays day(s) before the provided date', 'when the provided date is $inputAsString, numberOfDays is $numberOfDays, and the options parameter is $options, returns $expectedAsString',
({ numberOfDays, expectedResult }) => { ({ inputAsString, numberOfDays, options, expectedAsString }) => {
expect(datetimeUtility.nDaysBefore(date, numberOfDays)).toBe(expectedResult); const inputDate = new Date(inputAsString);
const actual = datetimeUtility.nDaysAfter(inputDate, numberOfDays, options);
expect(actual.toISOString()).toBe(expectedAsString);
}, },
); );
}); });
describe('nWeeksAfter', () => { describe('nDaysBefore', () => {
const date = new Date('2021-07-16T00:00:00.000Z'); const input = '2019-07-16T00:00:00.000Z';
it.each` it.each`
numberOfWeeks | expectedResult inputAsString | numberOfDays | options | expectedAsString
${1} | ${new Date('2021-07-23T00:00:00.000Z').valueOf()} ${input} | ${1} | ${undefined} | ${'2019-07-15T00:00:00.000Z'}
${3} | ${new Date('2021-08-06T00:00:00.000Z').valueOf()} ${input} | ${-1} | ${undefined} | ${'2019-07-17T00:00:00.000Z'}
${-1} | ${new Date('2021-07-09T00:00:00.000Z').valueOf()} ${input} | ${0} | ${undefined} | ${'2019-07-16T00:00:00.000Z'}
${0} | ${date.valueOf()} ${input} | ${0.9} | ${undefined} | ${'2019-07-15T00:00:00.000Z'}
${0.6} | ${new Date('2021-07-20T00:00:00.000Z').valueOf()} ${input} | ${180} | ${undefined} | ${'2019-01-17T01:00:00.000Z'}
${input} | ${180} | ${{}} | ${'2019-01-17T01:00:00.000Z'}
${input} | ${180} | ${{ utc: false }} | ${'2019-01-17T01:00:00.000Z'}
${input} | ${180} | ${{ utc: true }} | ${'2019-01-17T00:00:00.000Z'}
`( `(
'returns the date $numberOfWeeks week(s) after the provided date', 'when the provided date is $inputAsString, numberOfDays is $numberOfDays, and the options parameter is $options, returns $expectedAsString',
({ numberOfWeeks, expectedResult }) => { ({ inputAsString, numberOfDays, options, expectedAsString }) => {
expect(datetimeUtility.nWeeksAfter(date, numberOfWeeks)).toBe(expectedResult); const inputDate = new Date(inputAsString);
const actual = datetimeUtility.nDaysBefore(inputDate, numberOfDays, options);
expect(actual.toISOString()).toBe(expectedAsString);
}, },
); );
}); });
describe('nWeeksBefore', () => { describe('nWeeksAfter', () => {
const date = new Date('2021-07-16T00:00:00.000Z'); const input = '2021-07-16T00:00:00.000Z';
it.each` it.each`
numberOfWeeks | expectedResult inputAsString | numberOfWeeks | options | expectedAsString
${1} | ${new Date('2021-07-09T00:00:00.000Z').valueOf()} ${input} | ${1} | ${undefined} | ${'2021-07-23T00:00:00.000Z'}
${3} | ${new Date('2021-06-25T00:00:00.000Z').valueOf()} ${input} | ${3} | ${undefined} | ${'2021-08-06T00:00:00.000Z'}
${-1} | ${new Date('2021-07-23T00:00:00.000Z').valueOf()} ${input} | ${-1} | ${undefined} | ${'2021-07-09T00:00:00.000Z'}
${0} | ${date.valueOf()} ${input} | ${0} | ${undefined} | ${'2021-07-16T00:00:00.000Z'}
${0.6} | ${new Date('2021-07-11T00:00:00.000Z').valueOf()} ${input} | ${0.6} | ${undefined} | ${'2021-07-20T00:00:00.000Z'}
${input} | ${18} | ${undefined} | ${'2021-11-19T01:00:00.000Z'}
${input} | ${18} | ${{}} | ${'2021-11-19T01:00:00.000Z'}
${input} | ${18} | ${{ utc: false }} | ${'2021-11-19T01:00:00.000Z'}
${input} | ${18} | ${{ utc: true }} | ${'2021-11-19T00:00:00.000Z'}
`( `(
'returns the date $numberOfWeeks week(s) before the provided date', 'when the provided date is $inputAsString, numberOfWeeks is $numberOfWeeks, and the options parameter is $options, returns $expectedAsString',
({ numberOfWeeks, expectedResult }) => { ({ inputAsString, numberOfWeeks, options, expectedAsString }) => {
expect(datetimeUtility.nWeeksBefore(date, numberOfWeeks)).toBe(expectedResult); const inputDate = new Date(inputAsString);
const actual = datetimeUtility.nWeeksAfter(inputDate, numberOfWeeks, options);
expect(actual.toISOString()).toBe(expectedAsString);
}, },
); );
}); });
describe('nWeeksBefore', () => {
const input = '2021-07-16T00:00:00.000Z';
describe('nMonthsAfter', () => { it.each`
inputAsString | numberOfWeeks | options | expectedAsString
${input} | ${1} | ${undefined} | ${'2021-07-09T00:00:00.000Z'}
${input} | ${3} | ${undefined} | ${'2021-06-25T00:00:00.000Z'}
${input} | ${-1} | ${undefined} | ${'2021-07-23T00:00:00.000Z'}
${input} | ${0} | ${undefined} | ${'2021-07-16T00:00:00.000Z'}
${input} | ${0.6} | ${undefined} | ${'2021-07-11T00:00:00.000Z'}
${input} | ${20} | ${undefined} | ${'2021-02-26T01:00:00.000Z'}
${input} | ${20} | ${{}} | ${'2021-02-26T01:00:00.000Z'}
${input} | ${20} | ${{ utc: false }} | ${'2021-02-26T01:00:00.000Z'}
${input} | ${20} | ${{ utc: true }} | ${'2021-02-26T00:00:00.000Z'}
`(
'when the provided date is $inputAsString, numberOfWeeks is $numberOfWeeks, and the options parameter is $options, returns $expectedAsString',
({ inputAsString, numberOfWeeks, options, expectedAsString }) => {
const inputDate = new Date(inputAsString);
const actual = datetimeUtility.nWeeksBefore(inputDate, numberOfWeeks, options);
expect(actual.toISOString()).toBe(expectedAsString);
},
);
});
describe('nMonthsAfter', () => {
// February has 28 days // February has 28 days
const feb2019 = new Date('2019-02-15T00:00:00.000Z'); const feb2019 = '2019-02-15T00:00:00.000Z';
// Except in 2020, it had 29 days // Except in 2020, it had 29 days
const feb2020 = new Date('2020-02-15T00:00:00.000Z'); const feb2020 = '2020-02-15T00:00:00.000Z';
// April has 30 days // April has 30 days
const apr2020 = new Date('2020-04-15T00:00:00.000Z'); const apr2020 = '2020-04-15T00:00:00.000Z';
// May has 31 days // May has 31 days
const may2020 = new Date('2020-05-15T00:00:00.000Z'); const may2020 = '2020-05-15T00:00:00.000Z';
// November 1, 2020 was the day Daylight Saving Time ended in 2020 (in the US)
const oct2020 = '2020-10-15T00:00:00.000Z';
it.each` it.each`
date | numberOfMonths | expectedResult inputAsString | numberOfMonths | options | expectedAsString
${feb2019} | ${1} | ${new Date('2019-03-15T00:00:00.000Z').valueOf()} ${feb2019} | ${1} | ${undefined} | ${'2019-03-14T23:00:00.000Z'}
${feb2020} | ${1} | ${new Date('2020-03-15T00:00:00.000Z').valueOf()} ${feb2020} | ${1} | ${undefined} | ${'2020-03-14T23:00:00.000Z'}
${apr2020} | ${1} | ${new Date('2020-05-15T00:00:00.000Z').valueOf()} ${apr2020} | ${1} | ${undefined} | ${'2020-05-15T00:00:00.000Z'}
${may2020} | ${1} | ${new Date('2020-06-15T00:00:00.000Z').valueOf()} ${may2020} | ${1} | ${undefined} | ${'2020-06-15T00:00:00.000Z'}
${may2020} | ${12} | ${new Date('2021-05-15T00:00:00.000Z').valueOf()} ${may2020} | ${12} | ${undefined} | ${'2021-05-15T00:00:00.000Z'}
${may2020} | ${-1} | ${new Date('2020-04-15T00:00:00.000Z').valueOf()} ${may2020} | ${-1} | ${undefined} | ${'2020-04-15T00:00:00.000Z'}
${may2020} | ${0} | ${may2020.valueOf()} ${may2020} | ${0} | ${undefined} | ${may2020}
${may2020} | ${0.9} | ${may2020.valueOf()} ${may2020} | ${0.9} | ${undefined} | ${may2020}
${oct2020} | ${1} | ${undefined} | ${'2020-11-15T01:00:00.000Z'}
${oct2020} | ${1} | ${{}} | ${'2020-11-15T01:00:00.000Z'}
${oct2020} | ${1} | ${{ utc: false }} | ${'2020-11-15T01:00:00.000Z'}
${oct2020} | ${1} | ${{ utc: true }} | ${'2020-11-15T00:00:00.000Z'}
`( `(
'returns the date $numberOfMonths month(s) after the provided date', 'when the provided date is $inputAsString, numberOfMonths is $numberOfMonths, and the options parameter is $options, returns $expectedAsString',
({ date, numberOfMonths, expectedResult }) => { ({ inputAsString, numberOfMonths, options, expectedAsString }) => {
expect(datetimeUtility.nMonthsAfter(date, numberOfMonths)).toBe(expectedResult); const inputDate = new Date(inputAsString);
const actual = datetimeUtility.nMonthsAfter(inputDate, numberOfMonths, options);
expect(actual.toISOString()).toBe(expectedAsString);
}, },
); );
}); });
describe('nMonthsBefore', () => { describe('nMonthsBefore', () => {
// The previous month (February) has 28 days // The previous month (February) has 28 days
const march2019 = new Date('2019-03-15T00:00:00.000Z'); const march2019 = '2019-03-15T00:00:00.000Z';
// Except in 2020, it had 29 days // Except in 2020, it had 29 days
const march2020 = new Date('2020-03-15T00:00:00.000Z'); const march2020 = '2020-03-15T00:00:00.000Z';
// The previous month (April) has 30 days // The previous month (April) has 30 days
const may2020 = new Date('2020-05-15T00:00:00.000Z'); const may2020 = '2020-05-15T00:00:00.000Z';
// The previous month (May) has 31 days // The previous month (May) has 31 days
const june2020 = new Date('2020-06-15T00:00:00.000Z'); const june2020 = '2020-06-15T00:00:00.000Z';
// November 1, 2020 was the day Daylight Saving Time ended in 2020 (in the US)
const nov2020 = '2020-11-15T00:00:00.000Z';
it.each` it.each`
date | numberOfMonths | expectedResult inputAsString | numberOfMonths | options | expectedAsString
${march2019} | ${1} | ${new Date('2019-02-15T00:00:00.000Z').valueOf()} ${march2019} | ${1} | ${undefined} | ${'2019-02-15T01:00:00.000Z'}
${march2020} | ${1} | ${new Date('2020-02-15T00:00:00.000Z').valueOf()} ${march2020} | ${1} | ${undefined} | ${'2020-02-15T01:00:00.000Z'}
${may2020} | ${1} | ${new Date('2020-04-15T00:00:00.000Z').valueOf()} ${may2020} | ${1} | ${undefined} | ${'2020-04-15T00:00:00.000Z'}
${june2020} | ${1} | ${new Date('2020-05-15T00:00:00.000Z').valueOf()} ${june2020} | ${1} | ${undefined} | ${'2020-05-15T00:00:00.000Z'}
${june2020} | ${12} | ${new Date('2019-06-15T00:00:00.000Z').valueOf()} ${june2020} | ${12} | ${undefined} | ${'2019-06-15T00:00:00.000Z'}
${june2020} | ${-1} | ${new Date('2020-07-15T00:00:00.000Z').valueOf()} ${june2020} | ${-1} | ${undefined} | ${'2020-07-15T00:00:00.000Z'}
${june2020} | ${0} | ${june2020.valueOf()} ${june2020} | ${0} | ${undefined} | ${june2020}
${june2020} | ${0.9} | ${new Date('2020-05-15T00:00:00.000Z').valueOf()} ${june2020} | ${0.9} | ${undefined} | ${'2020-05-15T00:00:00.000Z'}
${nov2020} | ${1} | ${undefined} | ${'2020-10-14T23:00:00.000Z'}
${nov2020} | ${1} | ${{}} | ${'2020-10-14T23:00:00.000Z'}
${nov2020} | ${1} | ${{ utc: false }} | ${'2020-10-14T23:00:00.000Z'}
${nov2020} | ${1} | ${{ utc: true }} | ${'2020-10-15T00:00:00.000Z'}
`( `(
'returns the date $numberOfMonths month(s) before the provided date', 'when the provided date is $inputAsString, numberOfMonths is $numberOfMonths, and the options parameter is $options, returns $expectedAsString',
({ date, numberOfMonths, expectedResult }) => { ({ inputAsString, numberOfMonths, options, expectedAsString }) => {
expect(datetimeUtility.nMonthsBefore(date, numberOfMonths)).toBe(expectedResult); const inputDate = new Date(inputAsString);
const actual = datetimeUtility.nMonthsBefore(inputDate, numberOfMonths, options);
expect(actual.toISOString()).toBe(expectedAsString);
}, },
); );
});
}); });
describe('approximateDuration', () => { describe('approximateDuration', () => {
...@@ -951,3 +1017,32 @@ describe('isToday', () => { ...@@ -951,3 +1017,32 @@ describe('isToday', () => {
expect(datetimeUtility.isToday(date)).toBe(expected); expect(datetimeUtility.isToday(date)).toBe(expected);
}); });
}); });
describe('getStartOfDay', () => {
beforeEach(() => {
timezoneMock.register('US/Eastern');
});
afterEach(() => {
timezoneMock.unregister();
});
it.each`
inputAsString | options | expectedAsString
${'2021-01-29T18:08:23.014Z'} | ${undefined} | ${'2021-01-29T05:00:00.000Z'}
${'2021-01-29T13:08:23.014-05:00'} | ${undefined} | ${'2021-01-29T05:00:00.000Z'}
${'2021-01-30T03:08:23.014+09:00'} | ${undefined} | ${'2021-01-29T05:00:00.000Z'}
${'2021-01-28T18:08:23.014-10:00'} | ${undefined} | ${'2021-01-28T05:00:00.000Z'}
${'2021-01-28T18:08:23.014-10:00'} | ${{}} | ${'2021-01-28T05:00:00.000Z'}
${'2021-01-28T18:08:23.014-10:00'} | ${{ utc: false }} | ${'2021-01-28T05:00:00.000Z'}
${'2021-01-28T18:08:23.014-10:00'} | ${{ utc: true }} | ${'2021-01-29T00:00:00.000Z'}
`(
'when the provided date is $inputAsString and the options parameter is $options, returns $expectedAsString',
({ inputAsString, options, expectedAsString }) => {
const inputDate = new Date(inputAsString);
const actual = datetimeUtility.getStartOfDay(inputDate, options);
expect(actual.toISOString()).toEqual(expectedAsString);
},
);
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment