diff --git a/README.md b/README.md index 7ad4e49..c19d56b 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,13 @@ dt.isHoliday('middle-america', {some: 'stuff'}); +## Typedefs + +
DateTime
](#DateTime)
@@ -264,6 +271,7 @@ dt.isHoliday('middle-america', {some: 'stuff'});
* [.isBusinessDay()](#DateTime+isBusinessDay) ⇒ boolean
* [.plusBusiness([days])](#DateTime+plusBusiness) ⇒ [DateTime
](#DateTime)
* [.minusBusiness([days])](#DateTime+minusBusiness) ⇒ [DateTime
](#DateTime)
+ * [.diffBusiness(targetDate, config)](#DateTime+diffBusiness) ⇒ number
@@ -362,6 +370,18 @@ Subtracts business days to an existing DateTime instance.
| --- | --- | --- | --- |
| [days] | number
| 1
| The number of business days to subtract. |
+
+
+### dateTime.diffBusiness(targetDate, config) ⇒ number
+Returns the difference in business days.
+
+**Kind**: instance method of [DateTime
](#DateTime)
+
+| Param | Type |
+| --- | --- |
+| targetDate | [DateTime
](#DateTime) |
+| config | [DiffBusinessConfig
](#DiffBusinessConfig) |
+
## getEasterMonthAndDay(year) ⇒ Array.<number>
@@ -374,3 +394,14 @@ Returns the month and day of Easter for a given year.
| --- | --- |
| year | number
|
+
+
+## DiffBusinessConfig
+**Kind**: global typedef
+**Properties**
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| [includeEndDate] | boolean
| false
| include the end date in the calculation |
+| [relative] | boolean
| false
| signs the return value as negative if end date is in the past |
+
diff --git a/src/index.js b/src/index.js
index 1aaf0ed..5f040e1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -146,4 +146,50 @@ DateTime.prototype.minusBusiness = function({ days = ONE_DAY } = {}) {
return this.plusBusiness({ days: -days });
};
+/**
+ * @typedef DiffBusinessConfig
+ * @property {boolean} [includeEndDate=false] - include the end date in the calculation
+ * @property {boolean} [relative=false] - signs the return value as negative if end date is in the past
+ */
+
+/**
+ * Returns the difference in business days.
+ * @param {DateTime} targetDate
+ * @param {DiffBusinessConfig} config
+ * @returns {number}
+ */
+DateTime.prototype.diffBusiness = function(
+ targetDate,
+ { includeEndDate = false, relative = false } = {}
+) {
+ let dt = this;
+ let start = dt < targetDate ? dt : targetDate;
+ let end = dt < targetDate ? targetDate : dt;
+ let daysDiff = Number(
+ includeEndDate && end.isBusinessDay() && !end.isHoliday()
+ );
+ let isSameDay =
+ dt.hasSame(targetDate, 'day') &&
+ dt.hasSame(targetDate, 'month') &&
+ dt.hasSame(targetDate, 'year');
+
+ if (isSameDay) {
+ return daysDiff;
+ }
+
+ while (start < end) {
+ if (start.isBusinessDay() && !start.isHoliday()) {
+ daysDiff += 1;
+ }
+
+ start = start.plus({ days: 1 });
+ }
+
+ if (relative) {
+ return dt <= targetDate ? daysDiff : -daysDiff;
+ }
+
+ return daysDiff;
+};
+
export { DateTime };
diff --git a/src/index.test.js b/src/index.test.js
index d3d86ad..0d28855 100644
--- a/src/index.test.js
+++ b/src/index.test.js
@@ -324,3 +324,60 @@ describe('time zone is carried over after a business-day operation', () => {
expect(nyPlusTwo.zoneName).toBe('America/New_York');
});
});
+
+describe('diffBusiness()', () => {
+ // Pick a start date we know is a valid business day
+ const defaultStartDate = DateTime.fromISO('2021-05-03');
+
+ it('knows two identical DateTimes have a business day diff of 0', () => {
+ const targetDate = defaultStartDate.startOf('day').plus({ hours: 2 });
+
+ expect(defaultStartDate.diffBusiness(targetDate)).toEqual(0);
+ });
+
+ it('knows there are 3 business days between two dates that are 3 business days apart', () => {
+ const myCompanyTakesNoHolidays = [];
+ const startDate = defaultStartDate.startOf('day');
+ const futureDate = startDate.plusBusiness({ days: 3 });
+ const pastDate = startDate.minusBusiness({ days: 3 });
+
+ startDate.setupBusiness({
+ holidayMatchers: myCompanyTakesNoHolidays,
+ });
+
+ expect(startDate.diffBusiness(futureDate)).toEqual(3);
+ expect(startDate.diffBusiness(pastDate)).toEqual(3);
+ });
+
+ it('knows diff is negative for the past and positive for the future if relative is specified', () => {
+ const myCompanyTakesNoHolidays = [];
+ const startDate = defaultStartDate.startOf('day');
+ const futureDate = startDate.plusBusiness({ days: 3 });
+ const pastDate = startDate.minusBusiness({ days: 3 });
+
+ startDate.setupBusiness({
+ holidayMatchers: myCompanyTakesNoHolidays,
+ });
+
+ const config = { relative: true };
+
+ expect(startDate.diffBusiness(futureDate, config)).toEqual(3);
+ expect(startDate.diffBusiness(pastDate, config)).toEqual(-3);
+ });
+
+ it('knows there are 4 business days between two dates that are 3 business days apart but include the end date', () => {
+ const myCompanyTakesNoHolidays = [];
+ const startDate = defaultStartDate.startOf('day');
+ const futureDate = startDate.plusBusiness({ days: 3 });
+ const pastDate = startDate.minusBusiness({ days: 3 });
+
+ startDate.setupBusiness({
+ holidayMatchers: myCompanyTakesNoHolidays,
+ });
+
+ const config = { includeEndDate: true };
+
+ expect(startDate.diffBusiness(futureDate, config)).toEqual(4);
+ expect(startDate.diffBusiness(pastDate, config)).toEqual(4);
+ });
+});