diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a65b1367e..78662c17a1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -761,6 +761,9 @@ for the list of changes made since `v2.0.0-alpha.1`. - [Add new function `differenceInBusinessDays`](https://github.com/date-fns/date-fns/pull/1194) which calculates the difference in business days. Kudos to [@ThorrStevens](https://github.com/ThorrStevens)! +- [Add new function `addBusinessDays`](https://github.com/date-fns/date-fns/pull/1154), + similar to `addDays` but ignoring weekends. Thanks to [@ThorrStevens](https://github.com/ThorrStevens)! + ## [1.30.1] - 2018-12-10 ### Fixed diff --git a/scripts/test/dst.sh b/scripts/test/dst.sh index 5c756d2bf5..2c8b198168 100755 --- a/scripts/test/dst.sh +++ b/scripts/test/dst.sh @@ -11,3 +11,4 @@ export PATH="$(yarn bin):$PATH" env TZ=America/Sao_Paulo babel-node ./test/dst/parseISO/basic.js env TZ=Pacific/Apia babel-node ./test/dst/parseISO/samoa.js env TZ=Asia/Damascus babel-node ./test/dst/eachDayOfInterval/basic.js +env TZ=America/Santiago babel-node ./test/dst/addBusinessDays/basic.js diff --git a/src/addBusinessDays/index.d.ts b/src/addBusinessDays/index.d.ts new file mode 100644 index 0000000000..b1c084e3cd --- /dev/null +++ b/src/addBusinessDays/index.d.ts @@ -0,0 +1,4 @@ +// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it. + +import { addBusinessDays } from 'date-fns' +export default addBusinessDays diff --git a/src/addBusinessDays/index.js b/src/addBusinessDays/index.js new file mode 100644 index 0000000000..4f0aa323d5 --- /dev/null +++ b/src/addBusinessDays/index.js @@ -0,0 +1,44 @@ +import isWeekend from '../isWeekend/index.js' +import toDate from '../toDate/index.js' +import toInteger from '../_lib/toInteger/index.js' + +/** + * @name addBusinessDays + * @category Day Helpers + * @summary Add the specified number of business days (mon - fri) to the given date. + * + * @description + * Add the specified number of business days (mon - fri) to the given date, ignoring weekends. + * + * @param {Date|Number} date - the date to be changed + * @param {Number} amount - the amount of business days to be added + * @returns {Date} the new date with the business days added + * @throws {TypeError} 2 arguments required + * + * @example + * // Add 10 business days to 1 September 2014: + * var result = addBusinessDays(new Date(2014, 8, 1), 10) + * //=> Mon Sep 15 2014 00:00:00 (skipped weekend days) + */ +export default function addBusinessDays(dirtyDate, dirtyAmount) { + if (arguments.length < 2) { + throw new TypeError( + '2 arguments required, but only ' + arguments.length + ' present' + ) + } + + var date = toDate(dirtyDate) + var amount = toInteger(dirtyAmount) + + if (isNaN(amount)) return new Date(NaN) + + var hours = date.getHours() + var numWeekDays = 0 + while (numWeekDays < amount) { + date.setDate(date.getDate() + 1) + date.setHours(hours) + if (!isWeekend(date)) numWeekDays++ + } + + return date +} diff --git a/src/addBusinessDays/index.js.flow b/src/addBusinessDays/index.js.flow new file mode 100644 index 0000000000..3a0eb45a59 --- /dev/null +++ b/src/addBusinessDays/index.js.flow @@ -0,0 +1,38 @@ +// @flow +// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it. + +type Interval = { + start: Date | number, + end: Date | number +} + +type Locale = { + formatDistance: Function, + formatRelative: Function, + localize: { + ordinalNumber: Function, + era: Function, + quarter: Function, + month: Function, + day: Function, + dayPeriod: Function + }, + formatLong: Object, + date: Function, + time: Function, + dateTime: Function, + match: { + ordinalNumber: Function, + era: Function, + quarter: Function, + month: Function, + day: Function, + dayPeriod: Function + }, + options?: { + weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6, + firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7 + } +} + +declare module.exports: (date: Date | number, amount: number) => Date diff --git a/src/addBusinessDays/test.js b/src/addBusinessDays/test.js new file mode 100644 index 0000000000..2b90008fb3 --- /dev/null +++ b/src/addBusinessDays/test.js @@ -0,0 +1,49 @@ +// @flow +/* eslint-env mocha */ + +import assert from 'power-assert' +import addBusinessDays from '.' + +describe('addBusinessDays', function() { + it('adds the given number of business days', function() { + var result = addBusinessDays(new Date(2014, 8 /* Sep */, 1), 10) + assert.deepEqual(result, new Date(2014, 8 /* Sep */, 15)) + }) + + it('accepts a timestamp', function() { + var result = addBusinessDays(new Date(2014, 8 /* Sep */, 1).getTime(), 10) + assert.deepEqual(result, new Date(2014, 8 /* Sep */, 15)) + }) + + it('converts a fractional number to an integer', function() { + var result = addBusinessDays(new Date(2014, 8 /* Sep */, 1), 10.5) + assert.deepEqual(result, new Date(2014, 8 /* Sep */, 15)) + }) + + it('implicitly converts number arguments', function() { + // $ExpectedMistake + var result = addBusinessDays(new Date(2014, 8 /* Sep */, 1), '10') + assert.deepEqual(result, new Date(2014, 8 /* Sep */, 15)) + }) + + it('does not mutate the original date', function() { + var date = new Date(2014, 8 /* Sep */, 1) + addBusinessDays(date, 11) + assert.deepEqual(date, new Date(2014, 8 /* Sep */, 1)) + }) + + it('returns `Invalid Date` if the given date is invalid', function() { + var result = addBusinessDays(new Date(NaN), 10) + assert(result instanceof Date && isNaN(result)) + }) + + it('returns `Invalid Date` if the given amount is NaN', function() { + var result = addBusinessDays(new Date(2014, 8 /* Sep */, 1), NaN) + assert(result instanceof Date && isNaN(result)) + }) + + it('throws TypeError exception if passed less than 2 arguments', function() { + assert.throws(addBusinessDays.bind(null), TypeError) + assert.throws(addBusinessDays.bind(null, 1), TypeError) + }) +}) diff --git a/src/esm/fp/index.js b/src/esm/fp/index.js index 2d6a5016ee..fdf8246627 100644 --- a/src/esm/fp/index.js +++ b/src/esm/fp/index.js @@ -1,5 +1,6 @@ // This file is generated automatically by `scripts/build/indices.js`. Please, don't change it. +export { default as addBusinessDays } from './addBusinessDays/index.js' export { default as addDays } from './addDays/index.js' export { default as addHours } from './addHours/index.js' export { default as addISOWeekYears } from './addISOWeekYears/index.js' diff --git a/src/esm/index.js b/src/esm/index.js index 4f2c2ed493..837ff003ca 100644 --- a/src/esm/index.js +++ b/src/esm/index.js @@ -1,5 +1,6 @@ // This file is generated automatically by `scripts/build/indices.js`. Please, don't change it. +export { default as addBusinessDays } from './addBusinessDays/index.js' export { default as addDays } from './addDays/index.js' export { default as addHours } from './addHours/index.js' export { default as addISOWeekYears } from './addISOWeekYears/index.js' diff --git a/src/fp/addBusinessDays/index.d.ts b/src/fp/addBusinessDays/index.d.ts new file mode 100644 index 0000000000..dd3b8696a7 --- /dev/null +++ b/src/fp/addBusinessDays/index.d.ts @@ -0,0 +1,4 @@ +// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it. + +import { addBusinessDays } from 'date-fns/fp' +export default addBusinessDays diff --git a/src/fp/addBusinessDays/index.js b/src/fp/addBusinessDays/index.js new file mode 100644 index 0000000000..e1f14a0215 --- /dev/null +++ b/src/fp/addBusinessDays/index.js @@ -0,0 +1,8 @@ +// This file is generated automatically by `scripts/build/fp.js`. Please, don't change it. + +import fn from '../../addBusinessDays/index.js' +import convertToFP from '../_lib/convertToFP/index.js' + +var addBusinessDays = convertToFP(fn, 2) + +export default addBusinessDays diff --git a/src/fp/addBusinessDays/index.js.flow b/src/fp/addBusinessDays/index.js.flow new file mode 100644 index 0000000000..03e03ee8c0 --- /dev/null +++ b/src/fp/addBusinessDays/index.js.flow @@ -0,0 +1,44 @@ +// @flow +// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it. + +type Interval = { + start: Date | number, + end: Date | number +} + +type Locale = { + formatDistance: Function, + formatRelative: Function, + localize: { + ordinalNumber: Function, + era: Function, + quarter: Function, + month: Function, + day: Function, + dayPeriod: Function + }, + formatLong: Object, + date: Function, + time: Function, + dateTime: Function, + match: { + ordinalNumber: Function, + era: Function, + quarter: Function, + month: Function, + day: Function, + dayPeriod: Function + }, + options?: { + weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6, + firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7 + } +} + +type CurriedFn1 = (a: A) => R + +type CurriedFn2 = ( + a: A +) => CurriedFn1 | ((a: A, b: B) => R) + +declare module.exports: CurriedFn2 diff --git a/src/fp/index.js b/src/fp/index.js index 261e022c14..e2741dc8c2 100644 --- a/src/fp/index.js +++ b/src/fp/index.js @@ -3,6 +3,7 @@ var constants = require('../constants/index.js') module.exports = { + addBusinessDays: require('./addBusinessDays/index.js'), addDays: require('./addDays/index.js'), addHours: require('./addHours/index.js'), addISOWeekYears: require('./addISOWeekYears/index.js'), diff --git a/src/fp/index.js.flow b/src/fp/index.js.flow index 4fd6141bf9..ceb51ccd26 100644 --- a/src/fp/index.js.flow +++ b/src/fp/index.js.flow @@ -66,6 +66,7 @@ type CurriedFn4 = ( ) => CurriedFn1 | ((a: A, b: B, c: C, d: D) => R))) declare module.exports: { + addBusinessDays: CurriedFn2, addDays: CurriedFn2, addHours: CurriedFn2, addISOWeekYears: CurriedFn2, diff --git a/src/index.js b/src/index.js index 2ae1382dae..14756b4579 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ var constants = require('./constants/index.js') module.exports = { + addBusinessDays: require('./addBusinessDays/index.js'), addDays: require('./addDays/index.js'), addHours: require('./addHours/index.js'), addISOWeekYears: require('./addISOWeekYears/index.js'), diff --git a/src/index.js.flow b/src/index.js.flow index 53f5aaf286..12ef492dd0 100644 --- a/src/index.js.flow +++ b/src/index.js.flow @@ -36,6 +36,8 @@ type Locale = { } declare module.exports: { + addBusinessDays: (date: Date | number, amount: number) => Date, + addDays: (date: Date | number, amount: number) => Date, addHours: (date: Date | number, amount: number) => Date, diff --git a/test/dst/addBusinessDays/basic.js b/test/dst/addBusinessDays/basic.js new file mode 100644 index 0000000000..c55a6860e6 --- /dev/null +++ b/test/dst/addBusinessDays/basic.js @@ -0,0 +1,18 @@ +// This is basic DST test for addBusinessDays + +import assert from 'assert' +import addBusinessDays from '../../../src/addBusinessDays' + +if (process.env.TZ !== 'America/Santiago') + throw new Error('The test must be run with TZ=America/Santiago') + +if (parseInt(process.version.match(/^v(\d+)\./)[1]) < 10) + throw new Error('The test must be run on Node.js version >= 10') + +console.log(addBusinessDays(new Date(2014, 8 /* Sep */, 1), 10).toString()) + +assert.deepEqual( + // new Date(2014, 8, 7) is the DST day + addBusinessDays(new Date(2014, 8 /* Sep */, 1), 10).toString(), + 'Mon Sep 15 2014 00:00:00 GMT-0300 (Chile Summer Time)' +) diff --git a/typings.d.ts b/typings.d.ts index 1318494fe0..611eea81ec 100644 --- a/typings.d.ts +++ b/typings.d.ts @@ -73,6 +73,9 @@ declare module 'date-fns' { // Regular Functions declare module 'date-fns' { + function addBusinessDays(date: Date | number, amount: number): Date + namespace addBusinessDays {} + function addDays(date: Date | number, amount: number): Date namespace addDays {} @@ -800,6 +803,11 @@ declare module 'date-fns' { const minTime: number } +declare module 'date-fns/addBusinessDays' { + import { addBusinessDays } from 'date-fns' + export default addBusinessDays +} + declare module 'date-fns/addDays' { import { addDays } from 'date-fns' export default addDays @@ -1580,6 +1588,11 @@ declare module 'date-fns/toDate' { export default toDate } +declare module 'date-fns/addBusinessDays/index' { + import { addBusinessDays } from 'date-fns' + export default addBusinessDays +} + declare module 'date-fns/addDays/index' { import { addDays } from 'date-fns' export default addDays @@ -2360,6 +2373,11 @@ declare module 'date-fns/toDate/index' { export default toDate } +declare module 'date-fns/addBusinessDays/index.js' { + import { addBusinessDays } from 'date-fns' + export default addBusinessDays +} + declare module 'date-fns/addDays/index.js' { import { addDays } from 'date-fns' export default addDays @@ -3143,6 +3161,9 @@ declare module 'date-fns/toDate/index.js' { // FP Functions declare module 'date-fns/fp' { + const addBusinessDays: CurriedFn2 + namespace addBusinessDays {} + const addDays: CurriedFn2 namespace addDays {} @@ -3763,6 +3784,11 @@ declare module 'date-fns/fp' { const minTime: number } +declare module 'date-fns/fp/addBusinessDays' { + import { addBusinessDays } from 'date-fns/fp' + export = addBusinessDays +} + declare module 'date-fns/fp/addDays' { import { addDays } from 'date-fns/fp' export = addDays @@ -4663,6 +4689,11 @@ declare module 'date-fns/fp/toDate' { export = toDate } +declare module 'date-fns/fp/addBusinessDays/index' { + import { addBusinessDays } from 'date-fns/fp' + export = addBusinessDays +} + declare module 'date-fns/fp/addDays/index' { import { addDays } from 'date-fns/fp' export = addDays @@ -5563,6 +5594,11 @@ declare module 'date-fns/fp/toDate/index' { export = toDate } +declare module 'date-fns/fp/addBusinessDays/index.js' { + import { addBusinessDays } from 'date-fns/fp' + export = addBusinessDays +} + declare module 'date-fns/fp/addDays/index.js' { import { addDays } from 'date-fns/fp' export = addDays @@ -6466,6 +6502,9 @@ declare module 'date-fns/fp/toDate/index.js' { // ECMAScript Module Functions declare module 'date-fns/esm' { + function addBusinessDays(date: Date | number, amount: number): Date + namespace addBusinessDays {} + function addDays(date: Date | number, amount: number): Date namespace addDays {} @@ -7193,6 +7232,11 @@ declare module 'date-fns/esm' { const minTime: number } +declare module 'date-fns/esm/addBusinessDays' { + import { addBusinessDays } from 'date-fns/esm' + export default addBusinessDays +} + declare module 'date-fns/esm/addDays' { import { addDays } from 'date-fns/esm' export default addDays @@ -7973,6 +8017,11 @@ declare module 'date-fns/esm/toDate' { export default toDate } +declare module 'date-fns/esm/addBusinessDays/index' { + import { addBusinessDays } from 'date-fns/esm' + export default addBusinessDays +} + declare module 'date-fns/esm/addDays/index' { import { addDays } from 'date-fns/esm' export default addDays @@ -8753,6 +8802,11 @@ declare module 'date-fns/esm/toDate/index' { export default toDate } +declare module 'date-fns/esm/addBusinessDays/index.js' { + import { addBusinessDays } from 'date-fns/esm' + export default addBusinessDays +} + declare module 'date-fns/esm/addDays/index.js' { import { addDays } from 'date-fns/esm' export default addDays @@ -9536,6 +9590,9 @@ declare module 'date-fns/esm/toDate/index.js' { // ECMAScript Module FP Functions declare module 'date-fns/esm/fp' { + const addBusinessDays: CurriedFn2 + namespace addBusinessDays {} + const addDays: CurriedFn2 namespace addDays {} @@ -10156,6 +10213,11 @@ declare module 'date-fns/esm/fp' { const minTime: number } +declare module 'date-fns/esm/fp/addBusinessDays' { + import { addBusinessDays } from 'date-fns/esm/fp' + export default addBusinessDays +} + declare module 'date-fns/esm/fp/addDays' { import { addDays } from 'date-fns/esm/fp' export default addDays @@ -11056,6 +11118,11 @@ declare module 'date-fns/esm/fp/toDate' { export default toDate } +declare module 'date-fns/esm/fp/addBusinessDays/index' { + import { addBusinessDays } from 'date-fns/esm/fp' + export default addBusinessDays +} + declare module 'date-fns/esm/fp/addDays/index' { import { addDays } from 'date-fns/esm/fp' export default addDays @@ -11956,6 +12023,11 @@ declare module 'date-fns/esm/fp/toDate/index' { export default toDate } +declare module 'date-fns/esm/fp/addBusinessDays/index.js' { + import { addBusinessDays } from 'date-fns/esm/fp' + export default addBusinessDays +} + declare module 'date-fns/esm/fp/addDays/index.js' { import { addDays } from 'date-fns/esm/fp' export default addDays @@ -14919,6 +14991,8 @@ declare module 'date-fns/esm/locale/zh-TW/index.js' { // dateFns Global Interface interface dateFns { + addBusinessDays(date: Date | number, amount: number): Date + addDays(date: Date | number, amount: number): Date addHours(date: Date | number, amount: number): Date