{"_id":"@northscaler/recurrify","_rev":"32-34ca1ce29233902d8993dded7285b56e","name":"@northscaler/recurrify","description":"Find time slots given a recurrence","dist-tags":{"stable-1.2":"1.2.1","latest":"1.3.2","stable-1.3":"1.3.2"},"versions":{"1.2.0-rc.0":{"name":"@northscaler/recurrify","version":"1.2.0-rc.0","keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","_id":"@northscaler/recurrify@1.2.0-rc.0","maintainers":[{"name":"npm_scispike","email":"npm@scispike.com"},{"name":"npm_northscaler","email":"npm@northscaler.com"}],"homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"dist":{"shasum":"3ec684e7d553e5f838e309c5b37cbb4b8268725d","tarball":"https://registry.npmjs.org/@northscaler/recurrify/-/recurrify-1.2.0-rc.0.tgz","fileCount":33,"integrity":"sha512-xT1Tr4Kfw8Er3bo0WeR2gLSWS5VF5yvwXfTrO50tndCsxhn9kRCKQC1YFnIVFhxbOSnjpXbSmDzArd7PlR8Lrg==","signatures":[{"sig":"MEQCIHSXcWP85cXejUeH49NPxxCjXJhKSC7gHGuHJTb+lhZcAiBNIsz2idVOzeIUnkVi/lyUyIhPHJCzCBK6hAtBSiFmzA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1623853,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeLyWBCRA9TVsSAnZWagAA/v4P/3H59nxLcQLE/DSz3CI4\nkZf8tJwLQuWPTmqZYjH6GhAtmhIT6sgU48hkkd3zi6CIdjr8vhZ/bRx/YQw8\nkDTWwejFQ9nDJogh9GAyP3iUV/rcGXFrk4gEccjFFtyB1BXOY3+uGS/C3AJS\nMXvOslTLV7kRl3VxTVIN6D3miHIkE8IN6c2Si3Xrw8uTt7fJN1I6qhQuyurj\nuY/T9vNhW20EzaIAIYRFwCqFYgsLHQgXsZ/v9+30C+94IvmVlsuSQUe9mgXd\nRqL5eHIEnVtrMLESYF3rFuHUWNsMgKkq0saZV4ntKZDfOozvn30PuQ8BlEvL\nUF7lJt+zigiL5+hRR02373pi64acTCJPxTYoVMkR9H0a2xp1VwrabtDIBWbs\nyLBTXmDXz2hNoJ2Qvq1hmBmiqcafL3/BiVfQA7e/j+PnjllewjqrJuM0pjFx\nTnPofr78xHyluWFzm/DrfPOdyNgV5X6IPWswJzKbdRPA0fJmLgMv+iWoLHVc\nbkGTsb5UaRjPG/myh2yLLu9M4U6spxZIvE7z5oZL+ahuHxsoINicnrqI/roR\njaZEZlXLky50SrwV4t4LPx3xZLUq4zgGFVrwraqDZ/wV9svmNDKF+GIIhASX\nMBnNJDDYnBxOBX+AY7Iblc3oaSdbgDkOuN0hDFIoTnqXEAPRaRdimEEaE3gH\nEDU3\r\n=vGVV\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","babel":{"plugins":[["@babel/plugin-proposal-optional-chaining",{"loose":false}]],"presets":[["@babel/preset-env",{"targets":{"node":true}}]],"sourceMaps":"inline"},"mocha":{"colors":true,"require":"@babel/register"},"engines":{"node":">=8.0.0"},"scripts":{"i":"mocha 'src/test/integration/**/*.spec.js' | npx bunyan","u":"mocha 'src/test/unit/**/*.spec.js' | npx bunyan","cov":"open coverage/index.html","doc":"jsdoc --verbose -d lib/main/doc -r -c jsdoc.json -R lib/main/README.md lib/main","link":"mkdir -p lib/main && cd lib/main && npm link; cd ../..","lint":"standard --verbose 'src/**/*.js'","test":"run-s transpile unit-integration lint","unit":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/unit/**/*.js'","build":"npm install && npm test","format":"standard --fix 'src/**/*.js'","report":"nyc report --reporter=html","unlink":"mkdir -p lib/main && cd lib/main && npm unlink; cd ../..","prelink":"run-s make-distribution","postunit":"run-s report","transpile":"run-s transpile-main transpile-test","integration":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/integration/**/*.js'","transpile-main":"babel --delete-dir-on-start --verbose --out-dir lib/main --copy-files src/main","transpile-test":"babel --delete-dir-on-start --verbose --out-dir lib/test --copy-files src/test","postintegration":"run-s report","unit-integration":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/unit/**/*.js' 'lib/test/integration/**/*.js'","make-distribution":"run-s transpile finalize-distribution","postunit-integration":"run-s report","finalize-distribution":"copyfiles --verbose package.json package-lock.json README.md LICENSE lib/main","postfinalize-distribution":"run-s doc"},"_npmUser":{"name":"npm_northscaler","email":"npm@northscaler.com"},"standard":{"parser":"babel-eslint"},"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"_npmVersion":"6.13.4","description":"Find time slots given a recurrence","directories":{},"_nodeVersion":"8.17.0","dependencies":{"@babel/polyfill":"7.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"14.1.1","chai":"4.2.0","acorn":"7.1.0","jsdoc":"3.6.3","mocha":"6.2.2","should":"13.2.3","standard":"14.3.1","copyfiles":"2.1.1","@babel/cli":"7.7.4","dirty-chai":"2.0.1","@babel/core":"7.7.4","npm-run-all":"4.1.5","babel-eslint":"10.0.3","@babel/register":"7.7.4","@babel/preset-env":"7.7.4","@northscaler/mutrait":"1.1.0","config-cev-generator":"0.1.6","@babel/plugin-proposal-optional-chaining":"7.7.4"},"_npmOperationalInternal":{"tmp":"tmp/recurrify_1.2.0-rc.0_1580148097328_0.07396537243960921","host":"s3://npm-registry-packages"}},"1.2.0":{"name":"@northscaler/recurrify","version":"1.2.0","keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","_id":"@northscaler/recurrify@1.2.0","maintainers":[{"name":"npm_northscaler","email":"npm@northscaler.com"},{"name":"npm_scispike","email":"npm@scispike.com"}],"homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"dist":{"shasum":"5eb01270026b203690eb9b1de24f344052fc47c6","tarball":"https://registry.npmjs.org/@northscaler/recurrify/-/recurrify-1.2.0.tgz","fileCount":33,"integrity":"sha512-ksgzMdgbJXN5eeWJ7GLZJNTYq4R0zUHiBdaLQj4pXHzDJ4OUBotmQbSe48UFXz9YAF6eQm+j/Sn4GpEMEARrFg==","signatures":[{"sig":"MEYCIQCQ21bi8NzUXY62HKWJyJ+JYfEcWtwpv3b8p8ZP12VfJAIhAPc+HCJYt1n5WEmEvtpi7n6TiXcCIg1lKABOvcY9iIS1","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":1623848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeLyZ7CRA9TVsSAnZWagAAtFQP/ixChflLWXDmRDXAKf/W\nAYVL3HkFDc3QYTtd+btcCHNssYF2YNIavmG6cvIzQfmEcYe3yLm2K0thgY3u\npwG5oc4lG9An84+wyYwPiXXFP/jHJy62Qy849mDTAPvJUPqm/Eg0hQirLbvb\nqmj11pMongCfYDtvpnofzdeqoGqVU+eSvXcjEVRd2rYQQZO/YqRPGdHcajOD\ntnN0BzU17bXwWpLfl4eT/F+4Zr3DhK7+an132NKtKwGXU3MO9l/X8yeqdQ6W\nW2CL3VHoEV5yIrSmru/WtOkqkxP6MB5SVqkdp7Wh/TKP9Mh8F3UMaS28xwqn\nyITX3yu1dY3dK+EK6uGV4gFgMscPrcOO4rFWG+i3IUYDu2K2l4rvsmwQAA7r\nMNyCm8jVkmLN0G49oS1tA7U5NPd6kwhJJB9scbugLY1kE1R3m4B8Hi7Rrlwd\nYhKGwUBn/oLN6voqZutHJXzDXg9WW29ba7pCS24ejibUPMs7/Skn+/ml+pG8\nebF+1Kn1Ck8D/521qpxph8N/WnScTiCnBLI2V/eqy7+rq4P5Hn7U0RdHiHJc\nF2/UP352CDuzmk4oGbOoyinBENCdUiodyxFG1L4g/EO8xGHkjIyJrpFV1x0f\nKIcJnSxyRH79mr2Gr5c77KDLlZBhwoOAv6FXoLDlCQi85Ssvr3T0l8td7JZn\n8u1d\r\n=RlpU\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","babel":{"plugins":[["@babel/plugin-proposal-optional-chaining",{"loose":false}]],"presets":[["@babel/preset-env",{"targets":{"node":true}}]],"sourceMaps":"inline"},"mocha":{"colors":true,"require":"@babel/register"},"engines":{"node":">=8.0.0"},"scripts":{"i":"mocha 'src/test/integration/**/*.spec.js' | npx bunyan","u":"mocha 'src/test/unit/**/*.spec.js' | npx bunyan","cov":"open coverage/index.html","doc":"jsdoc --verbose -d lib/main/doc -r -c jsdoc.json -R lib/main/README.md lib/main","link":"mkdir -p lib/main && cd lib/main && npm link; cd ../..","lint":"standard --verbose 'src/**/*.js'","test":"run-s transpile unit-integration lint","unit":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/unit/**/*.js'","build":"npm install && npm test","format":"standard --fix 'src/**/*.js'","report":"nyc report --reporter=html","unlink":"mkdir -p lib/main && cd lib/main && npm unlink; cd ../..","prelink":"run-s make-distribution","postunit":"run-s report","transpile":"run-s transpile-main transpile-test","integration":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/integration/**/*.js'","transpile-main":"babel --delete-dir-on-start --verbose --out-dir lib/main --copy-files src/main","transpile-test":"babel --delete-dir-on-start --verbose --out-dir lib/test --copy-files src/test","postintegration":"run-s report","unit-integration":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/unit/**/*.js' 'lib/test/integration/**/*.js'","make-distribution":"run-s transpile finalize-distribution","postunit-integration":"run-s report","finalize-distribution":"copyfiles --verbose package.json package-lock.json README.md LICENSE lib/main","postfinalize-distribution":"run-s doc"},"_npmUser":{"name":"npm_northscaler","email":"npm@northscaler.com"},"standard":{"parser":"babel-eslint"},"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"_npmVersion":"6.13.4","description":"Find time slots given a recurrence","directories":{},"_nodeVersion":"8.17.0","dependencies":{"@babel/polyfill":"7.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"14.1.1","chai":"4.2.0","acorn":"7.1.0","jsdoc":"3.6.3","mocha":"6.2.2","should":"13.2.3","standard":"14.3.1","copyfiles":"2.1.1","@babel/cli":"7.7.4","dirty-chai":"2.0.1","@babel/core":"7.7.4","npm-run-all":"4.1.5","babel-eslint":"10.0.3","@babel/register":"7.7.4","@babel/preset-env":"7.7.4","@northscaler/mutrait":"1.1.0","config-cev-generator":"0.1.6","@babel/plugin-proposal-optional-chaining":"7.7.4"},"_npmOperationalInternal":{"tmp":"tmp/recurrify_1.2.0_1580148347186_0.7295946475673971","host":"s3://npm-registry-packages"}},"1.2.1":{"name":"@northscaler/recurrify","version":"1.2.1","keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","_id":"@northscaler/recurrify@1.2.1","maintainers":[{"name":"npm_northscaler","email":"npm@northscaler.com"},{"name":"npm_scispike","email":"npm@scispike.com"}],"homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"dist":{"shasum":"f36ea5369334af8a2c9fe293d94dc59d8035c3f0","tarball":"https://registry.npmjs.org/@northscaler/recurrify/-/recurrify-1.2.1.tgz","fileCount":37,"integrity":"sha512-qkHlUmjSu6hRvGLSHCdW61SuGsgykfpZtid+nl1JNPUDs49hQJBF7C9IyKqfRiBkSIcHT3LQ+dp7NdBqjI0XQw==","signatures":[{"sig":"MEUCIFFaFzvR6lZziRQughMPst8Q5l3oAddZqO4CneWKLJkoAiEAzmB5RbBtsD/a4ctZCCzoKtzF0O6hGmYpzV5pa70smMc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":2015639,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeSw/2CRA9TVsSAnZWagAAtrgP/j98AhrQOumG7+lqUhex\nH142tfO43f9kVs0OdtzTnF7kyW0iyPNxDLDWDnjndUVr0XwDrUO4T2NstjvO\nSvP6NCAE48ZJe0l+X13PeunbiydYFTrvku+RX3l1VdU1fCxRBuNJQ3vT+HG/\nYyJciBRwhieSatfATz7HWrR99s4sX0aJi1AjLbRA2hSsY30ZT4QeMnTGlPNX\nV+Y0otXhsK9htEUQunQ04vPJ8dcj2aQkeJI+M0lQ+4Bg+xRq5/oqwBN8DkUO\nzpKzpsGyOLU2+MNgVMZt6m884Auo0n/JVxqJojPZ36/nqvVifXZgAnbCcaDY\nFOQq/rSCKZmnnR75W45wpGEnNstinc83C1OUysK5iGhU1rWBCHfZno5mis8X\nEEXbg/QnlUuSkHiFyM1sFdqxHnoJN+9Uy8ffi9NiZFJ/qnr1gDrtx8cW5wN+\nkwayD3AFNQYDwXUcLPMo871NSZgFwNLbU8vTIkFjLNN4PEeGGeu+PFcEoXEe\nQCfL5RFNbWFftsA87tSVQ5qpccw9l7eTdqryVMaK2fdZjSsxHZGPJGf/6z0R\niMR+FpO6yvzBVMi5kwJywVQsdBXXoaqviIcbTsdrGezWHu2zAiTJhWBfPiYC\n54Oj40vQJ/qgkNkxYq6h6lYHGee4tQTt+MpNr1cSqWOUuU8N3hOE030RDeZr\n+UNv\r\n=73LY\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","babel":{"plugins":[["@babel/plugin-proposal-optional-chaining",{"loose":false}]],"presets":[["@babel/preset-env",{"targets":{"node":true}}]],"sourceMaps":"inline"},"mocha":{"colors":true,"require":"@babel/register"},"engines":{"node":">=8.6.0"},"scripts":{"i":"mocha 'src/test/integration/**/*-test.js' | npx bunyan","u":"mocha 'src/test/unit/**/*-test.js' | npx bunyan","cov":"open coverage/index.html","doc":"jsdoc --verbose -d lib/main/doc -r -c jsdoc.json -R lib/main/README.md lib/main","link":"mkdirp lib/main && cd lib/main && npm link; cd ../..","lint":"standard --verbose 'src/**/*.js'","test":"run-s transpile unit-integration lint","unit":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/unit/**/*-test.js'","build":"npm install && npm test","clean":"rimraf lib/main","format":"standard --fix 'src/**/*.js'","report":"nyc report --reporter=html","unlink":"mkdirp lib/main && cd lib/main && npm unlink; cd ../..","prelink":"run-s make-distribution","copy-src":"mkdirp lib/main/src && cp -r src/main/* lib/main/src","postunit":"run-s report","transpile":"run-s transpile-main transpile-test","integration":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/integration/**/*-test.js'","transpile-main":"babel --verbose --out-dir lib/main --copy-files lib/main/src","transpile-test":"babel --delete-dir-on-start --verbose --out-dir lib/test --copy-files src/test","postintegration":"run-s report","unit-integration":"nyc --check-coverage -x 'lib/test' --exclude-after-remap false mocha 'lib/test/unit/**/*-test.js' 'lib/test/integration/**/*-test.js'","make-distribution":"run-s transpile finalize-distribution doc","pretranspile-main":"run-s clean copy-src","postunit-integration":"run-s report","finalize-distribution":"cp package.json package-lock.json README.md LICENSE lib/main"},"_npmUser":{"name":"npm_northscaler","email":"npm@northscaler.com"},"standard":{"parser":"babel-eslint"},"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"_npmVersion":"5.3.0","description":"Find time slots given a recurrence","directories":{},"_nodeVersion":"8.6.0","dependencies":{"@babel/polyfill":"7.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":true,"devDependencies":{"nyc":"14.1.1","chai":"4.2.0","acorn":"7.1.0","jsdoc":"3.6.3","mocha":"6.2.2","should":"13.2.3","standard":"14.3.1","copyfiles":"2.1.1","@babel/cli":"7.7.4","dirty-chai":"2.0.1","@babel/core":"7.7.4","npm-run-all":"4.1.5","babel-eslint":"10.0.3","@babel/register":"7.7.4","@babel/preset-env":"7.7.4","@northscaler/mutrait":"1.1.0","config-cev-generator":"0.1.6","@babel/plugin-proposal-optional-chaining":"7.7.4"},"_npmOperationalInternal":{"tmp":"tmp/recurrify_1.2.1_1581977589708_0.9345881350663878","host":"s3://npm-registry-packages"}},"1.3.0-rc.0":{"name":"@northscaler/recurrify","version":"1.3.0-rc.0","keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","_id":"@northscaler/recurrify@1.3.0-rc.0","maintainers":[{"name":"npm_northscaler","email":"npm@northscaler.com"},{"name":"npm_scispike","email":"npm@scispike.com"}],"homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"dist":{"shasum":"3faf7cba8ee165d20a8fe8079bfeb25bd9bb4d18","tarball":"https://registry.npmjs.org/@northscaler/recurrify/-/recurrify-1.3.0-rc.0.tgz","fileCount":82,"integrity":"sha512-A2RtiD7kd1eR+l+NCGSeOtq7+VyKZDWmhHQ0UAWj/boWvlvn1bxxs9CIhzlyTY6eMsAQMe+kkCC1nmJacYBXbQ==","signatures":[{"sig":"MEUCIBAGBwPz8SpuoKYabr0CHuGUvvEAlyo3/UeQe8PbJf/LAiEAmcQ3DtaE+TWY4xC1g7hUALsrxI6zF29jTnzIl7fHEIA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":2198011,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeuXoICRA9TVsSAnZWagAAjWkP/RdVmyD3CpzYlg3/llp6\nNSxmpqFu1WZQyBDvIkr1YbDL/1lWWk7d2jXpUNYISQ2QZK1kK2hyWfI11at3\nhgAW51+fDsrVx7EuKMETMQ1wnz9kl842y5Kpe7ZqEPfzlm25X7WJY7Auuf9t\nN6p+7KZMfvniqfIyXS30a/h1XDnhJL5vNrQdR7/2+qLHxY6O+Jc1/rjehNUh\nloEef+Jj4xDU/+Y4ogS5e9gQfptrNw3/FzXNM71LaW7MA9Yn4dFT4FhuWbzl\nMdE8bzSzoCYZayiLc6AjQBfZlxjOe7UJDLClLJrSL2sbJEXxvANlZZaOoEgG\nGmZ6qRFc02jGv3AvkyJUUiYklCF2J6fpKDlTaj69CEjYfp+gxTvfVLmD21zp\nQ8HgTSV6KqUU2rCMefTYmYKlSUTn8YwKiKeYs73LB4jIQrXNqNPWYvWa43CH\nekegIGWhgFhDlRqIXNlaXNcQa0z9WVtaLUWGE/M7XbcjEiYTGtaRFipvb1Bq\nzugb480MaiZxupWcn0iEV6qyn59IvWNMIn37Km7MFcJ/GPlZc+tB3NDYG/60\nNvREjzI5apOnyAGPuz5IW/Pmxd5Us50wt428njLcV2Ja8Nwzpm2jVBk316sB\navCC4wpL6T7IrT2G95IufgKHjGQYDlUj6yPZV/T/g1DWEgmi3a7h59diYTeo\nwd0+\r\n=xG0j\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","babel":{"plugins":[["@babel/plugin-proposal-optional-chaining",{"loose":false}]],"presets":[["@babel/preset-env",{"targets":{"node":true}}]],"sourceMaps":"inline"},"mocha":{"colors":true,"require":"@babel/register"},"readme":"# recurrify\n\nBased on a shameless fork of [later.js](https://github.com/kirkins/later).\n\n* Added ability to get *all* time slots of a recurrence between a start and end time\n* Removed the parsers in favor of just dealing with recurrence definitions directly\n* Removed `setTimeout` and `setInterval` implementations\n* Removed bower and Makefile\n* Renamed `schedule` to `recurrence` and `occurrence` to `time slot`\n* Renamed package from `cronicle` to `recurrify`\n* WIP to update syntax to more modern ES6+\n\n## Installation\n\n`$ npm install recurrify`\n\n## Example usage\n\n```\nconst recurrify = require('recurrify')\n\nconst startDate = new Date('2018-10-01T13:00:00Z')\nconst endDate = new Date('2018-10-01T15:00:00Z')\n\n// every 5 minutes\nconst sched = { 'recurrences': [{ 'm': [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] }] }\nconst timeSlots = recurrify.recurrence(sched).all(startDate, endDate)\nconsole.log(timeSlots)\n```\n\n### Result\n```\n$ node example.js\n[ 2018-10-01T13:00:00.000Z,\n  2018-10-01T13:05:00.000Z,\n  2018-10-01T13:10:00.000Z,\n  2018-10-01T13:15:00.000Z,\n  2018-10-01T13:20:00.000Z,\n  2018-10-01T13:25:00.000Z,\n  2018-10-01T13:30:00.000Z,\n  2018-10-01T13:35:00.000Z,\n  2018-10-01T13:40:00.000Z,\n  2018-10-01T13:45:00.000Z,\n  2018-10-01T13:50:00.000Z,\n  2018-10-01T13:55:00.000Z,\n  2018-10-01T14:00:00.000Z,\n  2018-10-01T14:05:00.000Z,\n  2018-10-01T14:10:00.000Z,\n  2018-10-01T14:15:00.000Z,\n  2018-10-01T14:20:00.000Z,\n  2018-10-01T14:25:00.000Z,\n  2018-10-01T14:30:00.000Z,\n  2018-10-01T14:35:00.000Z,\n  2018-10-01T14:40:00.000Z,\n  2018-10-01T14:45:00.000Z,\n  2018-10-01T14:50:00.000Z,\n  2018-10-01T14:55:00.000Z,\n  2018-10-01T15:00:00.000Z ]\n```\n\n## Running tests\n\n`$ npm test`\n\n## Recurrences\nRecurrences in `recurrify` are json objects that define a set of time units along with the values that should be considered valid for that time unit.\nThe combination of a time unit with their valid values is called a *constraint*.\n`recurrify` then takes all of the constraints that have been defined and finds dates that match all of them.\n\nSince `recurrify` recurrences are json objects, they can easily be serialized and stored in caches and databases as needed.\nThey are also completely deterministic which means a recurrence will always produce exactly the same valid time slots.\nTherefore, time slots never need to be stored as they can always be recalculated from the recurrence definition.\n\n## Basic recurrences\nA basic recurrence is a set of time units along with their valid values.\nA date is only considered valid if it meets all of the constraints within a basic recurrence.\nA basic recurrence can include as many time units (with or without modifiers) as needed, in any order.\n\n```\n// a basic recurrence that is valid every day at 10:15am and 10:45am\nconst basic = {h: [10], m: [15,45]}\n```\n\nHere we can see a recurrence is made up of objects with properties that correspond to the various time units.\nIn this case `h` is the hour time unit and `m` is the minute time unit.\nThe values to consider valid are always stored in an array as the value of the property.\n\n\n**Note:** Basic recurrences are only valid as part of a complete recurrence definition.\n\n## Composite recurrences\nMultiple basic recurrences can be combined into a single composite recurrence by placing them into an array.\nA date is considered valid if any of the basic recurrences are valid (basically an OR of all of the basic recurrences).\nA composite recurrence can contain as many basic recurrences as needed.\n\n```\n// a composite recurrence that is valid every day at 10:15am and 10:45am\n// and every day at 5:30pm\nconst composite = [\n  {h: [10], m: [15,45]},\n  {h: [17], m: [30]}\n]\n\n```\n\n**Note:** Composite recurrences are only valid as part of a complete recurrence definition.\n\n## Exception recurrences\nAn exception recurrence is a basic or composite recurrence that defines when a recurrence should be considered invalid.\nA date is considered invalid if any of the basic recurrences within an exception recurrence are valid.\n\n```\n// an exception recurrence that makes any date in March as\n// well as any Monday of any month invalid\nconst exception = [\n  {M: [3]},\n  {dw: [2]}\n]\n```\n\n**Note:** Exception recurrences are only valid as part of a complete recurrence definition.\n\n## Complete definition\nA complete definition is a json object that at a minimum contains a `recurrences` property that defines a composite recurrence with at least one basic recurrence.\nOptionally, the definition can also include an `exceptions` property that defines a composite exception recurrence.\n\n### Examples\nHere's an example of a valid recurrence that fires every 10 minutes.\nThe composite recurrence is always placed in an object under a property named `recurrences`. Even if you only have a basic recurrence, the `recurrences` property must be an array.\n\n```\nconst recurrence = {\n  recurrences: [\n    {m: [0,10,20,30,40,50]}\n  ]\n}\n```\n\nHere's a valid recurrence definition that includes exceptions with modifiers.\nHere we see the composite exception recurrence is always placed in an object under a property named `exceptions`.\nEven if you only have a basic exception recurrence, the `exceptions` property must be an array.\n\n```\nconst recurrence = {\n  recurrences: [\n    {h: [10], m: [15,45]},\n    {h: [17], m: [30]}\n  ],\n  exceptions: [\n    {M_a: [3]},\n    {dw: [2]}\n  ]\n}\n```\n\n## Performance considerations\nWhile `recurrify` has been designed to efficiently calculate time slots for all types and complexities of recurrences, there are a few things to keep in mind for applications that have particularly high performance requirements.\n\n* Basic time units perform the best. These include years, months, days, hours, minutes, and seconds.\nCalculating ISO week of year is particularly expensive.\n* Recurrences without exceptions perform better than those with exceptions.\nDefining your recurrence without the need for exceptions will improve performance.\n* Use the `time` time unit instead of specifying hours and minutes separately when possible.\nReducing the number of constraints will generally improve performance.\n* Using `after` and `before` modifiers to eliminate the need for specifying a lot of valid values will improve performance, especially when calculating ranges.\n\n## time units\nTime units are the crux of the `recurrify` library and are used to define new recurrences.\n`recurrify` comes with a large assortment of time units and is also fully extensible making it easy to create custom time units.\n\nWhile time units are primarily used by `recurrify` to define recurrences and calculate time slots, they are also useful for performing time based calculations.\nCalculating values such as ISO week number, moving between days of the year, or figuring out how many days are in a month are all possible using the time unit interface.\n\nIf you don't see the time unit that you need for your recurrence, `recurrify` is fully extensible and it is easy to write your own.\nSee the custom time unit at the bottom of this page for an example.\n\n## Interface\nAll time units implement the same public interface for interacting with them:\n\n**`name`**\nThe name of the time unit.\n\n**`range`**\nThe rough number of seconds that are covered when moving from one instance of this time unit to the next instance.\n\n**`val(date)`**\nThe value of this time unit for the date specified.\n\n**`isValid(date, value)`**\nTrue if the specified value is valid for the specified date, false otherwise.\n\n**`extent(date)`**\nThe minimum and maximum valid values for the time unit for the specified date.\nIf the minimum value is not `0`, `0` can be specified in recurrences to indicate the maximum value.\nThis makes working with non-constant extents (like days in a month) easier.\n\n**`start(date)`**\nThe first second in which the value is the same as the value of the specified date.\nFor example, the start of an hour would be the hour with `0` minutes and `0` seconds.\n\n**`end(date)`**\nThe last second in which the value is the same as the value of the specified date.\nFor example, the end of an hour would be the hour with `59` minutes and `59` seconds.\n\n**`next(date, value)`**\nReturns the next date where the value is the value specified.\nSets the value to `1` if value specified is greater than the max allowed value.\n\n**`prev(`date, value`)**\nReturns the previous date where the value is the value specified.\nSets the value to the max allowed value if the value specified is greater than the max allowed value.\n\n## Second (second, s)\nSeconds in a minute, from `0` to `59`.\n\nUsing seconds in a recurrence:\n\n`const sched = {recurrences: [{s: [0, 15, 30, 45]}]}`\n\nPerforming seconds based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.second.name;\n// --> 'second'\n\nrecurrify.second.range;\n// --> 1\n\nrecurrify.second.val(d);\n// --> 5\n\nrecurrify.second.isValid(d, 10);\n// --> false\n\nrecurrify.second.extent();\n// --> [0, 59]\n\nrecurrify.second.start(d);\n// --> 'Fri, 22 Mar 2013 10:02:05 GMT'\n\nrecurrify.second.end(d);\n// --> 'Fri, 22 Mar 2013 10:02:05 GMT'\n\nrecurrify.second.next(d, 27);\n// --> 'Fri, 22 Mar 2013 10:02:27 GMT'\n\nrecurrify.second.prev(d, 27);\n// --> 'Fri, 22 Mar 2013 10:01:27 GMT'\n```\n\n## Minute (minute, m)\nMinutes in an hour, from `0` to `59`.\n\nUsing minutes in a recurrence:\n\n`const sched = {recurrences: [{m: [0, 15, 30, 45]}]}`\n\nPerforming minutes based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.minute.name;\n// --> 'minute'\n\nrecurrify.minute.range;\n// --> 60\n\nrecurrify.minute.val(d);\n// --> 2\n\nrecurrify.minute.isValid(d, 2);\n// --> true\n\nrecurrify.minute.extent();\n// --> [0, 59]\n\nrecurrify.minute.start(d);\n// --> 'Fri, 22 Mar 2013 10:02:00 GMT'\n\nrecurrify.minute.end(d);\n// --> 'Fri, 22 Mar 2013 10:02:59 GMT'\n\nrecurrify.minute.next(d, 27);\n// --> 'Fri, 22 Mar 2013 10:27:00 GMT'\n\nrecurrify.minute.prev(d, 27);\n// --> 'Fri, 22 Mar 2013 09:27:59 GMT'\n```\n\n## Hour (hour, h)\nHours in a day, from `0` to `23`.\n\nUsing hours in a recurrence:\n\n`const sched = {recurrences: [{h: [0, 5, 12]}]}`\n\nPerforming hours based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.hour.name;\n// --> 'hour'\n\nrecurrify.hour.range;\n// --> 3600\n\nrecurrify.hour.val(d);\n// --> 10\n\nrecurrify.hour.isValid(d, 2);\n// --> false\n\nrecurrify.hour.extent();\n// --> [0, 23]\n\nrecurrify.hour.start(d);\n// --> 'Fri, 22 Mar 2013 10:00:00 GMT'\n\nrecurrify.hour.end(d);\n// --> 'Fri, 22 Mar 2013 10:59:59 GMT'\n\nrecurrify.hour.next(d, 5);\n// --> 'Sat, 23 Mar 2013 05:00:00 GMT'\n\nrecurrify.hour.prev(d, 21);\n// --> 'Thu, 21 Mar 2013 21:59:59 GMT'\n```\n\n## Time (time, t)\nTime of day, represented as seconds since midnight. From `0` to `86399` (60 * 60 * 24 - 1).\n\n\nUsing time in a recurrence:\n\n`const sched = {recurrences: [{t: [6500]}]}`\n\nPerforming time based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.time.name;\n// --> 'time'\n\nrecurrify.time.range;\n// --> 1\n\nrecurrify.time.val(d);\n// --> 36125\n\nrecurrify.time.isValid(d, 36125);\n// --> true\n\nrecurrify.time.extent();\n// --> [0, 86399]\n\nrecurrify.time.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.time.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.time.next(d, 60);\n// --> 'Sat, 23 Mar 2013 00:01:00 GMT'\n\nrecurrify.time.prev(d, 60);\n// --> 'Fri, 22 Mar 2013 00:01:00 GMT'\n```\n\n## Day (day, D)\nDays of a month, from `1` to the max days in the month.\nSpecify `0` for the varying last day of the month, which is between `28` through `31`.\n\nUsing days in a recurrence:\n\n`const sched = {recurrences: [{D: [0]}]}`\n\nPerforming day based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.day.name;\n// --> 'day'\n\nrecurrify.day.range;\n// --> 86400\n\nrecurrify.day.val(d);\n// --> 22\n\nrecurrify.day.isValid(d, 3);\n// --> false\n\nrecurrify.day.extent(d);\n// --> [1, 31]\n\nrecurrify.day.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.day.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.day.next(d, 11);\n// --> 'Thu, 11 Apr 2013 00:00:00 GMT'\n\nrecurrify.day.prev(d, 2);\n// --> 'Sat, 02 Mar 2013 23:59:59 GMT'\n```\n\n## Day of week (dayOfWeek, dw, d)\nDays of a week, from `1` to `7`.\nSpecify `0` for the last day of the week (Saturday).\n\nUsing days of week in a recurrence:\n\n`const sched = {recurrences: [{dw: [2,3,4,5,6]}]}`\n\nPerforming day of week based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfWeek.name;\n// --> 'day of week'\n\nrecurrify.dayOfWeek.range;\n// --> 86400\n\nrecurrify.dayOfWeek.val(d);\n// --> 6\n\nrecurrify.dayOfWeek.isValid(d, 3);\n// --> false\n\nrecurrify.dayOfWeek.extent();\n// --> [1, 7]\n\nrecurrify.dayOfWeek.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeek.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.dayOfWeek.next(d, 1);\n// --> 'Sun, 24 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeek.prev(d, 5);\n// --> 'Thu, 21 Mar 2013 23:59:59 GMT'\n```\n\n## Day of week count (dayOfWeekCount, dc)\nThe nth day of the week within a month, from `1` to the max weeks in a month.\nSpecify `0` for the last day instance.\nUsed together with the day of the week time unit to specify things like the 2nd Tuesday or last Friday of a month.\n\nUsing days of week count in a recurrence:\n\n`const sched = {recurrences: [{dc: [2]}]}`\n\nPerforming day of week count based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfWeekCount.name;\n// --> 'day of week count'\n\nrecurrify.dayOfWeekCount.range;\n// --> 604800\n\nrecurrify.dayOfWeekCount.val(d);\n// --> 4\n\nrecurrify.dayOfWeekCount.isValid(d, 4);\n// --> true\n\nrecurrify.dayOfWeekCount.extent(d);\n// --> [1, 5]\n\nrecurrify.dayOfWeekCount.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeekCount.end(d);\n// --> 'Thu, 28 Mar 2013 23:59:59 GMT'\n\n// zero is special cased and means the last instance of\n// a day of the week in the month, instead of meaning the\n// first day of the week with the highest instance count\n// which would have been Mar 29 with value 5.\nrecurrify.dayOfWeekCount.next(d, 0);\n// --> 'Mon, 25 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeekCount.prev(d, 2);\n// --> 'Thu, 14 Mar 2013 23:59:59 GMT'\n```\n\n## Day of year (dayOfYear, dy)\nDay of the year, from `1` to the max days in year.\nSpecify `0` for last day of the year.\n\nUsing days of year in a recurrence:\n\n`const sched = {recurrences: [{dy: [189, 267]}]}`\n\nPerforming day of year based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfYear.name;\n// --> 'day of year'\n\nrecurrify.dayOfYear.range;\n// --> 86400\n\nrecurrify.dayOfYear.val(d);\n// --> 81\n\nrecurrify.dayOfYear.isValid(d, 4);\n// --> false\n\nrecurrify.dayOfYear.extent(d);\n// --> [1, 365]\n\nrecurrify.dayOfYear.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfYear.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.dayOfYear.next(d, 256);\n// --> 'Fri, 13 Sep 2013 00:00:00 GMT'\n\nrecurrify.dayOfYear.prev(d, 44);\n// --> 'Wed, 13 Feb 2013 23:59:59 GMT'\n```\n\n## Week of month (weekOfMonth, wm)\nWeek of the month where the 1st of the month is week `1` and following weeks start on Sunday.\nFrom `1` to max weeks in the month.\nSpecify `0` for last week of the month.\n\nUsing weeks of month in a recurrence:\n\n`const sched = {recurrences: [{wm: [1, 2]}]}`\n\nPerforming week of month based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.weekOfMonth.name;\n// --> 'week of month'\n\nrecurrify.weekOfMonth.range;\n// --> 604800\n\nrecurrify.weekOfMonth.val(d);\n// --> 4\n\nrecurrify.weekOfMonth.isValid(d, 4);\n// --> true\n\nrecurrify.weekOfMonth.extent(d);\n// --> [1, 6]\n\nrecurrify.weekOfMonth.start(d);\n// --> 'Sun, 17 Mar 2013 00:00:00 GMT'\n\nrecurrify.weekOfMonth.end(d);\n// --> 'Sat, 23 Mar 2013 23:59:59 GMT'\n\nrecurrify.weekOfMonth.next(d, 1);\n// --> 'Mon, 01 Apr 2013 00:00:00 GMT'\n\nrecurrify.weekOfMonth.prev(d, 2);\n// --> 'Sat, 09 Mar 2013 23:59:59 GMT'\n```\n\n## ISO Week of year (weekOfYear, wy)\nThe ISO-8601 week of the year.\nFrom `1` to the max ISO week in the year.\nSpecify `0` for last ISO week of the year.\n\nUsing weeks of year in a recurrence:\n\n`const sched = {recurrences: [{wy: [13,26,39,0]}]}`\n\nPerforming week of year based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.weekOfYear.name;\n// --> 'week of year'\n\nrecurrify.weekOfYear.range;\n// --> 604800\n\nrecurrify.weekOfYear.val(d);\n// --> 12\n\nrecurrify.weekOfYear.isValid(d, 21);\n// --> false\n\nrecurrify.weekOfYear.extent(d);\n// --> [1, 52]\n\nrecurrify.weekOfYear.start(d);\n// --> 'Mon, 18 Mar 2013 00:00:00 GMT'\n\nrecurrify.weekOfYear.end(d);\n// --> 'Sun, 24 Mar 2013 23:59:59 GMT'\n\nrecurrify.weekOfYear.next(d, 47);\n// --> 'Mon, 18 Nov 2013 00:00:00 GMT'\n\nrecurrify.weekOfYear.prev(d, 52);\n// --> 'Sun, 30 Dec 2012 23:59:59 GMT'\n```\n\n## Month (month, M)\nMonth of the year, from `1` to `12`.\nSpecify `0` for the last month of the year.\n\nUsing months in a recurrence:\n\n`const sched = {recurrences: [{M: [3,5,7]}]}`\n\nPerforming months based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.month.name;\n// --> 'month'\n\nrecurrify.month.range;\n// --> 2629740\n\nrecurrify.month.val(d);\n// --> 3\n\nrecurrify.month.isValid(d, 3);\n// --> true\n\nrecurrify.month.extent();\n// --> [1, 12]\n\nrecurrify.month.start(d);\n// --> 'Fri, 01 Mar 2013 00:00:00 GMT'\n\nrecurrify.month.end(d);\n// --> 'Sun, 31 Mar 2013 23:59:59 GMT'\n\nrecurrify.month.next(d, 11);\n// --> 'Fri, 01 Nov 2013 00:00:00 GMT'\n\nrecurrify.month.prev(d, 2);\n// --> 'Thu, 28 Feb 2013 23:59:59 GMT'\n```\n\n## Year (year, Y)\nYear, from `1970` to `2099`.\n\nUsing years in a recurrence:\n\n`const sched = {recurrences: [{Y: [2013, 2014, 2015]}]}`\n\nPerforming years based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.year.name;\n// --> 'year'\n\nrecurrify.year.range;\n// --> 31556900\n\nrecurrify.year.val(d);\n// --> 2013\n\nrecurrify.year.isValid(d, 2013);\n// --> true\n\nrecurrify.year.extent();\n// --> [1970, 2099]\n\nrecurrify.year.start(d);\n// --> 'Tue, 01 Jan 2013 00:00:00 GMT'\n\nrecurrify.year.end(d);\n// --> 'Tue, 31 Dec 2013 23:59:59 GMT'\n\nrecurrify.year.next(d, 2014);\n// --> 'Wed, 01 Jan 2014 00:00:00 GMT'\n\nrecurrify.year.prev(d, 2012);\n// --> 'Mon, 31 Dec 2012 23:59:59 GMT'\n```\n\n## Writing a custom time unit\n`recurrify` is fully extensible and it is easy to create your own custom time units that can be used to define new recurrences.\nTo keep things simple, we'll walk through creating a new time unit for indicating morning, afternoon, and evening.\nFor our purposes, morning will be before noon and have a value of `0`, afternoon will be before 6pm and have a value of `1`, and evening will be before midnight and have a value of `2`.\n\nThe first step is to create a name and id for the modifier and add it to the recurrify namespace.\n\n```javascript\nrecurrify.partOfDay = recurrify.pd = {\n  // interface implementation goes here\n};\n```\n\nNext, we need to implement the time unit interface.\nFirst we will just specify the name of this time unit.\n\n`name: 'part of day',`\n\nThe range is approximately 6 hours.\nThough some of our periods are longer and some shorter,\nwe'll use the shortest range which is afternoon at 6 hours.\n\n`range: recurrify.h.range * 6,`\n\nWe then implement `val` to return the appropriate value based on the definition described previously.\n\n```javascript\nval: function(d) {\n  return recurrify.h.val(d) < 12 ? 0 :\n         recurrify.h.val(d) < 6 ? 1 :\n         2;\n},\n```\n\nThen we can use our new `val` function to implement `isValid`.\n\n```javascript\nisValid: function(d, val) {\n  return recurrify.pd.val(d) === val;\n},\n```\n\nThe extent is always going to be the same for every day so we can just return a constant array here.\n\n`extent: function(d) { return [0, 2]; },`\n\nNext we need to implement `start` and `end` based on the current time unit.\nThis will be the start and end of each part of the day that we've defined.\n\n```javascript\nstart: function(d) {\n  const hour = recurrify.pd.val(d) === 0 ? 0 :\n                recurrify.pd.val(d) === 1 ? 12 :\n                6;\n\n  // recurrify.date.next is a helper function for creating the date in UTC or\n  // localTime as appropriate\n  return recurrify.date.next(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    recurrify.D.val(d),\n    hour\n  );\n},\n\nend: function(d) {\n  const hour = recurrify.pd.val(d) === 0 ? 11 :\n                recurrify.pd.val(d) === 1 ? 5 :\n                23;\n\n  // recurrify.date.prev is a helper function for creating the date in UTC or\n  // localTime as appropriate, and automatically adjusts the date to be at\n  // the last second of the specified time\n  return recurrify.date.prev(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    recurrify.D.val(d),\n    hour\n  );\n},\n```\n\nFinally, we need to implement `next` and `prev` so that you can move to different parts of the day.\nWe need to make sure to increment and decrement the day appropriately if we've already passed the specified value.\n\n```javascript\nnext: function(d, val) {\n  const hour = val === 0 ? 0 : val === 1 ? 12 : 18;\n\n  return recurrify.date.next(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    // increment the day if we already passed the desired time unit\n    recurrify.D.val(d) + (hour < recurrify.h.val(d) ? 1 : 0),\n    hour\n  );\n},\n\nprev: function(d, val) {\n  const hour = val === 0 ? 11 : val === 1 ? 5 : 23;\n\n  return recurrify.date.prev(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    // decrement the day if we already passed the desired time unit\n    recurrify.D.val(d) + (hour > recurrify.h.val(d) ? -1 : 0),\n    hour\n  );\n}\n```\n\n### Full implementation\nHere is the code for the completed example.\nTo use the time unit, just add this code after including `recurrify` into your project and before you use it in any recurrences.\n\n```javascript\nrecurrify.partOfDay = recurrify.pd = {\n\n  name: 'part of day',\n\n  range: recurrify.h.range * 6,\n\n  val: function(d) {\n    return recurrify.h.val(d) < 12 ? 0 :\n           recurrify.h.val(d) < 18 ? 1 :\n           2;\n  },\n\n  isValid: function(d, val) {\n    return recurrify.pd.val(d) === val;\n  },\n\n  extent: function(d) { return [0, 2]; },\n\n  start: function(d) {\n    const hour = recurrify.pd.val(d) === 0 ? 0 :\n                  recurrify.pd.val(d) === 1 ? 12 :\n                  18;\n\n    return recurrify.date.next(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      recurrify.D.val(d),\n      hour\n    );\n  },\n\n  end: function(d) {\n    const hour = recurrify.pd.val(d) === 0 ? 11 :\n                  recurrify.pd.val(d) === 1 ? 5 :\n                  23;\n\n    return recurrify.date.prev(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      recurrify.D.val(d),\n      hour\n    );\n  },\n\n  next: function(d, val) {\n    const hour = val === 0 ? 0 : val === 1 ? 12 : 18;\n\n    return recurrify.date.next(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      // increment the day if we already passed the desired time unit\n      recurrify.D.val(d) + (hour < recurrify.h.val(d) ? 1 : 0),\n      hour\n    );\n  },\n\n  prev: function(d, val) {\n    const hour = val === 0 ? 11 : val === 1 ? 5 : 23;\n\n    return recurrify.date.prev(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      // decrement the day if we already passed the desired time unit\n      recurrify.D.val(d) + (hour > recurrify.h.val(d) ? -1 : 0),\n      hour\n    );\n  }\n};\n```\n\n### Usage\nUsing the custom time unit is exactly the same as using a built-in time unit.\n\n```javascript\n// use our new time unit to specify every 15 mins at night\nconst sched = recurrify.parse.recur().every(15).minute().on(2).customPeriod('pd'),\n    next = recurrify.recurrence(sched).next(1, new Date(2013, 3, 21));\n\nconsole.log(next.toUTCString());\n// --> Sun, 21 Apr 2013 18:00:00 GMT\n```\n\n## Modifiers\nWith `recurrify`, not only can you write your own custom time units, you can also write custom modifiers that can change the behavior of existing time units.\nThe modifiers sit in between the scheduling engine and the time unit allowing you to intercept and modify the results that are returned by the time unit.\n\nModifies are specified by attaching `_(modifier-id)` to the time unit id that you want to modify.\nThe same time unit can be used with different modifiers within the same recurrence.\n\n## after: `_a`\nModifies the corresponding time unit such that all values after and including the specified value is considered valid.\nThis modifier can be used with any time unit.\nUseful for creating more compact recurrences when a time unit has a lot of consecutive valid values.\n\n```javascript\n// all hours after 5:00pm will be valid\nconst sched = {recurrences: [{h_a: [17]}]};\n\n// equivalent to\nconst sched = {recurrences: [{h: [17,18,19,20,21,22,23]}]};\n```\n\n## before `_b`\nModifies the corresponding time unit such that all values before (but not including) the specified value is considered valid.\nThis modifier can be used with any time unit.\nUseful for creating more compact recurrences when a time unit has a lot of consecutive valid values.\n\n```javascript\n// all hours before 5:00pm will be valid\nconst sched = {recurrences: [{h_b: [17]}]};\n\n// equivalent to\nconst sched = {recurrences: [{h: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}]};\n```\n\n## Writing a custom modifier\nCustom modifiers are very similar to custom time units and share the same interface.\nTo keep things simple, we'll walk through creating a modifier to change the `month` period to work with values `0`-`11` instead of `1`-`12`.\n\nThe first step is to create a name and id for the modifier and add it to the modifier namespace.\nModifiers take the time unit that is being modified along with the specified values as arguments.\n\n```javascript\nrecurrify.modifier.month = recurrify.modifier.m = function(period, values) {\n  if(period.name !== 'month') {\n    throw new Error('Month modifier only works with months!');\n  }\n\n  return {\n    // interface implementation goes here\n  };\n};\n```\n\nNext, we need to implement the same interface that time units implement and modify them to work with the new set of values.\nFirst, we will just modify the `name` to include a reference to the modifier.\n\n`name: 'reIndexed ' + period.name,`\n\nThe range is the same, so we just pass it through.\n\n`range: period.range,`\n\nWe then modify the `val` returned by subtracting `1` since our new indices are one less than the original ones.\n\n`val: function(d) { return period.val(d) - 1; },`\n\nNext, `isValid` is modified by tweaking the value that is passed in so that it is in the range that the month time unit expects.\n\n`isValid: function(d, val) { return period.isValid(d, val+1); },`\n\nThe `extent` also needs to be modified to reflect the new extent that goes from `0`-`11`.\nNow that the extent starts at `0`, the recurrence engine will no longer assume that a `0` value means \"last\".\nThere is nothing else that we need to do to correct for that behavior.\n\n`extent: function(d) { return [0, 11]; },`\n\nThe `start` and `end` dates for the month will be the same, so we can just pass those through to the time unit without modification.\n\n```javascript\nstart: period.start,\nend: period.end,\n```\n\nFinally, the values passed into `next` and `prev` need to be updated to be in the range that the month time unit expects.\n\n```javascript\nnext: function(d, val) { return period.next(d, val+1); },\nprev: function(d, val) { return period.prev(d, val+1); }\n```\n\n### Full implementation\nHere is the code for the completed example.\nTo use the modifier, just add this code after including `recurrify` into your project and before you use it in any recurrences.\n\n```javascript\nrecurrify.modifier.month = recurrify.modifier.m = function(period, values) {\n  if(period.name !== 'month') {\n    throw new Error('Month modifier only works with months!');\n  }\n\n  return {\n    name:     'reIndexed ' + period.name,\n    range:    period.range,\n    val:      function(d) { return period.val(d) - 1; },\n    isValid:  function(d, val) { return period.isValid(d, val+1); },\n    extent:   function(d) { return [0, 11]; },\n    start:    period.start,\n    end:      period.end,\n    next:     function(d, val) { return period.next(d, val+1); },\n    prev:     function(d, val) { return period.prev(d, val+1); }\n  };\n};\n```\n\n### Usage\nUsing the custom modifier is exactly the same as using a built-in modifier.\n\n```javascript\n// without our modifier, 2 means February\nconst sched1 = {recurrences: [{M: [2]}]};\n\nrecurrify.recurrence(sched1).next(1, new Date(2013, 3, 21));\n// --> Sat, 01 Feb 2014 00:00:00 GMT\n\n// use our new modifier so that 2 now means March\nconst sched = recurrify.parse.recur().customModifier('m', 2).month();\n\nnext = recurrify.recurrence(sched2).next(1, new Date(2013, 3, 21));\n// --> Sat, 01 Mar 2014 00:00:00 GMT\n```\n\n## Time Slots\nOnce a recurrence has been defined, it can be used to calculate future and past time slots of that recurrence.\nA time slot is a date that meets all of the constraints imposed by the recurrence.\n\nIn order to improve performance, recurrences are first compiled prior to time slots being calculated.\nThe compiled version of the recurrence can be reused to find additional time slots as needed.\n\nTo compile a recurrence, pass the recurrence definition to `recurrify.recurrence`.\n\n`const sched = recurrify.recurrence(recurrence)`\n\n> TIP: All recurrence definitions are timezone agnostic.\nWhen you need to calculate time slots, you can decide to perform the calculation using local time or UTC.\n\n```javascript\n// set recurrify to use UTC time (the default)\nrecurrify.date.UTC();\n\n// set recurrify to use local time\nrecurrify.date.localTime();\n```\n\n## `isValid(date)`\nReturns true if the `date` passed in is a valid time slot of the recurrence, false otherwise.\n\n`const valid = recurrify.recurrence(recurrence).isValid(date)`\n\n### Examples\n\n```javascript\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute());\n\nsched.isValid(new Date('2013-03-22T10:02:00Z'));\n// --> true\n\nsched.isValid(new Date('2013-03-22T10:22:00Z'));\n// --> false\n\nsched.isValid(new Date('2013-03-22T10:02:05Z'));\n// --> false\n```\n\n## Calculating instances\nInstances are individual dates that meet all of the constraints that are imposed by the recurrence.\nInstances can be calculated both forwards and backwards, in any quantity, and optionally between a start and end date.\nWhen calculating multiple instances, the minimum time between instances is based on the smallest ranged time unit.\n\n**`recurrify.recurrence(recurrence).all(start, end)`**\nCalculates *all* time slots of `recurrence` starting from the `start` date and ending before the `end` date.\nIf an end date is not specified, the maximum results returned is `1000000`.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).all(startDate, endDate)`\n\n**`recurrify.recurrence(recurrence*).next(*count, start, end*)`**\nCalculates the next `count` time slots of `recurrence`, optionally starting from the `start` date and ending before the `end` date.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).next(2)`\n\n\n**`recurrify.recurrence(recurrence).prev(count, start, end)`**\nCalculates the previous `count` time slots of `recurrence`, optionally starting from the `start` date and ending before the `end` date.\nWhen using previous, the `start` date must be greater than the `end` date.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).prev(2)`\n\n### Examples\n```javascript\n// sched for minute equal to 1,2, or 3\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute()),\n    start = new Date('2013-05-22T10:22:00Z');\n\n// get the next instance\nsched.next(1, start);\n// --> Mon, 22 May 2013 11:01:00 GMT\n\n// get the next 5 instances\nsched.next(5, start);\n// --> [ 'Mon, 22 May 2013 11:01:00 GMT',\n      'Mon, 22 May 2013 11:02:00 GMT',\n      'Mon, 22 May 2013 11:03:00 GMT',\n      'Mon, 22 May 2013 12:01:00 GMT',\n      'Mon, 22 May 2013 12:02:00 GMT' ]\n\n// get the previous instance\nsched.prev(1, start);\n// --> Mon, 22 May 2013 10:03:00 GMT\n```\n\n## Calculating ranges\nRanges combine consecutively valid instances into a single start and end block of time.\nThe start time is the first valid instance of the block of time.\nThe end time is the first invalid time after the block.\n\nRanges are useful when scheduling blocks of time such as a meeting or activity.\nThe recurrence definition defines the start and end time of the activity and then ranges are used to find their time slots.\n\n**`recurrify.recurrence(recurrence).nextRange(count, start, end)`**\nCalculates the next count ranges of recurrence, optionally starting from the start date and ending before the end date.\n\n`recurrify.recurrence({recurrences: [{m:[5,6,7]}]}).nextRange(2)`\n\n**`recurrify.recurrence(recurrence).prevRange(count, start, end)`**\nCalculates the previous count ranges of recurrence, optionally starting from the start date and ending before the end date.\n\n`recurrify.recurrence({recurrences: [{m:[5,6,7]}]}).prevRange(2)`\n\n### Examples\n```javascript\n// sched for minute equal to 1,2, or 3\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute()),\n    start = new Date('2013-05-22T10:22:00Z');\n\n// get the next range\nsched.nextRange(1, start);\n// --> ['Mon, 22 May 2013 11:01:00 GMT', 'Mon, 22 May 2013 11:04:00 GMT']\n\n// get the next 5 ranges\nsched.nextRange(5, start);\n// --> [\n      ['Mon, 22 May 2013 11:01:00 GMT', 'Mon, 22 May 2013 11:04:00 GMT']\n      ['Mon, 22 May 2013 12:01:00 GMT', 'Mon, 22 May 2013 12:04:00 GMT']\n      ['Mon, 22 May 2013 13:01:00 GMT', 'Mon, 22 May 2013 13:04:00 GMT']\n      ['Mon, 22 May 2013 14:01:00 GMT', 'Mon, 22 May 2013 14:04:00 GMT']\n      ['Mon, 22 May 2013 15:01:00 GMT', 'Mon, 22 May 2013 15:04:00 GMT']\n    ]\n\n// get the previous range\nsched.prevRange(1, start);\n// --> ['Mon, 22 May 2013 10:01:00 GMT', 'Mon, 22 May 2013 10:04:00 GMT']\n```\n","engines":{"node":">=8.17.0"},"gitHead":"56135596fe1533c59b1a8449f7de577e136fafc6","scripts":{"i":"mocha 'src/test/integration/**/*-test.js'","u":"mocha 'src/test/unit/**/*-test.js'","cov":"open coverage/index.html","doc":"jsdoc --verbose -d docs -r -c jsdoc.json -R README.md src/main","lic":"node lic.js > LICENSE","lint":"standard --verbose 'src/**/*.js'","nuke":"rimraf node_modules","test":"run-s transpile unit-integration lint","unit":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js'","build":"npm install && npm test","clean":"rimraf dist docs coverage .nyc_output","format":"standard --fix 'src/**/*.js'","report":"nyc report --reporter=html","prenuke":"npm run clean","postunit":"run-s report","transpile":"run-s transpile-main transpile-test","integration":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/integration/**/*-test.js'","transpile-main":"babel --delete-dir-on-start --verbose --out-dir dist/main --copy-files src/main","transpile-test":"babel --delete-dir-on-start --verbose --out-dir dist/test --copy-files src/test","postintegration":"run-s report","unit-integration":"nyc --check-coverage -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js' 'dist/test/integration/**/*-test.js'","make-distribution":"run-s transpile doc lic","postunit-integration":"run-s report"},"_npmUser":{"name":"npm_northscaler","email":"npm@northscaler.com"},"standard":{"parser":"babel-eslint"},"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"_npmVersion":"6.14.4","description":"Find time slots given a recurrence","directories":{},"_nodeVersion":"14.1.0","dependencies":{"@babel/polyfill":"7.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":true,"readmeFilename":"README.md","devDependencies":{"nyc":"14.1.1","chai":"4.2.0","acorn":"7.1.0","jsdoc":"3.6.3","mocha":"6.2.2","should":"13.2.3","standard":"14.3.1","copyfiles":"2.1.1","@babel/cli":"7.7.4","dirty-chai":"2.0.1","@babel/core":"7.7.4","npm-run-all":"4.1.5","babel-eslint":"10.0.3","@babel/register":"7.7.4","lodash.template":"4.5.0","@babel/preset-env":"7.7.4","@northscaler/mutrait":"1.1.0","config-cev-generator":"0.1.6","@babel/plugin-proposal-optional-chaining":"7.7.4"},"_npmOperationalInternal":{"tmp":"tmp/recurrify_1.3.0-rc.0_1589213703650_0.83944567782494","host":"s3://npm-registry-packages"}},"1.3.0-rc.1":{"name":"@northscaler/recurrify","version":"1.3.0-rc.1","keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","_id":"@northscaler/recurrify@1.3.0-rc.1","maintainers":[{"name":"npm_northscaler","email":"npm@northscaler.com"},{"name":"npm_scispike","email":"npm@scispike.com"}],"homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"dist":{"shasum":"786cca23c840a8da8bae65ce5ec2b552edecf2ff","tarball":"https://registry.npmjs.org/@northscaler/recurrify/-/recurrify-1.3.0-rc.1.tgz","fileCount":82,"integrity":"sha512-Y5ytJn8PnSzK7VVQ+LryAgCZ0u+fIXT3pvLPWcWIcCwG70cs/UrnItEUH0Ny5FTdNA4PD3gucCk9EC7JapEung==","signatures":[{"sig":"MEYCIQDDf1kwUO5wCeItJb4NHBR9JEEPheFAPtSRCQZtJVJq1QIhAJKhiQo5mA7b2l8URgHJ4o1H4hNZgCv0AcfBjJiAnmnd","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":2564362,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeuXsFCRA9TVsSAnZWagAAjLMP/1mylEUXrxJJ6+ahFQkl\ns+M7MmUU+KcwLoXicOLTAjnP15csqxBvgQv7Ft28vvaau1ZZeKQr29oKEgNr\nDDu+csvheJb7GgkKqTjJb3Uq7cqOUpzIUubLdj6u2gdYSEr8/TaI7V44kwX1\nHgnPouI9yws10Rur1owEaedFuYRb8X3RJRlDtzGjgixVIZbppLODTGgxThYQ\ntsll1fjBODNDtJqQbjiVXxMeehOV9KpUKvWYF3oldfD3l/dhcIYN4zzFfSaZ\nvNwyS2P/otxtlL5nICjaL0g5GztQ004bOuVmP2YkDizCpR9Dk3VneOl78fA1\nYXHyMYAGuY4JIQ+sYfkH9fnq/cq2w75PueFVXv4Ic92sLIs3FD2RR024AuJs\nTbNIg2G9vZfWpKrtSldB2Y05LqT/FSbExRAz10lLZOtFuZDRNBN4O8C5NV9U\npNpAfJiDJxaInl/IA/xhpektsyYgttru7f4WsHnXnpQOQ+TxNLQHu58V77b2\n46MNL8EhiLegRSE95AjRSCjzF58sKRcYaT/eUnpZoWJ/sFPGZLgZFmffyCKb\nrLRbMhTmMplYP3bc7vQ0nzmM8pQO0PK5hbuHTtl7GG9LWMBxktqNHHaLKaGe\ntDwfBh20qWnOHTgKnYGAL8Dmra30yfcw3Txphqx4pMVe0bySMkzmFzfFKCp+\ngK5S\r\n=P+EE\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","babel":{"plugins":[["@babel/plugin-proposal-optional-chaining",{"loose":false}]],"presets":[["@babel/preset-env",{"targets":{"node":true}}]],"sourceMaps":"inline"},"mocha":{"colors":true,"require":"@babel/register"},"readme":"# recurrify\n\nBased on a shameless fork of [later.js](https://github.com/kirkins/later).\n\n* Added ability to get *all* time slots of a recurrence between a start and end time\n* Removed the parsers in favor of just dealing with recurrence definitions directly\n* Removed `setTimeout` and `setInterval` implementations\n* Removed bower and Makefile\n* Renamed `schedule` to `recurrence` and `occurrence` to `time slot`\n* Renamed package from `cronicle` to `recurrify`\n* WIP to update syntax to more modern ES6+\n\n## Installation\n\n`$ npm install recurrify`\n\n## Example usage\n\n```\nconst recurrify = require('recurrify')\n\nconst startDate = new Date('2018-10-01T13:00:00Z')\nconst endDate = new Date('2018-10-01T15:00:00Z')\n\n// every 5 minutes\nconst sched = { 'recurrences': [{ 'm': [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] }] }\nconst timeSlots = recurrify.recurrence(sched).all(startDate, endDate)\nconsole.log(timeSlots)\n```\n\n### Result\n```\n$ node example.js\n[ 2018-10-01T13:00:00.000Z,\n  2018-10-01T13:05:00.000Z,\n  2018-10-01T13:10:00.000Z,\n  2018-10-01T13:15:00.000Z,\n  2018-10-01T13:20:00.000Z,\n  2018-10-01T13:25:00.000Z,\n  2018-10-01T13:30:00.000Z,\n  2018-10-01T13:35:00.000Z,\n  2018-10-01T13:40:00.000Z,\n  2018-10-01T13:45:00.000Z,\n  2018-10-01T13:50:00.000Z,\n  2018-10-01T13:55:00.000Z,\n  2018-10-01T14:00:00.000Z,\n  2018-10-01T14:05:00.000Z,\n  2018-10-01T14:10:00.000Z,\n  2018-10-01T14:15:00.000Z,\n  2018-10-01T14:20:00.000Z,\n  2018-10-01T14:25:00.000Z,\n  2018-10-01T14:30:00.000Z,\n  2018-10-01T14:35:00.000Z,\n  2018-10-01T14:40:00.000Z,\n  2018-10-01T14:45:00.000Z,\n  2018-10-01T14:50:00.000Z,\n  2018-10-01T14:55:00.000Z,\n  2018-10-01T15:00:00.000Z ]\n```\n\n## Running tests\n\n`$ npm test`\n\n## Recurrences\nRecurrences in `recurrify` are json objects that define a set of time units along with the values that should be considered valid for that time unit.\nThe combination of a time unit with their valid values is called a *constraint*.\n`recurrify` then takes all of the constraints that have been defined and finds dates that match all of them.\n\nSince `recurrify` recurrences are json objects, they can easily be serialized and stored in caches and databases as needed.\nThey are also completely deterministic which means a recurrence will always produce exactly the same valid time slots.\nTherefore, time slots never need to be stored as they can always be recalculated from the recurrence definition.\n\n## Basic recurrences\nA basic recurrence is a set of time units along with their valid values.\nA date is only considered valid if it meets all of the constraints within a basic recurrence.\nA basic recurrence can include as many time units (with or without modifiers) as needed, in any order.\n\n```\n// a basic recurrence that is valid every day at 10:15am and 10:45am\nconst basic = {h: [10], m: [15,45]}\n```\n\nHere we can see a recurrence is made up of objects with properties that correspond to the various time units.\nIn this case `h` is the hour time unit and `m` is the minute time unit.\nThe values to consider valid are always stored in an array as the value of the property.\n\n\n**Note:** Basic recurrences are only valid as part of a complete recurrence definition.\n\n## Composite recurrences\nMultiple basic recurrences can be combined into a single composite recurrence by placing them into an array.\nA date is considered valid if any of the basic recurrences are valid (basically an OR of all of the basic recurrences).\nA composite recurrence can contain as many basic recurrences as needed.\n\n```\n// a composite recurrence that is valid every day at 10:15am and 10:45am\n// and every day at 5:30pm\nconst composite = [\n  {h: [10], m: [15,45]},\n  {h: [17], m: [30]}\n]\n\n```\n\n**Note:** Composite recurrences are only valid as part of a complete recurrence definition.\n\n## Exception recurrences\nAn exception recurrence is a basic or composite recurrence that defines when a recurrence should be considered invalid.\nA date is considered invalid if any of the basic recurrences within an exception recurrence are valid.\n\n```\n// an exception recurrence that makes any date in March as\n// well as any Monday of any month invalid\nconst exception = [\n  {M: [3]},\n  {dw: [2]}\n]\n```\n\n**Note:** Exception recurrences are only valid as part of a complete recurrence definition.\n\n## Complete definition\nA complete definition is a json object that at a minimum contains a `recurrences` property that defines a composite recurrence with at least one basic recurrence.\nOptionally, the definition can also include an `exceptions` property that defines a composite exception recurrence.\n\n### Examples\nHere's an example of a valid recurrence that fires every 10 minutes.\nThe composite recurrence is always placed in an object under a property named `recurrences`. Even if you only have a basic recurrence, the `recurrences` property must be an array.\n\n```\nconst recurrence = {\n  recurrences: [\n    {m: [0,10,20,30,40,50]}\n  ]\n}\n```\n\nHere's a valid recurrence definition that includes exceptions with modifiers.\nHere we see the composite exception recurrence is always placed in an object under a property named `exceptions`.\nEven if you only have a basic exception recurrence, the `exceptions` property must be an array.\n\n```\nconst recurrence = {\n  recurrences: [\n    {h: [10], m: [15,45]},\n    {h: [17], m: [30]}\n  ],\n  exceptions: [\n    {M_a: [3]},\n    {dw: [2]}\n  ]\n}\n```\n\n## Performance considerations\nWhile `recurrify` has been designed to efficiently calculate time slots for all types and complexities of recurrences, there are a few things to keep in mind for applications that have particularly high performance requirements.\n\n* Basic time units perform the best. These include years, months, days, hours, minutes, and seconds.\nCalculating ISO week of year is particularly expensive.\n* Recurrences without exceptions perform better than those with exceptions.\nDefining your recurrence without the need for exceptions will improve performance.\n* Use the `time` time unit instead of specifying hours and minutes separately when possible.\nReducing the number of constraints will generally improve performance.\n* Using `after` and `before` modifiers to eliminate the need for specifying a lot of valid values will improve performance, especially when calculating ranges.\n\n## time units\nTime units are the crux of the `recurrify` library and are used to define new recurrences.\n`recurrify` comes with a large assortment of time units and is also fully extensible making it easy to create custom time units.\n\nWhile time units are primarily used by `recurrify` to define recurrences and calculate time slots, they are also useful for performing time based calculations.\nCalculating values such as ISO week number, moving between days of the year, or figuring out how many days are in a month are all possible using the time unit interface.\n\nIf you don't see the time unit that you need for your recurrence, `recurrify` is fully extensible and it is easy to write your own.\nSee the custom time unit at the bottom of this page for an example.\n\n## Interface\nAll time units implement the same public interface for interacting with them:\n\n**`name`**\nThe name of the time unit.\n\n**`range`**\nThe rough number of seconds that are covered when moving from one instance of this time unit to the next instance.\n\n**`val(date)`**\nThe value of this time unit for the date specified.\n\n**`isValid(date, value)`**\nTrue if the specified value is valid for the specified date, false otherwise.\n\n**`extent(date)`**\nThe minimum and maximum valid values for the time unit for the specified date.\nIf the minimum value is not `0`, `0` can be specified in recurrences to indicate the maximum value.\nThis makes working with non-constant extents (like days in a month) easier.\n\n**`start(date)`**\nThe first second in which the value is the same as the value of the specified date.\nFor example, the start of an hour would be the hour with `0` minutes and `0` seconds.\n\n**`end(date)`**\nThe last second in which the value is the same as the value of the specified date.\nFor example, the end of an hour would be the hour with `59` minutes and `59` seconds.\n\n**`next(date, value)`**\nReturns the next date where the value is the value specified.\nSets the value to `1` if value specified is greater than the max allowed value.\n\n**`prev(`date, value`)**\nReturns the previous date where the value is the value specified.\nSets the value to the max allowed value if the value specified is greater than the max allowed value.\n\n## Second (second, s)\nSeconds in a minute, from `0` to `59`.\n\nUsing seconds in a recurrence:\n\n`const sched = {recurrences: [{s: [0, 15, 30, 45]}]}`\n\nPerforming seconds based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.second.name;\n// --> 'second'\n\nrecurrify.second.range;\n// --> 1\n\nrecurrify.second.val(d);\n// --> 5\n\nrecurrify.second.isValid(d, 10);\n// --> false\n\nrecurrify.second.extent();\n// --> [0, 59]\n\nrecurrify.second.start(d);\n// --> 'Fri, 22 Mar 2013 10:02:05 GMT'\n\nrecurrify.second.end(d);\n// --> 'Fri, 22 Mar 2013 10:02:05 GMT'\n\nrecurrify.second.next(d, 27);\n// --> 'Fri, 22 Mar 2013 10:02:27 GMT'\n\nrecurrify.second.prev(d, 27);\n// --> 'Fri, 22 Mar 2013 10:01:27 GMT'\n```\n\n## Minute (minute, m)\nMinutes in an hour, from `0` to `59`.\n\nUsing minutes in a recurrence:\n\n`const sched = {recurrences: [{m: [0, 15, 30, 45]}]}`\n\nPerforming minutes based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.minute.name;\n// --> 'minute'\n\nrecurrify.minute.range;\n// --> 60\n\nrecurrify.minute.val(d);\n// --> 2\n\nrecurrify.minute.isValid(d, 2);\n// --> true\n\nrecurrify.minute.extent();\n// --> [0, 59]\n\nrecurrify.minute.start(d);\n// --> 'Fri, 22 Mar 2013 10:02:00 GMT'\n\nrecurrify.minute.end(d);\n// --> 'Fri, 22 Mar 2013 10:02:59 GMT'\n\nrecurrify.minute.next(d, 27);\n// --> 'Fri, 22 Mar 2013 10:27:00 GMT'\n\nrecurrify.minute.prev(d, 27);\n// --> 'Fri, 22 Mar 2013 09:27:59 GMT'\n```\n\n## Hour (hour, h)\nHours in a day, from `0` to `23`.\n\nUsing hours in a recurrence:\n\n`const sched = {recurrences: [{h: [0, 5, 12]}]}`\n\nPerforming hours based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.hour.name;\n// --> 'hour'\n\nrecurrify.hour.range;\n// --> 3600\n\nrecurrify.hour.val(d);\n// --> 10\n\nrecurrify.hour.isValid(d, 2);\n// --> false\n\nrecurrify.hour.extent();\n// --> [0, 23]\n\nrecurrify.hour.start(d);\n// --> 'Fri, 22 Mar 2013 10:00:00 GMT'\n\nrecurrify.hour.end(d);\n// --> 'Fri, 22 Mar 2013 10:59:59 GMT'\n\nrecurrify.hour.next(d, 5);\n// --> 'Sat, 23 Mar 2013 05:00:00 GMT'\n\nrecurrify.hour.prev(d, 21);\n// --> 'Thu, 21 Mar 2013 21:59:59 GMT'\n```\n\n## Time (time, t)\nTime of day, represented as seconds since midnight. From `0` to `86399` (60 * 60 * 24 - 1).\n\n\nUsing time in a recurrence:\n\n`const sched = {recurrences: [{t: [6500]}]}`\n\nPerforming time based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.time.name;\n// --> 'time'\n\nrecurrify.time.range;\n// --> 1\n\nrecurrify.time.val(d);\n// --> 36125\n\nrecurrify.time.isValid(d, 36125);\n// --> true\n\nrecurrify.time.extent();\n// --> [0, 86399]\n\nrecurrify.time.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.time.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.time.next(d, 60);\n// --> 'Sat, 23 Mar 2013 00:01:00 GMT'\n\nrecurrify.time.prev(d, 60);\n// --> 'Fri, 22 Mar 2013 00:01:00 GMT'\n```\n\n## Day (day, D)\nDays of a month, from `1` to the max days in the month.\nSpecify `0` for the varying last day of the month, which is between `28` through `31`.\n\nUsing days in a recurrence:\n\n`const sched = {recurrences: [{D: [0]}]}`\n\nPerforming day based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.day.name;\n// --> 'day'\n\nrecurrify.day.range;\n// --> 86400\n\nrecurrify.day.val(d);\n// --> 22\n\nrecurrify.day.isValid(d, 3);\n// --> false\n\nrecurrify.day.extent(d);\n// --> [1, 31]\n\nrecurrify.day.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.day.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.day.next(d, 11);\n// --> 'Thu, 11 Apr 2013 00:00:00 GMT'\n\nrecurrify.day.prev(d, 2);\n// --> 'Sat, 02 Mar 2013 23:59:59 GMT'\n```\n\n## Day of week (dayOfWeek, dw, d)\nDays of a week, from `1` to `7`.\nSpecify `0` for the last day of the week (Saturday).\n\nUsing days of week in a recurrence:\n\n`const sched = {recurrences: [{dw: [2,3,4,5,6]}]}`\n\nPerforming day of week based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfWeek.name;\n// --> 'day of week'\n\nrecurrify.dayOfWeek.range;\n// --> 86400\n\nrecurrify.dayOfWeek.val(d);\n// --> 6\n\nrecurrify.dayOfWeek.isValid(d, 3);\n// --> false\n\nrecurrify.dayOfWeek.extent();\n// --> [1, 7]\n\nrecurrify.dayOfWeek.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeek.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.dayOfWeek.next(d, 1);\n// --> 'Sun, 24 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeek.prev(d, 5);\n// --> 'Thu, 21 Mar 2013 23:59:59 GMT'\n```\n\n## Day of week count (dayOfWeekCount, dc)\nThe nth day of the week within a month, from `1` to the max weeks in a month.\nSpecify `0` for the last day instance.\nUsed together with the day of the week time unit to specify things like the 2nd Tuesday or last Friday of a month.\n\nUsing days of week count in a recurrence:\n\n`const sched = {recurrences: [{dc: [2]}]}`\n\nPerforming day of week count based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfWeekCount.name;\n// --> 'day of week count'\n\nrecurrify.dayOfWeekCount.range;\n// --> 604800\n\nrecurrify.dayOfWeekCount.val(d);\n// --> 4\n\nrecurrify.dayOfWeekCount.isValid(d, 4);\n// --> true\n\nrecurrify.dayOfWeekCount.extent(d);\n// --> [1, 5]\n\nrecurrify.dayOfWeekCount.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeekCount.end(d);\n// --> 'Thu, 28 Mar 2013 23:59:59 GMT'\n\n// zero is special cased and means the last instance of\n// a day of the week in the month, instead of meaning the\n// first day of the week with the highest instance count\n// which would have been Mar 29 with value 5.\nrecurrify.dayOfWeekCount.next(d, 0);\n// --> 'Mon, 25 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeekCount.prev(d, 2);\n// --> 'Thu, 14 Mar 2013 23:59:59 GMT'\n```\n\n## Day of year (dayOfYear, dy)\nDay of the year, from `1` to the max days in year.\nSpecify `0` for last day of the year.\n\nUsing days of year in a recurrence:\n\n`const sched = {recurrences: [{dy: [189, 267]}]}`\n\nPerforming day of year based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfYear.name;\n// --> 'day of year'\n\nrecurrify.dayOfYear.range;\n// --> 86400\n\nrecurrify.dayOfYear.val(d);\n// --> 81\n\nrecurrify.dayOfYear.isValid(d, 4);\n// --> false\n\nrecurrify.dayOfYear.extent(d);\n// --> [1, 365]\n\nrecurrify.dayOfYear.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfYear.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.dayOfYear.next(d, 256);\n// --> 'Fri, 13 Sep 2013 00:00:00 GMT'\n\nrecurrify.dayOfYear.prev(d, 44);\n// --> 'Wed, 13 Feb 2013 23:59:59 GMT'\n```\n\n## Week of month (weekOfMonth, wm)\nWeek of the month where the 1st of the month is week `1` and following weeks start on Sunday.\nFrom `1` to max weeks in the month.\nSpecify `0` for last week of the month.\n\nUsing weeks of month in a recurrence:\n\n`const sched = {recurrences: [{wm: [1, 2]}]}`\n\nPerforming week of month based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.weekOfMonth.name;\n// --> 'week of month'\n\nrecurrify.weekOfMonth.range;\n// --> 604800\n\nrecurrify.weekOfMonth.val(d);\n// --> 4\n\nrecurrify.weekOfMonth.isValid(d, 4);\n// --> true\n\nrecurrify.weekOfMonth.extent(d);\n// --> [1, 6]\n\nrecurrify.weekOfMonth.start(d);\n// --> 'Sun, 17 Mar 2013 00:00:00 GMT'\n\nrecurrify.weekOfMonth.end(d);\n// --> 'Sat, 23 Mar 2013 23:59:59 GMT'\n\nrecurrify.weekOfMonth.next(d, 1);\n// --> 'Mon, 01 Apr 2013 00:00:00 GMT'\n\nrecurrify.weekOfMonth.prev(d, 2);\n// --> 'Sat, 09 Mar 2013 23:59:59 GMT'\n```\n\n## ISO Week of year (weekOfYear, wy)\nThe ISO-8601 week of the year.\nFrom `1` to the max ISO week in the year.\nSpecify `0` for last ISO week of the year.\n\nUsing weeks of year in a recurrence:\n\n`const sched = {recurrences: [{wy: [13,26,39,0]}]}`\n\nPerforming week of year based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.weekOfYear.name;\n// --> 'week of year'\n\nrecurrify.weekOfYear.range;\n// --> 604800\n\nrecurrify.weekOfYear.val(d);\n// --> 12\n\nrecurrify.weekOfYear.isValid(d, 21);\n// --> false\n\nrecurrify.weekOfYear.extent(d);\n// --> [1, 52]\n\nrecurrify.weekOfYear.start(d);\n// --> 'Mon, 18 Mar 2013 00:00:00 GMT'\n\nrecurrify.weekOfYear.end(d);\n// --> 'Sun, 24 Mar 2013 23:59:59 GMT'\n\nrecurrify.weekOfYear.next(d, 47);\n// --> 'Mon, 18 Nov 2013 00:00:00 GMT'\n\nrecurrify.weekOfYear.prev(d, 52);\n// --> 'Sun, 30 Dec 2012 23:59:59 GMT'\n```\n\n## Month (month, M)\nMonth of the year, from `1` to `12`.\nSpecify `0` for the last month of the year.\n\nUsing months in a recurrence:\n\n`const sched = {recurrences: [{M: [3,5,7]}]}`\n\nPerforming months based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.month.name;\n// --> 'month'\n\nrecurrify.month.range;\n// --> 2629740\n\nrecurrify.month.val(d);\n// --> 3\n\nrecurrify.month.isValid(d, 3);\n// --> true\n\nrecurrify.month.extent();\n// --> [1, 12]\n\nrecurrify.month.start(d);\n// --> 'Fri, 01 Mar 2013 00:00:00 GMT'\n\nrecurrify.month.end(d);\n// --> 'Sun, 31 Mar 2013 23:59:59 GMT'\n\nrecurrify.month.next(d, 11);\n// --> 'Fri, 01 Nov 2013 00:00:00 GMT'\n\nrecurrify.month.prev(d, 2);\n// --> 'Thu, 28 Feb 2013 23:59:59 GMT'\n```\n\n## Year (year, Y)\nYear, from `1970` to `2099`.\n\nUsing years in a recurrence:\n\n`const sched = {recurrences: [{Y: [2013, 2014, 2015]}]}`\n\nPerforming years based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.year.name;\n// --> 'year'\n\nrecurrify.year.range;\n// --> 31556900\n\nrecurrify.year.val(d);\n// --> 2013\n\nrecurrify.year.isValid(d, 2013);\n// --> true\n\nrecurrify.year.extent();\n// --> [1970, 2099]\n\nrecurrify.year.start(d);\n// --> 'Tue, 01 Jan 2013 00:00:00 GMT'\n\nrecurrify.year.end(d);\n// --> 'Tue, 31 Dec 2013 23:59:59 GMT'\n\nrecurrify.year.next(d, 2014);\n// --> 'Wed, 01 Jan 2014 00:00:00 GMT'\n\nrecurrify.year.prev(d, 2012);\n// --> 'Mon, 31 Dec 2012 23:59:59 GMT'\n```\n\n## Writing a custom time unit\n`recurrify` is fully extensible and it is easy to create your own custom time units that can be used to define new recurrences.\nTo keep things simple, we'll walk through creating a new time unit for indicating morning, afternoon, and evening.\nFor our purposes, morning will be before noon and have a value of `0`, afternoon will be before 6pm and have a value of `1`, and evening will be before midnight and have a value of `2`.\n\nThe first step is to create a name and id for the modifier and add it to the recurrify namespace.\n\n```javascript\nrecurrify.partOfDay = recurrify.pd = {\n  // interface implementation goes here\n};\n```\n\nNext, we need to implement the time unit interface.\nFirst we will just specify the name of this time unit.\n\n`name: 'part of day',`\n\nThe range is approximately 6 hours.\nThough some of our periods are longer and some shorter,\nwe'll use the shortest range which is afternoon at 6 hours.\n\n`range: recurrify.h.range * 6,`\n\nWe then implement `val` to return the appropriate value based on the definition described previously.\n\n```javascript\nval: function(d) {\n  return recurrify.h.val(d) < 12 ? 0 :\n         recurrify.h.val(d) < 6 ? 1 :\n         2;\n},\n```\n\nThen we can use our new `val` function to implement `isValid`.\n\n```javascript\nisValid: function(d, val) {\n  return recurrify.pd.val(d) === val;\n},\n```\n\nThe extent is always going to be the same for every day so we can just return a constant array here.\n\n`extent: function(d) { return [0, 2]; },`\n\nNext we need to implement `start` and `end` based on the current time unit.\nThis will be the start and end of each part of the day that we've defined.\n\n```javascript\nstart: function(d) {\n  const hour = recurrify.pd.val(d) === 0 ? 0 :\n                recurrify.pd.val(d) === 1 ? 12 :\n                6;\n\n  // recurrify.date.next is a helper function for creating the date in UTC or\n  // localTime as appropriate\n  return recurrify.date.next(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    recurrify.D.val(d),\n    hour\n  );\n},\n\nend: function(d) {\n  const hour = recurrify.pd.val(d) === 0 ? 11 :\n                recurrify.pd.val(d) === 1 ? 5 :\n                23;\n\n  // recurrify.date.prev is a helper function for creating the date in UTC or\n  // localTime as appropriate, and automatically adjusts the date to be at\n  // the last second of the specified time\n  return recurrify.date.prev(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    recurrify.D.val(d),\n    hour\n  );\n},\n```\n\nFinally, we need to implement `next` and `prev` so that you can move to different parts of the day.\nWe need to make sure to increment and decrement the day appropriately if we've already passed the specified value.\n\n```javascript\nnext: function(d, val) {\n  const hour = val === 0 ? 0 : val === 1 ? 12 : 18;\n\n  return recurrify.date.next(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    // increment the day if we already passed the desired time unit\n    recurrify.D.val(d) + (hour < recurrify.h.val(d) ? 1 : 0),\n    hour\n  );\n},\n\nprev: function(d, val) {\n  const hour = val === 0 ? 11 : val === 1 ? 5 : 23;\n\n  return recurrify.date.prev(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    // decrement the day if we already passed the desired time unit\n    recurrify.D.val(d) + (hour > recurrify.h.val(d) ? -1 : 0),\n    hour\n  );\n}\n```\n\n### Full implementation\nHere is the code for the completed example.\nTo use the time unit, just add this code after including `recurrify` into your project and before you use it in any recurrences.\n\n```javascript\nrecurrify.partOfDay = recurrify.pd = {\n\n  name: 'part of day',\n\n  range: recurrify.h.range * 6,\n\n  val: function(d) {\n    return recurrify.h.val(d) < 12 ? 0 :\n           recurrify.h.val(d) < 18 ? 1 :\n           2;\n  },\n\n  isValid: function(d, val) {\n    return recurrify.pd.val(d) === val;\n  },\n\n  extent: function(d) { return [0, 2]; },\n\n  start: function(d) {\n    const hour = recurrify.pd.val(d) === 0 ? 0 :\n                  recurrify.pd.val(d) === 1 ? 12 :\n                  18;\n\n    return recurrify.date.next(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      recurrify.D.val(d),\n      hour\n    );\n  },\n\n  end: function(d) {\n    const hour = recurrify.pd.val(d) === 0 ? 11 :\n                  recurrify.pd.val(d) === 1 ? 5 :\n                  23;\n\n    return recurrify.date.prev(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      recurrify.D.val(d),\n      hour\n    );\n  },\n\n  next: function(d, val) {\n    const hour = val === 0 ? 0 : val === 1 ? 12 : 18;\n\n    return recurrify.date.next(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      // increment the day if we already passed the desired time unit\n      recurrify.D.val(d) + (hour < recurrify.h.val(d) ? 1 : 0),\n      hour\n    );\n  },\n\n  prev: function(d, val) {\n    const hour = val === 0 ? 11 : val === 1 ? 5 : 23;\n\n    return recurrify.date.prev(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      // decrement the day if we already passed the desired time unit\n      recurrify.D.val(d) + (hour > recurrify.h.val(d) ? -1 : 0),\n      hour\n    );\n  }\n};\n```\n\n### Usage\nUsing the custom time unit is exactly the same as using a built-in time unit.\n\n```javascript\n// use our new time unit to specify every 15 mins at night\nconst sched = recurrify.parse.recur().every(15).minute().on(2).customPeriod('pd'),\n    next = recurrify.recurrence(sched).next(1, new Date(2013, 3, 21));\n\nconsole.log(next.toUTCString());\n// --> Sun, 21 Apr 2013 18:00:00 GMT\n```\n\n## Modifiers\nWith `recurrify`, not only can you write your own custom time units, you can also write custom modifiers that can change the behavior of existing time units.\nThe modifiers sit in between the scheduling engine and the time unit allowing you to intercept and modify the results that are returned by the time unit.\n\nModifies are specified by attaching `_(modifier-id)` to the time unit id that you want to modify.\nThe same time unit can be used with different modifiers within the same recurrence.\n\n## after: `_a`\nModifies the corresponding time unit such that all values after and including the specified value is considered valid.\nThis modifier can be used with any time unit.\nUseful for creating more compact recurrences when a time unit has a lot of consecutive valid values.\n\n```javascript\n// all hours after 5:00pm will be valid\nconst sched = {recurrences: [{h_a: [17]}]};\n\n// equivalent to\nconst sched = {recurrences: [{h: [17,18,19,20,21,22,23]}]};\n```\n\n## before `_b`\nModifies the corresponding time unit such that all values before (but not including) the specified value is considered valid.\nThis modifier can be used with any time unit.\nUseful for creating more compact recurrences when a time unit has a lot of consecutive valid values.\n\n```javascript\n// all hours before 5:00pm will be valid\nconst sched = {recurrences: [{h_b: [17]}]};\n\n// equivalent to\nconst sched = {recurrences: [{h: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}]};\n```\n\n## Writing a custom modifier\nCustom modifiers are very similar to custom time units and share the same interface.\nTo keep things simple, we'll walk through creating a modifier to change the `month` period to work with values `0`-`11` instead of `1`-`12`.\n\nThe first step is to create a name and id for the modifier and add it to the modifier namespace.\nModifiers take the time unit that is being modified along with the specified values as arguments.\n\n```javascript\nrecurrify.modifier.month = recurrify.modifier.m = function(period, values) {\n  if(period.name !== 'month') {\n    throw new Error('Month modifier only works with months!');\n  }\n\n  return {\n    // interface implementation goes here\n  };\n};\n```\n\nNext, we need to implement the same interface that time units implement and modify them to work with the new set of values.\nFirst, we will just modify the `name` to include a reference to the modifier.\n\n`name: 'reIndexed ' + period.name,`\n\nThe range is the same, so we just pass it through.\n\n`range: period.range,`\n\nWe then modify the `val` returned by subtracting `1` since our new indices are one less than the original ones.\n\n`val: function(d) { return period.val(d) - 1; },`\n\nNext, `isValid` is modified by tweaking the value that is passed in so that it is in the range that the month time unit expects.\n\n`isValid: function(d, val) { return period.isValid(d, val+1); },`\n\nThe `extent` also needs to be modified to reflect the new extent that goes from `0`-`11`.\nNow that the extent starts at `0`, the recurrence engine will no longer assume that a `0` value means \"last\".\nThere is nothing else that we need to do to correct for that behavior.\n\n`extent: function(d) { return [0, 11]; },`\n\nThe `start` and `end` dates for the month will be the same, so we can just pass those through to the time unit without modification.\n\n```javascript\nstart: period.start,\nend: period.end,\n```\n\nFinally, the values passed into `next` and `prev` need to be updated to be in the range that the month time unit expects.\n\n```javascript\nnext: function(d, val) { return period.next(d, val+1); },\nprev: function(d, val) { return period.prev(d, val+1); }\n```\n\n### Full implementation\nHere is the code for the completed example.\nTo use the modifier, just add this code after including `recurrify` into your project and before you use it in any recurrences.\n\n```javascript\nrecurrify.modifier.month = recurrify.modifier.m = function(period, values) {\n  if(period.name !== 'month') {\n    throw new Error('Month modifier only works with months!');\n  }\n\n  return {\n    name:     'reIndexed ' + period.name,\n    range:    period.range,\n    val:      function(d) { return period.val(d) - 1; },\n    isValid:  function(d, val) { return period.isValid(d, val+1); },\n    extent:   function(d) { return [0, 11]; },\n    start:    period.start,\n    end:      period.end,\n    next:     function(d, val) { return period.next(d, val+1); },\n    prev:     function(d, val) { return period.prev(d, val+1); }\n  };\n};\n```\n\n### Usage\nUsing the custom modifier is exactly the same as using a built-in modifier.\n\n```javascript\n// without our modifier, 2 means February\nconst sched1 = {recurrences: [{M: [2]}]};\n\nrecurrify.recurrence(sched1).next(1, new Date(2013, 3, 21));\n// --> Sat, 01 Feb 2014 00:00:00 GMT\n\n// use our new modifier so that 2 now means March\nconst sched = recurrify.parse.recur().customModifier('m', 2).month();\n\nnext = recurrify.recurrence(sched2).next(1, new Date(2013, 3, 21));\n// --> Sat, 01 Mar 2014 00:00:00 GMT\n```\n\n## Time Slots\nOnce a recurrence has been defined, it can be used to calculate future and past time slots of that recurrence.\nA time slot is a date that meets all of the constraints imposed by the recurrence.\n\nIn order to improve performance, recurrences are first compiled prior to time slots being calculated.\nThe compiled version of the recurrence can be reused to find additional time slots as needed.\n\nTo compile a recurrence, pass the recurrence definition to `recurrify.recurrence`.\n\n`const sched = recurrify.recurrence(recurrence)`\n\n> TIP: All recurrence definitions are timezone agnostic.\nWhen you need to calculate time slots, you can decide to perform the calculation using local time or UTC.\n\n```javascript\n// set recurrify to use UTC time (the default)\nrecurrify.date.UTC();\n\n// set recurrify to use local time\nrecurrify.date.localTime();\n```\n\n## `isValid(date)`\nReturns true if the `date` passed in is a valid time slot of the recurrence, false otherwise.\n\n`const valid = recurrify.recurrence(recurrence).isValid(date)`\n\n### Examples\n\n```javascript\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute());\n\nsched.isValid(new Date('2013-03-22T10:02:00Z'));\n// --> true\n\nsched.isValid(new Date('2013-03-22T10:22:00Z'));\n// --> false\n\nsched.isValid(new Date('2013-03-22T10:02:05Z'));\n// --> false\n```\n\n## Calculating instances\nInstances are individual dates that meet all of the constraints that are imposed by the recurrence.\nInstances can be calculated both forwards and backwards, in any quantity, and optionally between a start and end date.\nWhen calculating multiple instances, the minimum time between instances is based on the smallest ranged time unit.\n\n**`recurrify.recurrence(recurrence).all(start, end)`**\nCalculates *all* time slots of `recurrence` starting from the `start` date and ending before the `end` date.\nIf an end date is not specified, the maximum results returned is `1000000`.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).all(startDate, endDate)`\n\n**`recurrify.recurrence(recurrence*).next(*count, start, end*)`**\nCalculates the next `count` time slots of `recurrence`, optionally starting from the `start` date and ending before the `end` date.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).next(2)`\n\n\n**`recurrify.recurrence(recurrence).prev(count, start, end)`**\nCalculates the previous `count` time slots of `recurrence`, optionally starting from the `start` date and ending before the `end` date.\nWhen using previous, the `start` date must be greater than the `end` date.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).prev(2)`\n\n### Examples\n```javascript\n// sched for minute equal to 1,2, or 3\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute()),\n    start = new Date('2013-05-22T10:22:00Z');\n\n// get the next instance\nsched.next(1, start);\n// --> Mon, 22 May 2013 11:01:00 GMT\n\n// get the next 5 instances\nsched.next(5, start);\n// --> [ 'Mon, 22 May 2013 11:01:00 GMT',\n      'Mon, 22 May 2013 11:02:00 GMT',\n      'Mon, 22 May 2013 11:03:00 GMT',\n      'Mon, 22 May 2013 12:01:00 GMT',\n      'Mon, 22 May 2013 12:02:00 GMT' ]\n\n// get the previous instance\nsched.prev(1, start);\n// --> Mon, 22 May 2013 10:03:00 GMT\n```\n\n## Calculating ranges\nRanges combine consecutively valid instances into a single start and end block of time.\nThe start time is the first valid instance of the block of time.\nThe end time is the first invalid time after the block.\n\nRanges are useful when scheduling blocks of time such as a meeting or activity.\nThe recurrence definition defines the start and end time of the activity and then ranges are used to find their time slots.\n\n**`recurrify.recurrence(recurrence).nextRange(count, start, end)`**\nCalculates the next count ranges of recurrence, optionally starting from the start date and ending before the end date.\n\n`recurrify.recurrence({recurrences: [{m:[5,6,7]}]}).nextRange(2)`\n\n**`recurrify.recurrence(recurrence).prevRange(count, start, end)`**\nCalculates the previous count ranges of recurrence, optionally starting from the start date and ending before the end date.\n\n`recurrify.recurrence({recurrences: [{m:[5,6,7]}]}).prevRange(2)`\n\n### Examples\n```javascript\n// sched for minute equal to 1,2, or 3\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute()),\n    start = new Date('2013-05-22T10:22:00Z');\n\n// get the next range\nsched.nextRange(1, start);\n// --> ['Mon, 22 May 2013 11:01:00 GMT', 'Mon, 22 May 2013 11:04:00 GMT']\n\n// get the next 5 ranges\nsched.nextRange(5, start);\n// --> [\n      ['Mon, 22 May 2013 11:01:00 GMT', 'Mon, 22 May 2013 11:04:00 GMT']\n      ['Mon, 22 May 2013 12:01:00 GMT', 'Mon, 22 May 2013 12:04:00 GMT']\n      ['Mon, 22 May 2013 13:01:00 GMT', 'Mon, 22 May 2013 13:04:00 GMT']\n      ['Mon, 22 May 2013 14:01:00 GMT', 'Mon, 22 May 2013 14:04:00 GMT']\n      ['Mon, 22 May 2013 15:01:00 GMT', 'Mon, 22 May 2013 15:04:00 GMT']\n    ]\n\n// get the previous range\nsched.prevRange(1, start);\n// --> ['Mon, 22 May 2013 10:01:00 GMT', 'Mon, 22 May 2013 10:04:00 GMT']\n```\n","engines":{"node":">=8.17.0"},"gitHead":"dce68e4112db40f3a64eda18c6440ff85cfbcbe3","scripts":{"i":"mocha 'src/test/integration/**/*-test.js'","u":"mocha 'src/test/unit/**/*-test.js'","cov":"open coverage/index.html","doc":"jsdoc --verbose -d docs -r -c jsdoc.json -R README.md src/main","lic":"node lic.js > LICENSE","lint":"standard --verbose 'src/**/*.js'","nuke":"rimraf node_modules","test":"run-s transpile unit-integration lint","unit":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js'","build":"npm install && npm test","clean":"rimraf dist docs coverage .nyc_output","format":"standard --fix 'src/**/*.js'","report":"nyc report --reporter=html","prenuke":"npm run clean","postunit":"run-s report","transpile":"run-s transpile-main transpile-test","integration":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/integration/**/*-test.js'","transpile-main":"babel --delete-dir-on-start --verbose --out-dir dist/main --copy-files src/main","transpile-test":"babel --delete-dir-on-start --verbose --out-dir dist/test --copy-files src/test","postintegration":"run-s report","unit-integration":"nyc --check-coverage -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js' 'dist/test/integration/**/*-test.js'","make-distribution":"run-s transpile doc lic","postunit-integration":"run-s report"},"_npmUser":{"name":"npm_northscaler","email":"npm@northscaler.com"},"standard":{"parser":"babel-eslint"},"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"_npmVersion":"6.14.4","description":"Find time slots given a recurrence","directories":{},"_nodeVersion":"14.1.0","dependencies":{"@babel/polyfill":"7.8.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":true,"readmeFilename":"README.md","devDependencies":{"nyc":"15.0.1","chai":"4.2.0","acorn":"7.2.0","jsdoc":"3.6.4","mocha":"7.1.2","should":"13.2.3","standard":"14.3.3","copyfiles":"2.2.0","@babel/cli":"7.8.4","dirty-chai":"2.0.1","@babel/core":"7.9.6","npm-run-all":"4.1.5","babel-eslint":"10.1.0","@babel/register":"7.9.0","lodash.template":"4.5.0","@babel/preset-env":"7.9.6","@northscaler/mutrait":"1.2.0","config-cev-generator":"0.1.6","@babel/plugin-proposal-optional-chaining":"7.9.0"},"_npmOperationalInternal":{"tmp":"tmp/recurrify_1.3.0-rc.1_1589213956934_0.08114563284036391","host":"s3://npm-registry-packages"}},"1.3.0":{"name":"@northscaler/recurrify","version":"1.3.0","keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","_id":"@northscaler/recurrify@1.3.0","maintainers":[{"name":"npm_northscaler","email":"npm@northscaler.com"},{"name":"npm_scispike","email":"npm@scispike.com"}],"homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"dist":{"shasum":"2c8313ed74436cb807d3ebe23b96668af174caed","tarball":"https://registry.npmjs.org/@northscaler/recurrify/-/recurrify-1.3.0.tgz","fileCount":82,"integrity":"sha512-wKP3u6WHBwaYxy1MblTHJRSM7xVkNvrl/dvlCuVVJ6A7BQOC+1JOygytq6kjCB/YE7/Qi3+KL8GGFPdiAlJAqw==","signatures":[{"sig":"MEUCIQCSXh0Ns7VPOL2A7WzvpUT2z1AWJ071FMsn15nkO4xscQIgf814TKotY5z49KOu1PIRHo1gfP8IX1Htd9gtbI00pTA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":2564352,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeuXxICRA9TVsSAnZWagAAjmgP/1d+tR/bt3WacOl+iozK\nTMKDNz5hrOUEfiLbFS+oS6HuCEcIzeeQIup8hK/+StnOWG155pZUFLEshtFr\nftHxP3YsHWcQ00faqWQo7d3i8whHNve8zLqeuXQ/ttRJypH3P8N5C6iLQMP9\n1fRCVLzgMHRK5G49HEOxGqYKatddrg1CZuDRsugrLOZlp0faMcWRfE6CYAQJ\nTABKc4urPPpvyXB9x/omFAwWiVl7tvxvt4dZJAkpHzACoUEU8iy+iTeaS0dJ\n+Nu9bzw2dU3j76BVZJkLqYGe1JO+i34JJBM8AycFYLTvjOrMsmTNPiX9SyoI\n0pSJz/c2C4X7p+iN82MLwu7b70oQFGb/g8zh/S5R39KysaSN7+L87nmKcBUk\nM92jWFHLrv3sZgpVIfnp1HGcZh+TUUMXqRT3WxPiLrlCncuUXPe5hfI+9Qli\nksxEHwKT8cmc9IyWJdTrhDPGeutWDaSZYYLwE8HRfXR60IbLz7k4RtM8uhNL\nLSI0NwzA+PALH6FHs3DzBxCYeS0ujDBcHdq7tV9H+oesXj7doxJKLknoqRXE\n0Jyf0uN7R0YNm32rC6/hPLV88AIPa6Ksg/E35GgLMv5vT2GZRzdmGiEOpCfi\n1yqkN6EAMHyMg757kVoP+xNgigXYxRg2bGlcsv3sv5TibNWWul+pmKmeNWyn\ngmPP\r\n=stAR\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","babel":{"plugins":[["@babel/plugin-proposal-optional-chaining",{"loose":false}]],"presets":[["@babel/preset-env",{"targets":{"node":true}}]],"sourceMaps":"inline"},"mocha":{"colors":true,"require":"@babel/register"},"engines":{"node":">=8.17.0"},"gitHead":"2e02168885b2204c1e2c3e178c47f3cb0ccbdac9","scripts":{"i":"mocha 'src/test/integration/**/*-test.js'","u":"mocha 'src/test/unit/**/*-test.js'","cov":"open coverage/index.html","doc":"jsdoc --verbose -d docs -r -c jsdoc.json -R README.md src/main","lic":"node lic.js > LICENSE","lint":"standard --verbose 'src/**/*.js'","nuke":"rimraf node_modules","test":"run-s transpile unit-integration lint","unit":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js'","build":"npm install && npm test","clean":"rimraf dist docs coverage .nyc_output","format":"standard --fix 'src/**/*.js'","report":"nyc report --reporter=html","prenuke":"npm run clean","postunit":"run-s report","transpile":"run-s transpile-main transpile-test","integration":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/integration/**/*-test.js'","transpile-main":"babel --delete-dir-on-start --verbose --out-dir dist/main --copy-files src/main","transpile-test":"babel --delete-dir-on-start --verbose --out-dir dist/test --copy-files src/test","postintegration":"run-s report","unit-integration":"nyc --check-coverage -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js' 'dist/test/integration/**/*-test.js'","make-distribution":"run-s transpile doc lic","postunit-integration":"run-s report"},"_npmUser":{"name":"npm_northscaler","email":"npm@northscaler.com"},"standard":{"parser":"babel-eslint"},"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"_npmVersion":"6.14.4","description":"Find time slots given a recurrence","directories":{},"_nodeVersion":"14.1.0","dependencies":{"@babel/polyfill":"7.8.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":true,"devDependencies":{"nyc":"15.0.1","chai":"4.2.0","acorn":"7.2.0","jsdoc":"3.6.4","mocha":"7.1.2","should":"13.2.3","standard":"14.3.3","copyfiles":"2.2.0","@babel/cli":"7.8.4","dirty-chai":"2.0.1","@babel/core":"7.9.6","npm-run-all":"4.1.5","babel-eslint":"10.1.0","@babel/register":"7.9.0","lodash.template":"4.5.0","@babel/preset-env":"7.9.6","@northscaler/mutrait":"1.2.0","config-cev-generator":"0.1.6","@babel/plugin-proposal-optional-chaining":"7.9.0"},"_npmOperationalInternal":{"tmp":"tmp/recurrify_1.3.0_1589214273646_0.581941370395409","host":"s3://npm-registry-packages"}},"1.3.1":{"name":"@northscaler/recurrify","version":"1.3.1","keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","_id":"@northscaler/recurrify@1.3.1","maintainers":[{"name":"npm_northscaler","email":"npm@northscaler.com"},{"name":"npm_scispike","email":"npm@scispike.com"}],"homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"dist":{"shasum":"c717e11ccc0be6bad0b8096627ba6ed7f34f223d","tarball":"https://registry.npmjs.org/@northscaler/recurrify/-/recurrify-1.3.1.tgz","fileCount":82,"integrity":"sha512-riMimeKkzgm1yIXS0UrtqiepKYs1ULbH/Zo4g/OAJ/KmLMpVCiH7P3zfqlT/u0/zV11cEJL9PrbrvCEuLGX5rA==","signatures":[{"sig":"MEUCIQDhrO+dBf6SMHueQXQTdRdf1GxMfgN7/dmIdUF723QrDQIgfSpXeLocSIagBxoGIyC3Zk1dSscFrkDZuTcoSzdgvGU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":2564369,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe19JSCRA9TVsSAnZWagAAZDwQAIapNgolODVV3lmUv5+6\njmLaWIvc3/yBQcB/9rxYnX4zAvsMnuaiCCFVOPE+EmQWlblziLwcvzAyaI+0\nPUNe4QcKHtooyKBidhTQ1n909rak/P9rDy1jrKd4fKs6l8yU7cPxS13Oh41z\nJkSGdCq+ndhaKZFSYBbWcfzJhmWIsrpgyg+CeAp0Jj0iccd3OXuNSMHd1Jnk\nd/VL7EZSwMZNQESlKeYulzMLXHd9h5h9FrmhEmWmTyOZKhQOLSwFWjRq0Rkm\nukcO6hVEvk5uc09wlikVHWdlJBjDMx6iukDpNuHkD7DSJNY57+IV/egCvjOh\nx3NSO1TSII5A+KtX72VwJAxEcjvc7kJnTw7ZMUB611eAG12DXyozwr8okL/K\nOYlLUcTFC0p97PPPIF8OfgZV5Mi7FB7JKNlDHCzM4ZkRP4ak3rDBdLcDDNjD\nxsemILvdg++luQ7iHTqRc0MgERFzIqnoCCnDkG+psFDamIOzA51OiksEa7Gx\ni8F3clKFPB8zjKHDWIeG3zkfnZacIQLgl36zCYwfVtcJqWE2PXh0LKoMnyum\n3eHTjLlQGAavRhZJ/XjnQHJ4vxuktEWki5QQdeJUaiTBNg4Y8Dk08jfRuOcV\nOUtz7PiYPE51Ob5r/rOkhGXp9McvR/vSqJY45C6jTtk7Z3jK3kbkDSYvke0v\nbkg+\r\n=PJGA\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","babel":{"plugins":[["@babel/plugin-proposal-optional-chaining",{"loose":false}]],"presets":[["@babel/preset-env",{"targets":{"node":true}}]],"sourceMaps":"inline"},"mocha":{"colors":true,"require":"@babel/register"},"engines":{"node":">=8.17.0"},"gitHead":"cd05df5a5e48b62a5b3d8fa30a9443d72907451d","scripts":{"i":"mocha 'src/test/integration/**/*-test.js'","u":"mocha 'src/test/unit/**/*-test.js'","cov":"open coverage/index.html","doc":"jsdoc --verbose -d docs -r -c jsdoc.json -R README.md src/main","lic":"node lic.js > LICENSE","lint":"standard --verbose 'src/**/*.js'","nuke":"rimraf node_modules","test":"run-s transpile unit-integration lint","unit":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js'","build":"npm install && npm test","clean":"rimraf dist docs coverage .nyc_output","format":"standard --fix 'src/**/*.js'","report":"nyc report --reporter=html","prenuke":"npm run clean","postunit":"run-s report","transpile":"run-s transpile-main transpile-test","integration":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/integration/**/*-test.js'","transpile-main":"babel --delete-dir-on-start --verbose --out-dir dist/main --copy-files src/main","transpile-test":"babel --delete-dir-on-start --verbose --out-dir dist/test --copy-files src/test","postintegration":"run-s report","unit-integration":"nyc --check-coverage -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js' 'dist/test/integration/**/*-test.js'","make-distribution":"run-s transpile doc lic","postunit-integration":"run-s report"},"_npmUser":{"name":"npm_northscaler","email":"npm@northscaler.com"},"standard":{"parser":"babel-eslint"},"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"_npmVersion":"6.14.4","description":"Find time slots given a recurrence","directories":{},"_nodeVersion":"14.1.0","dependencies":{"@babel/polyfill":"7.8.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":true,"devDependencies":{"nyc":"15.0.1","chai":"4.2.0","acorn":"7.2.0","jsdoc":"3.6.4","mocha":"7.1.2","should":"13.2.3","standard":"14.3.3","copyfiles":"2.2.0","@babel/cli":"7.8.4","dirty-chai":"2.0.1","@babel/core":"7.9.6","npm-run-all":"4.1.5","babel-eslint":"10.1.0","@babel/register":"7.9.0","lodash.template":"4.5.0","@babel/preset-env":"7.9.6","@northscaler/mutrait":"1.2.0","config-cev-generator":"0.1.6","@babel/plugin-proposal-optional-chaining":"7.9.0"},"_npmOperationalInternal":{"tmp":"tmp/recurrify_1.3.1_1591202386086_0.6764667205435224","host":"s3://npm-registry-packages"}},"1.3.2":{"name":"@northscaler/recurrify","version":"1.3.2","keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","_id":"@northscaler/recurrify@1.3.2","maintainers":[{"name":"matthewadams","email":"matthew@matthewadams.me"},{"name":"victorynap","email":"djannotta@gmail.com"},{"name":"npm_scispike","email":"npm@scispike.com"},{"name":"npm_northscaler","email":"npm@northscaler.com"}],"homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"dist":{"shasum":"36011ba04fff28bf5465b776c79f6a3e69e479fb","tarball":"https://registry.npmjs.org/@northscaler/recurrify/-/recurrify-1.3.2.tgz","fileCount":82,"integrity":"sha512-rR219pQEctaQxvp0Hn4ZtZbpcqTM6MtzCPpC7s2jGWxBKP7FsQ2QJB11kdIg/Icsn28KE9/veQHIR0dZRhZpZA==","signatures":[{"sig":"MEUCIA/mZ4vhZrAKcOyADYmyNfIoQ0/Y9QbltTZwHMfnM0gaAiEAgjCu1TgijeW+H0lZVAUOQrAAzwZSZE4K5chzMKGI8fI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":2564367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrWOzCRA9TVsSAnZWagAAS00P/RcmWmr3cCtfsLUWKSFQ\npyJsUi5krlS6/Ukj8gq/5o2j2dXhJfyXE5lUDyg3f+Z+0f8JDqcGNmwCUhyu\nJ7bxbwJ/PDy2qQoeKqCQAi/WYTR2ibZt44INxzzUuMmRzvNzQJpJpJ1C9zEh\nR6dJ/AriLCaHg1us3ENUyzliU9tm1FqKDZiYCDQC9UYRmdfOSXNA0u4PcY6b\nL4U/7ew9rxhdthV0V9c43Sw0EGzF4EtMS6CjZ4gKCtH09kv4Eg7TChjEGql9\n7wKOD4mLoF56cjNOQC0/ESCmZTEp4lg7fHAQK1MwZVuv+qL/ETCFVnTywWuN\n19OxOEhHPkXMtCOcnzhRLwqayRvnOpQl6xMUVnODBQRn1F1V542NkJNXOdRz\ncF+q0UlSqVnzQXAOHIqNhbt5HXuNSeUkgVyf+kk9jKwyDWW5ULorTJXZB5nI\nbNGbRjGCBP7kGBa69JWIFDE8aolAHMDLpZw8JOImKZA31bPpBYcLm+W7CRjF\nIcznoTVC8tU4cqI1sHqCQSWxeerbO7fSIh87E5a2LDuJi8HEJndDI6iJJoi+\nU440Y/ePgcdeKcwuuV6PjiKbdwvzk32LfjbmMFoaFIhvg6xYIwuvgoltpIA8\nzEmL7c4fo8+NEv0HojqX1U1sC7h+tDJZd71tewcgbzvQ+dKMmn5AnMC2HRAw\nKbp/\r\n=DOpp\r\n-----END PGP SIGNATURE-----\r\n"},"main":"index.js","babel":{"plugins":[["@babel/plugin-proposal-optional-chaining",{"loose":false}]],"presets":[["@babel/preset-env",{"targets":{"node":true}}]],"sourceMaps":"both"},"mocha":{"colors":true,"require":"@babel/register"},"readme":"# recurrify\n\nBased on a shameless fork of [later.js](https://github.com/kirkins/later).\n\n* Added ability to get *all* time slots of a recurrence between a start and end time\n* Removed the parsers in favor of just dealing with recurrence definitions directly\n* Removed `setTimeout` and `setInterval` implementations\n* Removed bower and Makefile\n* Renamed `schedule` to `recurrence` and `occurrence` to `time slot`\n* Renamed package from `cronicle` to `recurrify`\n* WIP to update syntax to more modern ES6+\n\n## Installation\n\n`$ npm install recurrify`\n\n## Example usage\n\n```\nconst recurrify = require('recurrify')\n\nconst startDate = new Date('2018-10-01T13:00:00Z')\nconst endDate = new Date('2018-10-01T15:00:00Z')\n\n// every 5 minutes\nconst sched = { 'recurrences': [{ 'm': [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] }] }\nconst timeSlots = recurrify.recurrence(sched).all(startDate, endDate)\nconsole.log(timeSlots)\n```\n\n### Result\n```\n$ node example.js\n[ 2018-10-01T13:00:00.000Z,\n  2018-10-01T13:05:00.000Z,\n  2018-10-01T13:10:00.000Z,\n  2018-10-01T13:15:00.000Z,\n  2018-10-01T13:20:00.000Z,\n  2018-10-01T13:25:00.000Z,\n  2018-10-01T13:30:00.000Z,\n  2018-10-01T13:35:00.000Z,\n  2018-10-01T13:40:00.000Z,\n  2018-10-01T13:45:00.000Z,\n  2018-10-01T13:50:00.000Z,\n  2018-10-01T13:55:00.000Z,\n  2018-10-01T14:00:00.000Z,\n  2018-10-01T14:05:00.000Z,\n  2018-10-01T14:10:00.000Z,\n  2018-10-01T14:15:00.000Z,\n  2018-10-01T14:20:00.000Z,\n  2018-10-01T14:25:00.000Z,\n  2018-10-01T14:30:00.000Z,\n  2018-10-01T14:35:00.000Z,\n  2018-10-01T14:40:00.000Z,\n  2018-10-01T14:45:00.000Z,\n  2018-10-01T14:50:00.000Z,\n  2018-10-01T14:55:00.000Z,\n  2018-10-01T15:00:00.000Z ]\n```\n\n## Running tests\n\n`$ npm test`\n\n## Recurrences\nRecurrences in `recurrify` are json objects that define a set of time units along with the values that should be considered valid for that time unit.\nThe combination of a time unit with their valid values is called a *constraint*.\n`recurrify` then takes all of the constraints that have been defined and finds dates that match all of them.\n\nSince `recurrify` recurrences are json objects, they can easily be serialized and stored in caches and databases as needed.\nThey are also completely deterministic which means a recurrence will always produce exactly the same valid time slots.\nTherefore, time slots never need to be stored as they can always be recalculated from the recurrence definition.\n\n## Basic recurrences\nA basic recurrence is a set of time units along with their valid values.\nA date is only considered valid if it meets all of the constraints within a basic recurrence.\nA basic recurrence can include as many time units (with or without modifiers) as needed, in any order.\n\n```\n// a basic recurrence that is valid every day at 10:15am and 10:45am\nconst basic = {h: [10], m: [15,45]}\n```\n\nHere we can see a recurrence is made up of objects with properties that correspond to the various time units.\nIn this case `h` is the hour time unit and `m` is the minute time unit.\nThe values to consider valid are always stored in an array as the value of the property.\n\n\n**Note:** Basic recurrences are only valid as part of a complete recurrence definition.\n\n## Composite recurrences\nMultiple basic recurrences can be combined into a single composite recurrence by placing them into an array.\nA date is considered valid if any of the basic recurrences are valid (basically an OR of all of the basic recurrences).\nA composite recurrence can contain as many basic recurrences as needed.\n\n```\n// a composite recurrence that is valid every day at 10:15am and 10:45am\n// and every day at 5:30pm\nconst composite = [\n  {h: [10], m: [15,45]},\n  {h: [17], m: [30]}\n]\n\n```\n\n**Note:** Composite recurrences are only valid as part of a complete recurrence definition.\n\n## Exception recurrences\nAn exception recurrence is a basic or composite recurrence that defines when a recurrence should be considered invalid.\nA date is considered invalid if any of the basic recurrences within an exception recurrence are valid.\n\n```\n// an exception recurrence that makes any date in March as\n// well as any Monday of any month invalid\nconst exception = [\n  {M: [3]},\n  {dw: [2]}\n]\n```\n\n**Note:** Exception recurrences are only valid as part of a complete recurrence definition.\n\n## Complete definition\nA complete definition is a json object that at a minimum contains a `recurrences` property that defines a composite recurrence with at least one basic recurrence.\nOptionally, the definition can also include an `exceptions` property that defines a composite exception recurrence.\n\n### Examples\nHere's an example of a valid recurrence that fires every 10 minutes.\nThe composite recurrence is always placed in an object under a property named `recurrences`. Even if you only have a basic recurrence, the `recurrences` property must be an array.\n\n```\nconst recurrence = {\n  recurrences: [\n    {m: [0,10,20,30,40,50]}\n  ]\n}\n```\n\nHere's a valid recurrence definition that includes exceptions with modifiers.\nHere we see the composite exception recurrence is always placed in an object under a property named `exceptions`.\nEven if you only have a basic exception recurrence, the `exceptions` property must be an array.\n\n```\nconst recurrence = {\n  recurrences: [\n    {h: [10], m: [15,45]},\n    {h: [17], m: [30]}\n  ],\n  exceptions: [\n    {M_a: [3]},\n    {dw: [2]}\n  ]\n}\n```\n\n## Performance considerations\nWhile `recurrify` has been designed to efficiently calculate time slots for all types and complexities of recurrences, there are a few things to keep in mind for applications that have particularly high performance requirements.\n\n* Basic time units perform the best. These include years, months, days, hours, minutes, and seconds.\nCalculating ISO week of year is particularly expensive.\n* Recurrences without exceptions perform better than those with exceptions.\nDefining your recurrence without the need for exceptions will improve performance.\n* Use the `time` time unit instead of specifying hours and minutes separately when possible.\nReducing the number of constraints will generally improve performance.\n* Using `after` and `before` modifiers to eliminate the need for specifying a lot of valid values will improve performance, especially when calculating ranges.\n\n## time units\nTime units are the crux of the `recurrify` library and are used to define new recurrences.\n`recurrify` comes with a large assortment of time units and is also fully extensible making it easy to create custom time units.\n\nWhile time units are primarily used by `recurrify` to define recurrences and calculate time slots, they are also useful for performing time based calculations.\nCalculating values such as ISO week number, moving between days of the year, or figuring out how many days are in a month are all possible using the time unit interface.\n\nIf you don't see the time unit that you need for your recurrence, `recurrify` is fully extensible and it is easy to write your own.\nSee the custom time unit at the bottom of this page for an example.\n\n## Interface\nAll time units implement the same public interface for interacting with them:\n\n**`name`**\nThe name of the time unit.\n\n**`range`**\nThe rough number of seconds that are covered when moving from one instance of this time unit to the next instance.\n\n**`val(date)`**\nThe value of this time unit for the date specified.\n\n**`isValid(date, value)`**\nTrue if the specified value is valid for the specified date, false otherwise.\n\n**`extent(date)`**\nThe minimum and maximum valid values for the time unit for the specified date.\nIf the minimum value is not `0`, `0` can be specified in recurrences to indicate the maximum value.\nThis makes working with non-constant extents (like days in a month) easier.\n\n**`start(date)`**\nThe first second in which the value is the same as the value of the specified date.\nFor example, the start of an hour would be the hour with `0` minutes and `0` seconds.\n\n**`end(date)`**\nThe last second in which the value is the same as the value of the specified date.\nFor example, the end of an hour would be the hour with `59` minutes and `59` seconds.\n\n**`next(date, value)`**\nReturns the next date where the value is the value specified.\nSets the value to `1` if value specified is greater than the max allowed value.\n\n**`prev(`date, value`)**\nReturns the previous date where the value is the value specified.\nSets the value to the max allowed value if the value specified is greater than the max allowed value.\n\n## Second (second, s)\nSeconds in a minute, from `0` to `59`.\n\nUsing seconds in a recurrence:\n\n`const sched = {recurrences: [{s: [0, 15, 30, 45]}]}`\n\nPerforming seconds based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.second.name;\n// --> 'second'\n\nrecurrify.second.range;\n// --> 1\n\nrecurrify.second.val(d);\n// --> 5\n\nrecurrify.second.isValid(d, 10);\n// --> false\n\nrecurrify.second.extent();\n// --> [0, 59]\n\nrecurrify.second.start(d);\n// --> 'Fri, 22 Mar 2013 10:02:05 GMT'\n\nrecurrify.second.end(d);\n// --> 'Fri, 22 Mar 2013 10:02:05 GMT'\n\nrecurrify.second.next(d, 27);\n// --> 'Fri, 22 Mar 2013 10:02:27 GMT'\n\nrecurrify.second.prev(d, 27);\n// --> 'Fri, 22 Mar 2013 10:01:27 GMT'\n```\n\n## Minute (minute, m)\nMinutes in an hour, from `0` to `59`.\n\nUsing minutes in a recurrence:\n\n`const sched = {recurrences: [{m: [0, 15, 30, 45]}]}`\n\nPerforming minutes based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.minute.name;\n// --> 'minute'\n\nrecurrify.minute.range;\n// --> 60\n\nrecurrify.minute.val(d);\n// --> 2\n\nrecurrify.minute.isValid(d, 2);\n// --> true\n\nrecurrify.minute.extent();\n// --> [0, 59]\n\nrecurrify.minute.start(d);\n// --> 'Fri, 22 Mar 2013 10:02:00 GMT'\n\nrecurrify.minute.end(d);\n// --> 'Fri, 22 Mar 2013 10:02:59 GMT'\n\nrecurrify.minute.next(d, 27);\n// --> 'Fri, 22 Mar 2013 10:27:00 GMT'\n\nrecurrify.minute.prev(d, 27);\n// --> 'Fri, 22 Mar 2013 09:27:59 GMT'\n```\n\n## Hour (hour, h)\nHours in a day, from `0` to `23`.\n\nUsing hours in a recurrence:\n\n`const sched = {recurrences: [{h: [0, 5, 12]}]}`\n\nPerforming hours based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.hour.name;\n// --> 'hour'\n\nrecurrify.hour.range;\n// --> 3600\n\nrecurrify.hour.val(d);\n// --> 10\n\nrecurrify.hour.isValid(d, 2);\n// --> false\n\nrecurrify.hour.extent();\n// --> [0, 23]\n\nrecurrify.hour.start(d);\n// --> 'Fri, 22 Mar 2013 10:00:00 GMT'\n\nrecurrify.hour.end(d);\n// --> 'Fri, 22 Mar 2013 10:59:59 GMT'\n\nrecurrify.hour.next(d, 5);\n// --> 'Sat, 23 Mar 2013 05:00:00 GMT'\n\nrecurrify.hour.prev(d, 21);\n// --> 'Thu, 21 Mar 2013 21:59:59 GMT'\n```\n\n## Time (time, t)\nTime of day, represented as seconds since midnight. From `0` to `86399` (60 * 60 * 24 - 1).\n\n\nUsing time in a recurrence:\n\n`const sched = {recurrences: [{t: [6500]}]}`\n\nPerforming time based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.time.name;\n// --> 'time'\n\nrecurrify.time.range;\n// --> 1\n\nrecurrify.time.val(d);\n// --> 36125\n\nrecurrify.time.isValid(d, 36125);\n// --> true\n\nrecurrify.time.extent();\n// --> [0, 86399]\n\nrecurrify.time.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.time.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.time.next(d, 60);\n// --> 'Sat, 23 Mar 2013 00:01:00 GMT'\n\nrecurrify.time.prev(d, 60);\n// --> 'Fri, 22 Mar 2013 00:01:00 GMT'\n```\n\n## Day (day, D)\nDays of a month, from `1` to the max days in the month.\nSpecify `0` for the varying last day of the month, which is between `28` through `31`.\n\nUsing days in a recurrence:\n\n`const sched = {recurrences: [{D: [0]}]}`\n\nPerforming day based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.day.name;\n// --> 'day'\n\nrecurrify.day.range;\n// --> 86400\n\nrecurrify.day.val(d);\n// --> 22\n\nrecurrify.day.isValid(d, 3);\n// --> false\n\nrecurrify.day.extent(d);\n// --> [1, 31]\n\nrecurrify.day.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.day.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.day.next(d, 11);\n// --> 'Thu, 11 Apr 2013 00:00:00 GMT'\n\nrecurrify.day.prev(d, 2);\n// --> 'Sat, 02 Mar 2013 23:59:59 GMT'\n```\n\n## Day of week (dayOfWeek, dw, d)\nDays of a week, from `1` to `7`.\nSpecify `0` for the last day of the week (Saturday).\n\nUsing days of week in a recurrence:\n\n`const sched = {recurrences: [{dw: [2,3,4,5,6]}]}`\n\nPerforming day of week based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfWeek.name;\n// --> 'day of week'\n\nrecurrify.dayOfWeek.range;\n// --> 86400\n\nrecurrify.dayOfWeek.val(d);\n// --> 6\n\nrecurrify.dayOfWeek.isValid(d, 3);\n// --> false\n\nrecurrify.dayOfWeek.extent();\n// --> [1, 7]\n\nrecurrify.dayOfWeek.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeek.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.dayOfWeek.next(d, 1);\n// --> 'Sun, 24 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeek.prev(d, 5);\n// --> 'Thu, 21 Mar 2013 23:59:59 GMT'\n```\n\n## Day of week count (dayOfWeekCount, dc)\nThe nth day of the week within a month, from `1` to the max weeks in a month.\nSpecify `0` for the last day instance.\nUsed together with the day of the week time unit to specify things like the 2nd Tuesday or last Friday of a month.\n\nUsing days of week count in a recurrence:\n\n`const sched = {recurrences: [{dc: [2]}]}`\n\nPerforming day of week count based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfWeekCount.name;\n// --> 'day of week count'\n\nrecurrify.dayOfWeekCount.range;\n// --> 604800\n\nrecurrify.dayOfWeekCount.val(d);\n// --> 4\n\nrecurrify.dayOfWeekCount.isValid(d, 4);\n// --> true\n\nrecurrify.dayOfWeekCount.extent(d);\n// --> [1, 5]\n\nrecurrify.dayOfWeekCount.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeekCount.end(d);\n// --> 'Thu, 28 Mar 2013 23:59:59 GMT'\n\n// zero is special cased and means the last instance of\n// a day of the week in the month, instead of meaning the\n// first day of the week with the highest instance count\n// which would have been Mar 29 with value 5.\nrecurrify.dayOfWeekCount.next(d, 0);\n// --> 'Mon, 25 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeekCount.prev(d, 2);\n// --> 'Thu, 14 Mar 2013 23:59:59 GMT'\n```\n\n## Day of year (dayOfYear, dy)\nDay of the year, from `1` to the max days in year.\nSpecify `0` for last day of the year.\n\nUsing days of year in a recurrence:\n\n`const sched = {recurrences: [{dy: [189, 267]}]}`\n\nPerforming day of year based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfYear.name;\n// --> 'day of year'\n\nrecurrify.dayOfYear.range;\n// --> 86400\n\nrecurrify.dayOfYear.val(d);\n// --> 81\n\nrecurrify.dayOfYear.isValid(d, 4);\n// --> false\n\nrecurrify.dayOfYear.extent(d);\n// --> [1, 365]\n\nrecurrify.dayOfYear.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfYear.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.dayOfYear.next(d, 256);\n// --> 'Fri, 13 Sep 2013 00:00:00 GMT'\n\nrecurrify.dayOfYear.prev(d, 44);\n// --> 'Wed, 13 Feb 2013 23:59:59 GMT'\n```\n\n## Week of month (weekOfMonth, wm)\nWeek of the month where the 1st of the month is week `1` and following weeks start on Sunday.\nFrom `1` to max weeks in the month.\nSpecify `0` for last week of the month.\n\nUsing weeks of month in a recurrence:\n\n`const sched = {recurrences: [{wm: [1, 2]}]}`\n\nPerforming week of month based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.weekOfMonth.name;\n// --> 'week of month'\n\nrecurrify.weekOfMonth.range;\n// --> 604800\n\nrecurrify.weekOfMonth.val(d);\n// --> 4\n\nrecurrify.weekOfMonth.isValid(d, 4);\n// --> true\n\nrecurrify.weekOfMonth.extent(d);\n// --> [1, 6]\n\nrecurrify.weekOfMonth.start(d);\n// --> 'Sun, 17 Mar 2013 00:00:00 GMT'\n\nrecurrify.weekOfMonth.end(d);\n// --> 'Sat, 23 Mar 2013 23:59:59 GMT'\n\nrecurrify.weekOfMonth.next(d, 1);\n// --> 'Mon, 01 Apr 2013 00:00:00 GMT'\n\nrecurrify.weekOfMonth.prev(d, 2);\n// --> 'Sat, 09 Mar 2013 23:59:59 GMT'\n```\n\n## ISO Week of year (weekOfYear, wy)\nThe ISO-8601 week of the year.\nFrom `1` to the max ISO week in the year.\nSpecify `0` for last ISO week of the year.\n\nUsing weeks of year in a recurrence:\n\n`const sched = {recurrences: [{wy: [13,26,39,0]}]}`\n\nPerforming week of year based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.weekOfYear.name;\n// --> 'week of year'\n\nrecurrify.weekOfYear.range;\n// --> 604800\n\nrecurrify.weekOfYear.val(d);\n// --> 12\n\nrecurrify.weekOfYear.isValid(d, 21);\n// --> false\n\nrecurrify.weekOfYear.extent(d);\n// --> [1, 52]\n\nrecurrify.weekOfYear.start(d);\n// --> 'Mon, 18 Mar 2013 00:00:00 GMT'\n\nrecurrify.weekOfYear.end(d);\n// --> 'Sun, 24 Mar 2013 23:59:59 GMT'\n\nrecurrify.weekOfYear.next(d, 47);\n// --> 'Mon, 18 Nov 2013 00:00:00 GMT'\n\nrecurrify.weekOfYear.prev(d, 52);\n// --> 'Sun, 30 Dec 2012 23:59:59 GMT'\n```\n\n## Month (month, M)\nMonth of the year, from `1` to `12`.\nSpecify `0` for the last month of the year.\n\nUsing months in a recurrence:\n\n`const sched = {recurrences: [{M: [3,5,7]}]}`\n\nPerforming months based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.month.name;\n// --> 'month'\n\nrecurrify.month.range;\n// --> 2629740\n\nrecurrify.month.val(d);\n// --> 3\n\nrecurrify.month.isValid(d, 3);\n// --> true\n\nrecurrify.month.extent();\n// --> [1, 12]\n\nrecurrify.month.start(d);\n// --> 'Fri, 01 Mar 2013 00:00:00 GMT'\n\nrecurrify.month.end(d);\n// --> 'Sun, 31 Mar 2013 23:59:59 GMT'\n\nrecurrify.month.next(d, 11);\n// --> 'Fri, 01 Nov 2013 00:00:00 GMT'\n\nrecurrify.month.prev(d, 2);\n// --> 'Thu, 28 Feb 2013 23:59:59 GMT'\n```\n\n## Year (year, Y)\nYear, from `1970` to `2099`.\n\nUsing years in a recurrence:\n\n`const sched = {recurrences: [{Y: [2013, 2014, 2015]}]}`\n\nPerforming years based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.year.name;\n// --> 'year'\n\nrecurrify.year.range;\n// --> 31556900\n\nrecurrify.year.val(d);\n// --> 2013\n\nrecurrify.year.isValid(d, 2013);\n// --> true\n\nrecurrify.year.extent();\n// --> [1970, 2099]\n\nrecurrify.year.start(d);\n// --> 'Tue, 01 Jan 2013 00:00:00 GMT'\n\nrecurrify.year.end(d);\n// --> 'Tue, 31 Dec 2013 23:59:59 GMT'\n\nrecurrify.year.next(d, 2014);\n// --> 'Wed, 01 Jan 2014 00:00:00 GMT'\n\nrecurrify.year.prev(d, 2012);\n// --> 'Mon, 31 Dec 2012 23:59:59 GMT'\n```\n\n## Writing a custom time unit\n`recurrify` is fully extensible and it is easy to create your own custom time units that can be used to define new recurrences.\nTo keep things simple, we'll walk through creating a new time unit for indicating morning, afternoon, and evening.\nFor our purposes, morning will be before noon and have a value of `0`, afternoon will be before 6pm and have a value of `1`, and evening will be before midnight and have a value of `2`.\n\nThe first step is to create a name and id for the modifier and add it to the recurrify namespace.\n\n```javascript\nrecurrify.partOfDay = recurrify.pd = {\n  // interface implementation goes here\n};\n```\n\nNext, we need to implement the time unit interface.\nFirst we will just specify the name of this time unit.\n\n`name: 'part of day',`\n\nThe range is approximately 6 hours.\nThough some of our periods are longer and some shorter,\nwe'll use the shortest range which is afternoon at 6 hours.\n\n`range: recurrify.h.range * 6,`\n\nWe then implement `val` to return the appropriate value based on the definition described previously.\n\n```javascript\nval: function(d) {\n  return recurrify.h.val(d) < 12 ? 0 :\n         recurrify.h.val(d) < 6 ? 1 :\n         2;\n},\n```\n\nThen we can use our new `val` function to implement `isValid`.\n\n```javascript\nisValid: function(d, val) {\n  return recurrify.pd.val(d) === val;\n},\n```\n\nThe extent is always going to be the same for every day so we can just return a constant array here.\n\n`extent: function(d) { return [0, 2]; },`\n\nNext we need to implement `start` and `end` based on the current time unit.\nThis will be the start and end of each part of the day that we've defined.\n\n```javascript\nstart: function(d) {\n  const hour = recurrify.pd.val(d) === 0 ? 0 :\n                recurrify.pd.val(d) === 1 ? 12 :\n                6;\n\n  // recurrify.date.next is a helper function for creating the date in UTC or\n  // localTime as appropriate\n  return recurrify.date.next(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    recurrify.D.val(d),\n    hour\n  );\n},\n\nend: function(d) {\n  const hour = recurrify.pd.val(d) === 0 ? 11 :\n                recurrify.pd.val(d) === 1 ? 5 :\n                23;\n\n  // recurrify.date.prev is a helper function for creating the date in UTC or\n  // localTime as appropriate, and automatically adjusts the date to be at\n  // the last second of the specified time\n  return recurrify.date.prev(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    recurrify.D.val(d),\n    hour\n  );\n},\n```\n\nFinally, we need to implement `next` and `prev` so that you can move to different parts of the day.\nWe need to make sure to increment and decrement the day appropriately if we've already passed the specified value.\n\n```javascript\nnext: function(d, val) {\n  const hour = val === 0 ? 0 : val === 1 ? 12 : 18;\n\n  return recurrify.date.next(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    // increment the day if we already passed the desired time unit\n    recurrify.D.val(d) + (hour < recurrify.h.val(d) ? 1 : 0),\n    hour\n  );\n},\n\nprev: function(d, val) {\n  const hour = val === 0 ? 11 : val === 1 ? 5 : 23;\n\n  return recurrify.date.prev(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    // decrement the day if we already passed the desired time unit\n    recurrify.D.val(d) + (hour > recurrify.h.val(d) ? -1 : 0),\n    hour\n  );\n}\n```\n\n### Full implementation\nHere is the code for the completed example.\nTo use the time unit, just add this code after including `recurrify` into your project and before you use it in any recurrences.\n\n```javascript\nrecurrify.partOfDay = recurrify.pd = {\n\n  name: 'part of day',\n\n  range: recurrify.h.range * 6,\n\n  val: function(d) {\n    return recurrify.h.val(d) < 12 ? 0 :\n           recurrify.h.val(d) < 18 ? 1 :\n           2;\n  },\n\n  isValid: function(d, val) {\n    return recurrify.pd.val(d) === val;\n  },\n\n  extent: function(d) { return [0, 2]; },\n\n  start: function(d) {\n    const hour = recurrify.pd.val(d) === 0 ? 0 :\n                  recurrify.pd.val(d) === 1 ? 12 :\n                  18;\n\n    return recurrify.date.next(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      recurrify.D.val(d),\n      hour\n    );\n  },\n\n  end: function(d) {\n    const hour = recurrify.pd.val(d) === 0 ? 11 :\n                  recurrify.pd.val(d) === 1 ? 5 :\n                  23;\n\n    return recurrify.date.prev(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      recurrify.D.val(d),\n      hour\n    );\n  },\n\n  next: function(d, val) {\n    const hour = val === 0 ? 0 : val === 1 ? 12 : 18;\n\n    return recurrify.date.next(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      // increment the day if we already passed the desired time unit\n      recurrify.D.val(d) + (hour < recurrify.h.val(d) ? 1 : 0),\n      hour\n    );\n  },\n\n  prev: function(d, val) {\n    const hour = val === 0 ? 11 : val === 1 ? 5 : 23;\n\n    return recurrify.date.prev(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      // decrement the day if we already passed the desired time unit\n      recurrify.D.val(d) + (hour > recurrify.h.val(d) ? -1 : 0),\n      hour\n    );\n  }\n};\n```\n\n### Usage\nUsing the custom time unit is exactly the same as using a built-in time unit.\n\n```javascript\n// use our new time unit to specify every 15 mins at night\nconst sched = recurrify.parse.recur().every(15).minute().on(2).customPeriod('pd'),\n    next = recurrify.recurrence(sched).next(1, new Date(2013, 3, 21));\n\nconsole.log(next.toUTCString());\n// --> Sun, 21 Apr 2013 18:00:00 GMT\n```\n\n## Modifiers\nWith `recurrify`, not only can you write your own custom time units, you can also write custom modifiers that can change the behavior of existing time units.\nThe modifiers sit in between the scheduling engine and the time unit allowing you to intercept and modify the results that are returned by the time unit.\n\nModifies are specified by attaching `_(modifier-id)` to the time unit id that you want to modify.\nThe same time unit can be used with different modifiers within the same recurrence.\n\n## after: `_a`\nModifies the corresponding time unit such that all values after and including the specified value is considered valid.\nThis modifier can be used with any time unit.\nUseful for creating more compact recurrences when a time unit has a lot of consecutive valid values.\n\n```javascript\n// all hours after 5:00pm will be valid\nconst sched = {recurrences: [{h_a: [17]}]};\n\n// equivalent to\nconst sched = {recurrences: [{h: [17,18,19,20,21,22,23]}]};\n```\n\n## before `_b`\nModifies the corresponding time unit such that all values before (but not including) the specified value is considered valid.\nThis modifier can be used with any time unit.\nUseful for creating more compact recurrences when a time unit has a lot of consecutive valid values.\n\n```javascript\n// all hours before 5:00pm will be valid\nconst sched = {recurrences: [{h_b: [17]}]};\n\n// equivalent to\nconst sched = {recurrences: [{h: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}]};\n```\n\n## Writing a custom modifier\nCustom modifiers are very similar to custom time units and share the same interface.\nTo keep things simple, we'll walk through creating a modifier to change the `month` period to work with values `0`-`11` instead of `1`-`12`.\n\nThe first step is to create a name and id for the modifier and add it to the modifier namespace.\nModifiers take the time unit that is being modified along with the specified values as arguments.\n\n```javascript\nrecurrify.modifier.month = recurrify.modifier.m = function(period, values) {\n  if(period.name !== 'month') {\n    throw new Error('Month modifier only works with months!');\n  }\n\n  return {\n    // interface implementation goes here\n  };\n};\n```\n\nNext, we need to implement the same interface that time units implement and modify them to work with the new set of values.\nFirst, we will just modify the `name` to include a reference to the modifier.\n\n`name: 'reIndexed ' + period.name,`\n\nThe range is the same, so we just pass it through.\n\n`range: period.range,`\n\nWe then modify the `val` returned by subtracting `1` since our new indices are one less than the original ones.\n\n`val: function(d) { return period.val(d) - 1; },`\n\nNext, `isValid` is modified by tweaking the value that is passed in so that it is in the range that the month time unit expects.\n\n`isValid: function(d, val) { return period.isValid(d, val+1); },`\n\nThe `extent` also needs to be modified to reflect the new extent that goes from `0`-`11`.\nNow that the extent starts at `0`, the recurrence engine will no longer assume that a `0` value means \"last\".\nThere is nothing else that we need to do to correct for that behavior.\n\n`extent: function(d) { return [0, 11]; },`\n\nThe `start` and `end` dates for the month will be the same, so we can just pass those through to the time unit without modification.\n\n```javascript\nstart: period.start,\nend: period.end,\n```\n\nFinally, the values passed into `next` and `prev` need to be updated to be in the range that the month time unit expects.\n\n```javascript\nnext: function(d, val) { return period.next(d, val+1); },\nprev: function(d, val) { return period.prev(d, val+1); }\n```\n\n### Full implementation\nHere is the code for the completed example.\nTo use the modifier, just add this code after including `recurrify` into your project and before you use it in any recurrences.\n\n```javascript\nrecurrify.modifier.month = recurrify.modifier.m = function(period, values) {\n  if(period.name !== 'month') {\n    throw new Error('Month modifier only works with months!');\n  }\n\n  return {\n    name:     'reIndexed ' + period.name,\n    range:    period.range,\n    val:      function(d) { return period.val(d) - 1; },\n    isValid:  function(d, val) { return period.isValid(d, val+1); },\n    extent:   function(d) { return [0, 11]; },\n    start:    period.start,\n    end:      period.end,\n    next:     function(d, val) { return period.next(d, val+1); },\n    prev:     function(d, val) { return period.prev(d, val+1); }\n  };\n};\n```\n\n### Usage\nUsing the custom modifier is exactly the same as using a built-in modifier.\n\n```javascript\n// without our modifier, 2 means February\nconst sched1 = {recurrences: [{M: [2]}]};\n\nrecurrify.recurrence(sched1).next(1, new Date(2013, 3, 21));\n// --> Sat, 01 Feb 2014 00:00:00 GMT\n\n// use our new modifier so that 2 now means March\nconst sched = recurrify.parse.recur().customModifier('m', 2).month();\n\nnext = recurrify.recurrence(sched2).next(1, new Date(2013, 3, 21));\n// --> Sat, 01 Mar 2014 00:00:00 GMT\n```\n\n## Time Slots\nOnce a recurrence has been defined, it can be used to calculate future and past time slots of that recurrence.\nA time slot is a date that meets all of the constraints imposed by the recurrence.\n\nIn order to improve performance, recurrences are first compiled prior to time slots being calculated.\nThe compiled version of the recurrence can be reused to find additional time slots as needed.\n\nTo compile a recurrence, pass the recurrence definition to `recurrify.recurrence`.\n\n`const sched = recurrify.recurrence(recurrence)`\n\n> TIP: All recurrence definitions are timezone agnostic.\nWhen you need to calculate time slots, you can decide to perform the calculation using local time or UTC.\n\n```javascript\n// set recurrify to use UTC time (the default)\nrecurrify.date.UTC();\n\n// set recurrify to use local time\nrecurrify.date.localTime();\n```\n\n## `isValid(date)`\nReturns true if the `date` passed in is a valid time slot of the recurrence, false otherwise.\n\n`const valid = recurrify.recurrence(recurrence).isValid(date)`\n\n### Examples\n\n```javascript\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute());\n\nsched.isValid(new Date('2013-03-22T10:02:00Z'));\n// --> true\n\nsched.isValid(new Date('2013-03-22T10:22:00Z'));\n// --> false\n\nsched.isValid(new Date('2013-03-22T10:02:05Z'));\n// --> false\n```\n\n## Calculating instances\nInstances are individual dates that meet all of the constraints that are imposed by the recurrence.\nInstances can be calculated both forwards and backwards, in any quantity, and optionally between a start and end date.\nWhen calculating multiple instances, the minimum time between instances is based on the smallest ranged time unit.\n\n**`recurrify.recurrence(recurrence).all(start, end)`**\nCalculates *all* time slots of `recurrence` starting from the `start` date and ending before the `end` date.\nIf an end date is not specified, the maximum results returned is `1000000`.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).all(startDate, endDate)`\n\n**`recurrify.recurrence(recurrence*).next(*count, start, end*)`**\nCalculates the next `count` time slots of `recurrence`, optionally starting from the `start` date and ending before the `end` date.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).next(2)`\n\n\n**`recurrify.recurrence(recurrence).prev(count, start, end)`**\nCalculates the previous `count` time slots of `recurrence`, optionally starting from the `start` date and ending before the `end` date.\nWhen using previous, the `start` date must be greater than the `end` date.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).prev(2)`\n\n### Examples\n```javascript\n// sched for minute equal to 1,2, or 3\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute()),\n    start = new Date('2013-05-22T10:22:00Z');\n\n// get the next instance\nsched.next(1, start);\n// --> Mon, 22 May 2013 11:01:00 GMT\n\n// get the next 5 instances\nsched.next(5, start);\n// --> [ 'Mon, 22 May 2013 11:01:00 GMT',\n      'Mon, 22 May 2013 11:02:00 GMT',\n      'Mon, 22 May 2013 11:03:00 GMT',\n      'Mon, 22 May 2013 12:01:00 GMT',\n      'Mon, 22 May 2013 12:02:00 GMT' ]\n\n// get the previous instance\nsched.prev(1, start);\n// --> Mon, 22 May 2013 10:03:00 GMT\n```\n\n## Calculating ranges\nRanges combine consecutively valid instances into a single start and end block of time.\nThe start time is the first valid instance of the block of time.\nThe end time is the first invalid time after the block.\n\nRanges are useful when scheduling blocks of time such as a meeting or activity.\nThe recurrence definition defines the start and end time of the activity and then ranges are used to find their time slots.\n\n**`recurrify.recurrence(recurrence).nextRange(count, start, end)`**\nCalculates the next count ranges of recurrence, optionally starting from the start date and ending before the end date.\n\n`recurrify.recurrence({recurrences: [{m:[5,6,7]}]}).nextRange(2)`\n\n**`recurrify.recurrence(recurrence).prevRange(count, start, end)`**\nCalculates the previous count ranges of recurrence, optionally starting from the start date and ending before the end date.\n\n`recurrify.recurrence({recurrences: [{m:[5,6,7]}]}).prevRange(2)`\n\n### Examples\n```javascript\n// sched for minute equal to 1,2, or 3\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute()),\n    start = new Date('2013-05-22T10:22:00Z');\n\n// get the next range\nsched.nextRange(1, start);\n// --> ['Mon, 22 May 2013 11:01:00 GMT', 'Mon, 22 May 2013 11:04:00 GMT']\n\n// get the next 5 ranges\nsched.nextRange(5, start);\n// --> [\n      ['Mon, 22 May 2013 11:01:00 GMT', 'Mon, 22 May 2013 11:04:00 GMT']\n      ['Mon, 22 May 2013 12:01:00 GMT', 'Mon, 22 May 2013 12:04:00 GMT']\n      ['Mon, 22 May 2013 13:01:00 GMT', 'Mon, 22 May 2013 13:04:00 GMT']\n      ['Mon, 22 May 2013 14:01:00 GMT', 'Mon, 22 May 2013 14:04:00 GMT']\n      ['Mon, 22 May 2013 15:01:00 GMT', 'Mon, 22 May 2013 15:04:00 GMT']\n    ]\n\n// get the previous range\nsched.prevRange(1, start);\n// --> ['Mon, 22 May 2013 10:01:00 GMT', 'Mon, 22 May 2013 10:04:00 GMT']\n```\n","engines":{"node":">=8.17.0"},"gitHead":"f79fd9b2d65f3b311395060e87169599aeb68c11","scripts":{"i":"mocha 'src/test/integration/**/*-test.js'","u":"mocha 'src/test/unit/**/*-test.js'","cov":"open coverage/index.html","doc":"jsdoc --verbose -d docs -r -c jsdoc.json -R README.md src/main","lic":"node lic.js > LICENSE","lint":"standard --verbose 'src/**/*.js'","nuke":"rimraf node_modules","test":"run-s transpile unit-integration lint","unit":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js'","build":"npm install && npm test","clean":"rimraf dist docs coverage .nyc_output","format":"standard --fix 'src/**/*.js'","report":"nyc report --reporter=html","prenuke":"npm run clean","postunit":"run-s report","transpile":"run-s transpile-main transpile-test","integration":"nyc -x 'dist/test' --exclude-after-remap false mocha 'dist/test/integration/**/*-test.js'","transpile-main":"babel --delete-dir-on-start --verbose --out-dir dist/main --copy-files src/main","transpile-test":"babel --delete-dir-on-start --verbose --out-dir dist/test --copy-files src/test","postintegration":"run-s report","unit-integration":"nyc --check-coverage -x 'dist/test' --exclude-after-remap false mocha 'dist/test/unit/**/*-test.js' 'dist/test/integration/**/*-test.js'","make-distribution":"run-s transpile doc lic","postunit-integration":"run-s report"},"_npmUser":{"name":"npm_northscaler","email":"npm@northscaler.com"},"standard":{"parser":"babel-eslint"},"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"_npmVersion":"6.14.4","description":"Find time slots given a recurrence","directories":{},"_nodeVersion":"14.1.0","dependencies":{"@babel/polyfill":"7.8.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":true,"readmeFilename":"README.md","devDependencies":{"nyc":"15.0.1","chai":"4.2.0","acorn":"7.2.0","jsdoc":"3.6.4","mocha":"7.1.2","should":"13.2.3","standard":"14.3.3","copyfiles":"2.2.0","@babel/cli":"7.8.4","dirty-chai":"2.0.1","@babel/core":"7.9.6","npm-run-all":"4.1.5","babel-eslint":"10.1.0","@babel/register":"7.9.0","lodash.template":"4.5.0","@babel/preset-env":"7.9.6","@northscaler/mutrait":"1.2.0","config-cev-generator":"0.1.6","@babel/plugin-proposal-optional-chaining":"7.9.0"},"_npmOperationalInternal":{"tmp":"tmp/recurrify_1.3.2_1605198770625_0.8185443067031686","host":"s3://npm-registry-packages"}}},"time":{"created":"2020-01-27T18:01:37.083Z","modified":"2024-05-29T18:07:48.741Z","1.2.0-rc.0":"2020-01-27T18:01:37.523Z","1.2.0":"2020-01-27T18:05:47.437Z","1.2.1":"2020-02-17T22:13:09.886Z","1.3.0-rc.0":"2020-05-11T16:15:03.881Z","1.3.0-rc.1":"2020-05-11T16:19:17.149Z","1.3.0":"2020-05-11T16:24:33.900Z","1.3.1":"2020-06-03T16:39:46.244Z","1.3.2":"2020-11-12T16:32:50.801Z"},"maintainers":[{"email":"npm@northscaler.com","name":"npm_northscaler"},{"email":"npm@scispike.com","name":"npm_scispike"},{"email":"djannotta@gmail.com","name":"victorynap"}],"repository":{"url":"git+https://gitlab.com/northscaler-public/recurrify.git","type":"git"},"keywords":["cron","recurrence","schedule","time slot","availability"],"license":"MIT","homepage":"https://gitlab.com/northscaler-public/recurrify.git","bugs":{"url":"https://gitlab.com/northscaler-public/recurrify/issues"},"readme":"# recurrify\n\nBased on a shameless fork of [later.js](https://github.com/kirkins/later).\n\n* Added ability to get *all* time slots of a recurrence between a start and end time\n* Removed the parsers in favor of just dealing with recurrence definitions directly\n* Removed `setTimeout` and `setInterval` implementations\n* Removed bower and Makefile\n* Renamed `schedule` to `recurrence` and `occurrence` to `time slot`\n* Renamed package from `cronicle` to `recurrify`\n* WIP to update syntax to more modern ES6+\n\n## Installation\n\n`$ npm install recurrify`\n\n## Example usage\n\n```\nconst recurrify = require('recurrify')\n\nconst startDate = new Date('2018-10-01T13:00:00Z')\nconst endDate = new Date('2018-10-01T15:00:00Z')\n\n// every 5 minutes\nconst sched = { 'recurrences': [{ 'm': [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] }] }\nconst timeSlots = recurrify.recurrence(sched).all(startDate, endDate)\nconsole.log(timeSlots)\n```\n\n### Result\n```\n$ node example.js\n[ 2018-10-01T13:00:00.000Z,\n  2018-10-01T13:05:00.000Z,\n  2018-10-01T13:10:00.000Z,\n  2018-10-01T13:15:00.000Z,\n  2018-10-01T13:20:00.000Z,\n  2018-10-01T13:25:00.000Z,\n  2018-10-01T13:30:00.000Z,\n  2018-10-01T13:35:00.000Z,\n  2018-10-01T13:40:00.000Z,\n  2018-10-01T13:45:00.000Z,\n  2018-10-01T13:50:00.000Z,\n  2018-10-01T13:55:00.000Z,\n  2018-10-01T14:00:00.000Z,\n  2018-10-01T14:05:00.000Z,\n  2018-10-01T14:10:00.000Z,\n  2018-10-01T14:15:00.000Z,\n  2018-10-01T14:20:00.000Z,\n  2018-10-01T14:25:00.000Z,\n  2018-10-01T14:30:00.000Z,\n  2018-10-01T14:35:00.000Z,\n  2018-10-01T14:40:00.000Z,\n  2018-10-01T14:45:00.000Z,\n  2018-10-01T14:50:00.000Z,\n  2018-10-01T14:55:00.000Z,\n  2018-10-01T15:00:00.000Z ]\n```\n\n## Running tests\n\n`$ npm test`\n\n## Recurrences\nRecurrences in `recurrify` are json objects that define a set of time units along with the values that should be considered valid for that time unit.\nThe combination of a time unit with their valid values is called a *constraint*.\n`recurrify` then takes all of the constraints that have been defined and finds dates that match all of them.\n\nSince `recurrify` recurrences are json objects, they can easily be serialized and stored in caches and databases as needed.\nThey are also completely deterministic which means a recurrence will always produce exactly the same valid time slots.\nTherefore, time slots never need to be stored as they can always be recalculated from the recurrence definition.\n\n## Basic recurrences\nA basic recurrence is a set of time units along with their valid values.\nA date is only considered valid if it meets all of the constraints within a basic recurrence.\nA basic recurrence can include as many time units (with or without modifiers) as needed, in any order.\n\n```\n// a basic recurrence that is valid every day at 10:15am and 10:45am\nconst basic = {h: [10], m: [15,45]}\n```\n\nHere we can see a recurrence is made up of objects with properties that correspond to the various time units.\nIn this case `h` is the hour time unit and `m` is the minute time unit.\nThe values to consider valid are always stored in an array as the value of the property.\n\n\n**Note:** Basic recurrences are only valid as part of a complete recurrence definition.\n\n## Composite recurrences\nMultiple basic recurrences can be combined into a single composite recurrence by placing them into an array.\nA date is considered valid if any of the basic recurrences are valid (basically an OR of all of the basic recurrences).\nA composite recurrence can contain as many basic recurrences as needed.\n\n```\n// a composite recurrence that is valid every day at 10:15am and 10:45am\n// and every day at 5:30pm\nconst composite = [\n  {h: [10], m: [15,45]},\n  {h: [17], m: [30]}\n]\n\n```\n\n**Note:** Composite recurrences are only valid as part of a complete recurrence definition.\n\n## Exception recurrences\nAn exception recurrence is a basic or composite recurrence that defines when a recurrence should be considered invalid.\nA date is considered invalid if any of the basic recurrences within an exception recurrence are valid.\n\n```\n// an exception recurrence that makes any date in March as\n// well as any Monday of any month invalid\nconst exception = [\n  {M: [3]},\n  {dw: [2]}\n]\n```\n\n**Note:** Exception recurrences are only valid as part of a complete recurrence definition.\n\n## Complete definition\nA complete definition is a json object that at a minimum contains a `recurrences` property that defines a composite recurrence with at least one basic recurrence.\nOptionally, the definition can also include an `exceptions` property that defines a composite exception recurrence.\n\n### Examples\nHere's an example of a valid recurrence that fires every 10 minutes.\nThe composite recurrence is always placed in an object under a property named `recurrences`. Even if you only have a basic recurrence, the `recurrences` property must be an array.\n\n```\nconst recurrence = {\n  recurrences: [\n    {m: [0,10,20,30,40,50]}\n  ]\n}\n```\n\nHere's a valid recurrence definition that includes exceptions with modifiers.\nHere we see the composite exception recurrence is always placed in an object under a property named `exceptions`.\nEven if you only have a basic exception recurrence, the `exceptions` property must be an array.\n\n```\nconst recurrence = {\n  recurrences: [\n    {h: [10], m: [15,45]},\n    {h: [17], m: [30]}\n  ],\n  exceptions: [\n    {M_a: [3]},\n    {dw: [2]}\n  ]\n}\n```\n\n## Performance considerations\nWhile `recurrify` has been designed to efficiently calculate time slots for all types and complexities of recurrences, there are a few things to keep in mind for applications that have particularly high performance requirements.\n\n* Basic time units perform the best. These include years, months, days, hours, minutes, and seconds.\nCalculating ISO week of year is particularly expensive.\n* Recurrences without exceptions perform better than those with exceptions.\nDefining your recurrence without the need for exceptions will improve performance.\n* Use the `time` time unit instead of specifying hours and minutes separately when possible.\nReducing the number of constraints will generally improve performance.\n* Using `after` and `before` modifiers to eliminate the need for specifying a lot of valid values will improve performance, especially when calculating ranges.\n\n## time units\nTime units are the crux of the `recurrify` library and are used to define new recurrences.\n`recurrify` comes with a large assortment of time units and is also fully extensible making it easy to create custom time units.\n\nWhile time units are primarily used by `recurrify` to define recurrences and calculate time slots, they are also useful for performing time based calculations.\nCalculating values such as ISO week number, moving between days of the year, or figuring out how many days are in a month are all possible using the time unit interface.\n\nIf you don't see the time unit that you need for your recurrence, `recurrify` is fully extensible and it is easy to write your own.\nSee the custom time unit at the bottom of this page for an example.\n\n## Interface\nAll time units implement the same public interface for interacting with them:\n\n**`name`**\nThe name of the time unit.\n\n**`range`**\nThe rough number of seconds that are covered when moving from one instance of this time unit to the next instance.\n\n**`val(date)`**\nThe value of this time unit for the date specified.\n\n**`isValid(date, value)`**\nTrue if the specified value is valid for the specified date, false otherwise.\n\n**`extent(date)`**\nThe minimum and maximum valid values for the time unit for the specified date.\nIf the minimum value is not `0`, `0` can be specified in recurrences to indicate the maximum value.\nThis makes working with non-constant extents (like days in a month) easier.\n\n**`start(date)`**\nThe first second in which the value is the same as the value of the specified date.\nFor example, the start of an hour would be the hour with `0` minutes and `0` seconds.\n\n**`end(date)`**\nThe last second in which the value is the same as the value of the specified date.\nFor example, the end of an hour would be the hour with `59` minutes and `59` seconds.\n\n**`next(date, value)`**\nReturns the next date where the value is the value specified.\nSets the value to `1` if value specified is greater than the max allowed value.\n\n**`prev(`date, value`)**\nReturns the previous date where the value is the value specified.\nSets the value to the max allowed value if the value specified is greater than the max allowed value.\n\n## Second (second, s)\nSeconds in a minute, from `0` to `59`.\n\nUsing seconds in a recurrence:\n\n`const sched = {recurrences: [{s: [0, 15, 30, 45]}]}`\n\nPerforming seconds based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.second.name;\n// --> 'second'\n\nrecurrify.second.range;\n// --> 1\n\nrecurrify.second.val(d);\n// --> 5\n\nrecurrify.second.isValid(d, 10);\n// --> false\n\nrecurrify.second.extent();\n// --> [0, 59]\n\nrecurrify.second.start(d);\n// --> 'Fri, 22 Mar 2013 10:02:05 GMT'\n\nrecurrify.second.end(d);\n// --> 'Fri, 22 Mar 2013 10:02:05 GMT'\n\nrecurrify.second.next(d, 27);\n// --> 'Fri, 22 Mar 2013 10:02:27 GMT'\n\nrecurrify.second.prev(d, 27);\n// --> 'Fri, 22 Mar 2013 10:01:27 GMT'\n```\n\n## Minute (minute, m)\nMinutes in an hour, from `0` to `59`.\n\nUsing minutes in a recurrence:\n\n`const sched = {recurrences: [{m: [0, 15, 30, 45]}]}`\n\nPerforming minutes based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.minute.name;\n// --> 'minute'\n\nrecurrify.minute.range;\n// --> 60\n\nrecurrify.minute.val(d);\n// --> 2\n\nrecurrify.minute.isValid(d, 2);\n// --> true\n\nrecurrify.minute.extent();\n// --> [0, 59]\n\nrecurrify.minute.start(d);\n// --> 'Fri, 22 Mar 2013 10:02:00 GMT'\n\nrecurrify.minute.end(d);\n// --> 'Fri, 22 Mar 2013 10:02:59 GMT'\n\nrecurrify.minute.next(d, 27);\n// --> 'Fri, 22 Mar 2013 10:27:00 GMT'\n\nrecurrify.minute.prev(d, 27);\n// --> 'Fri, 22 Mar 2013 09:27:59 GMT'\n```\n\n## Hour (hour, h)\nHours in a day, from `0` to `23`.\n\nUsing hours in a recurrence:\n\n`const sched = {recurrences: [{h: [0, 5, 12]}]}`\n\nPerforming hours based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.hour.name;\n// --> 'hour'\n\nrecurrify.hour.range;\n// --> 3600\n\nrecurrify.hour.val(d);\n// --> 10\n\nrecurrify.hour.isValid(d, 2);\n// --> false\n\nrecurrify.hour.extent();\n// --> [0, 23]\n\nrecurrify.hour.start(d);\n// --> 'Fri, 22 Mar 2013 10:00:00 GMT'\n\nrecurrify.hour.end(d);\n// --> 'Fri, 22 Mar 2013 10:59:59 GMT'\n\nrecurrify.hour.next(d, 5);\n// --> 'Sat, 23 Mar 2013 05:00:00 GMT'\n\nrecurrify.hour.prev(d, 21);\n// --> 'Thu, 21 Mar 2013 21:59:59 GMT'\n```\n\n## Time (time, t)\nTime of day, represented as seconds since midnight. From `0` to `86399` (60 * 60 * 24 - 1).\n\n\nUsing time in a recurrence:\n\n`const sched = {recurrences: [{t: [6500]}]}`\n\nPerforming time based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.time.name;\n// --> 'time'\n\nrecurrify.time.range;\n// --> 1\n\nrecurrify.time.val(d);\n// --> 36125\n\nrecurrify.time.isValid(d, 36125);\n// --> true\n\nrecurrify.time.extent();\n// --> [0, 86399]\n\nrecurrify.time.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.time.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.time.next(d, 60);\n// --> 'Sat, 23 Mar 2013 00:01:00 GMT'\n\nrecurrify.time.prev(d, 60);\n// --> 'Fri, 22 Mar 2013 00:01:00 GMT'\n```\n\n## Day (day, D)\nDays of a month, from `1` to the max days in the month.\nSpecify `0` for the varying last day of the month, which is between `28` through `31`.\n\nUsing days in a recurrence:\n\n`const sched = {recurrences: [{D: [0]}]}`\n\nPerforming day based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.day.name;\n// --> 'day'\n\nrecurrify.day.range;\n// --> 86400\n\nrecurrify.day.val(d);\n// --> 22\n\nrecurrify.day.isValid(d, 3);\n// --> false\n\nrecurrify.day.extent(d);\n// --> [1, 31]\n\nrecurrify.day.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.day.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.day.next(d, 11);\n// --> 'Thu, 11 Apr 2013 00:00:00 GMT'\n\nrecurrify.day.prev(d, 2);\n// --> 'Sat, 02 Mar 2013 23:59:59 GMT'\n```\n\n## Day of week (dayOfWeek, dw, d)\nDays of a week, from `1` to `7`.\nSpecify `0` for the last day of the week (Saturday).\n\nUsing days of week in a recurrence:\n\n`const sched = {recurrences: [{dw: [2,3,4,5,6]}]}`\n\nPerforming day of week based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfWeek.name;\n// --> 'day of week'\n\nrecurrify.dayOfWeek.range;\n// --> 86400\n\nrecurrify.dayOfWeek.val(d);\n// --> 6\n\nrecurrify.dayOfWeek.isValid(d, 3);\n// --> false\n\nrecurrify.dayOfWeek.extent();\n// --> [1, 7]\n\nrecurrify.dayOfWeek.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeek.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.dayOfWeek.next(d, 1);\n// --> 'Sun, 24 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeek.prev(d, 5);\n// --> 'Thu, 21 Mar 2013 23:59:59 GMT'\n```\n\n## Day of week count (dayOfWeekCount, dc)\nThe nth day of the week within a month, from `1` to the max weeks in a month.\nSpecify `0` for the last day instance.\nUsed together with the day of the week time unit to specify things like the 2nd Tuesday or last Friday of a month.\n\nUsing days of week count in a recurrence:\n\n`const sched = {recurrences: [{dc: [2]}]}`\n\nPerforming day of week count based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfWeekCount.name;\n// --> 'day of week count'\n\nrecurrify.dayOfWeekCount.range;\n// --> 604800\n\nrecurrify.dayOfWeekCount.val(d);\n// --> 4\n\nrecurrify.dayOfWeekCount.isValid(d, 4);\n// --> true\n\nrecurrify.dayOfWeekCount.extent(d);\n// --> [1, 5]\n\nrecurrify.dayOfWeekCount.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeekCount.end(d);\n// --> 'Thu, 28 Mar 2013 23:59:59 GMT'\n\n// zero is special cased and means the last instance of\n// a day of the week in the month, instead of meaning the\n// first day of the week with the highest instance count\n// which would have been Mar 29 with value 5.\nrecurrify.dayOfWeekCount.next(d, 0);\n// --> 'Mon, 25 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfWeekCount.prev(d, 2);\n// --> 'Thu, 14 Mar 2013 23:59:59 GMT'\n```\n\n## Day of year (dayOfYear, dy)\nDay of the year, from `1` to the max days in year.\nSpecify `0` for last day of the year.\n\nUsing days of year in a recurrence:\n\n`const sched = {recurrences: [{dy: [189, 267]}]}`\n\nPerforming day of year based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.dayOfYear.name;\n// --> 'day of year'\n\nrecurrify.dayOfYear.range;\n// --> 86400\n\nrecurrify.dayOfYear.val(d);\n// --> 81\n\nrecurrify.dayOfYear.isValid(d, 4);\n// --> false\n\nrecurrify.dayOfYear.extent(d);\n// --> [1, 365]\n\nrecurrify.dayOfYear.start(d);\n// --> 'Fri, 22 Mar 2013 00:00:00 GMT'\n\nrecurrify.dayOfYear.end(d);\n// --> 'Fri, 22 Mar 2013 23:59:59 GMT'\n\nrecurrify.dayOfYear.next(d, 256);\n// --> 'Fri, 13 Sep 2013 00:00:00 GMT'\n\nrecurrify.dayOfYear.prev(d, 44);\n// --> 'Wed, 13 Feb 2013 23:59:59 GMT'\n```\n\n## Week of month (weekOfMonth, wm)\nWeek of the month where the 1st of the month is week `1` and following weeks start on Sunday.\nFrom `1` to max weeks in the month.\nSpecify `0` for last week of the month.\n\nUsing weeks of month in a recurrence:\n\n`const sched = {recurrences: [{wm: [1, 2]}]}`\n\nPerforming week of month based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.weekOfMonth.name;\n// --> 'week of month'\n\nrecurrify.weekOfMonth.range;\n// --> 604800\n\nrecurrify.weekOfMonth.val(d);\n// --> 4\n\nrecurrify.weekOfMonth.isValid(d, 4);\n// --> true\n\nrecurrify.weekOfMonth.extent(d);\n// --> [1, 6]\n\nrecurrify.weekOfMonth.start(d);\n// --> 'Sun, 17 Mar 2013 00:00:00 GMT'\n\nrecurrify.weekOfMonth.end(d);\n// --> 'Sat, 23 Mar 2013 23:59:59 GMT'\n\nrecurrify.weekOfMonth.next(d, 1);\n// --> 'Mon, 01 Apr 2013 00:00:00 GMT'\n\nrecurrify.weekOfMonth.prev(d, 2);\n// --> 'Sat, 09 Mar 2013 23:59:59 GMT'\n```\n\n## ISO Week of year (weekOfYear, wy)\nThe ISO-8601 week of the year.\nFrom `1` to the max ISO week in the year.\nSpecify `0` for last ISO week of the year.\n\nUsing weeks of year in a recurrence:\n\n`const sched = {recurrences: [{wy: [13,26,39,0]}]}`\n\nPerforming week of year based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.weekOfYear.name;\n// --> 'week of year'\n\nrecurrify.weekOfYear.range;\n// --> 604800\n\nrecurrify.weekOfYear.val(d);\n// --> 12\n\nrecurrify.weekOfYear.isValid(d, 21);\n// --> false\n\nrecurrify.weekOfYear.extent(d);\n// --> [1, 52]\n\nrecurrify.weekOfYear.start(d);\n// --> 'Mon, 18 Mar 2013 00:00:00 GMT'\n\nrecurrify.weekOfYear.end(d);\n// --> 'Sun, 24 Mar 2013 23:59:59 GMT'\n\nrecurrify.weekOfYear.next(d, 47);\n// --> 'Mon, 18 Nov 2013 00:00:00 GMT'\n\nrecurrify.weekOfYear.prev(d, 52);\n// --> 'Sun, 30 Dec 2012 23:59:59 GMT'\n```\n\n## Month (month, M)\nMonth of the year, from `1` to `12`.\nSpecify `0` for the last month of the year.\n\nUsing months in a recurrence:\n\n`const sched = {recurrences: [{M: [3,5,7]}]}`\n\nPerforming months based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.month.name;\n// --> 'month'\n\nrecurrify.month.range;\n// --> 2629740\n\nrecurrify.month.val(d);\n// --> 3\n\nrecurrify.month.isValid(d, 3);\n// --> true\n\nrecurrify.month.extent();\n// --> [1, 12]\n\nrecurrify.month.start(d);\n// --> 'Fri, 01 Mar 2013 00:00:00 GMT'\n\nrecurrify.month.end(d);\n// --> 'Sun, 31 Mar 2013 23:59:59 GMT'\n\nrecurrify.month.next(d, 11);\n// --> 'Fri, 01 Nov 2013 00:00:00 GMT'\n\nrecurrify.month.prev(d, 2);\n// --> 'Thu, 28 Feb 2013 23:59:59 GMT'\n```\n\n## Year (year, Y)\nYear, from `1970` to `2099`.\n\nUsing years in a recurrence:\n\n`const sched = {recurrences: [{Y: [2013, 2014, 2015]}]}`\n\nPerforming years based calculations:\n\n```javascript\nconst d = new Date('2013-03-22T10:02:05Z');\n\nrecurrify.year.name;\n// --> 'year'\n\nrecurrify.year.range;\n// --> 31556900\n\nrecurrify.year.val(d);\n// --> 2013\n\nrecurrify.year.isValid(d, 2013);\n// --> true\n\nrecurrify.year.extent();\n// --> [1970, 2099]\n\nrecurrify.year.start(d);\n// --> 'Tue, 01 Jan 2013 00:00:00 GMT'\n\nrecurrify.year.end(d);\n// --> 'Tue, 31 Dec 2013 23:59:59 GMT'\n\nrecurrify.year.next(d, 2014);\n// --> 'Wed, 01 Jan 2014 00:00:00 GMT'\n\nrecurrify.year.prev(d, 2012);\n// --> 'Mon, 31 Dec 2012 23:59:59 GMT'\n```\n\n## Writing a custom time unit\n`recurrify` is fully extensible and it is easy to create your own custom time units that can be used to define new recurrences.\nTo keep things simple, we'll walk through creating a new time unit for indicating morning, afternoon, and evening.\nFor our purposes, morning will be before noon and have a value of `0`, afternoon will be before 6pm and have a value of `1`, and evening will be before midnight and have a value of `2`.\n\nThe first step is to create a name and id for the modifier and add it to the recurrify namespace.\n\n```javascript\nrecurrify.partOfDay = recurrify.pd = {\n  // interface implementation goes here\n};\n```\n\nNext, we need to implement the time unit interface.\nFirst we will just specify the name of this time unit.\n\n`name: 'part of day',`\n\nThe range is approximately 6 hours.\nThough some of our periods are longer and some shorter,\nwe'll use the shortest range which is afternoon at 6 hours.\n\n`range: recurrify.h.range * 6,`\n\nWe then implement `val` to return the appropriate value based on the definition described previously.\n\n```javascript\nval: function(d) {\n  return recurrify.h.val(d) < 12 ? 0 :\n         recurrify.h.val(d) < 6 ? 1 :\n         2;\n},\n```\n\nThen we can use our new `val` function to implement `isValid`.\n\n```javascript\nisValid: function(d, val) {\n  return recurrify.pd.val(d) === val;\n},\n```\n\nThe extent is always going to be the same for every day so we can just return a constant array here.\n\n`extent: function(d) { return [0, 2]; },`\n\nNext we need to implement `start` and `end` based on the current time unit.\nThis will be the start and end of each part of the day that we've defined.\n\n```javascript\nstart: function(d) {\n  const hour = recurrify.pd.val(d) === 0 ? 0 :\n                recurrify.pd.val(d) === 1 ? 12 :\n                6;\n\n  // recurrify.date.next is a helper function for creating the date in UTC or\n  // localTime as appropriate\n  return recurrify.date.next(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    recurrify.D.val(d),\n    hour\n  );\n},\n\nend: function(d) {\n  const hour = recurrify.pd.val(d) === 0 ? 11 :\n                recurrify.pd.val(d) === 1 ? 5 :\n                23;\n\n  // recurrify.date.prev is a helper function for creating the date in UTC or\n  // localTime as appropriate, and automatically adjusts the date to be at\n  // the last second of the specified time\n  return recurrify.date.prev(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    recurrify.D.val(d),\n    hour\n  );\n},\n```\n\nFinally, we need to implement `next` and `prev` so that you can move to different parts of the day.\nWe need to make sure to increment and decrement the day appropriately if we've already passed the specified value.\n\n```javascript\nnext: function(d, val) {\n  const hour = val === 0 ? 0 : val === 1 ? 12 : 18;\n\n  return recurrify.date.next(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    // increment the day if we already passed the desired time unit\n    recurrify.D.val(d) + (hour < recurrify.h.val(d) ? 1 : 0),\n    hour\n  );\n},\n\nprev: function(d, val) {\n  const hour = val === 0 ? 11 : val === 1 ? 5 : 23;\n\n  return recurrify.date.prev(\n    recurrify.Y.val(d),\n    recurrify.M.val(d),\n    // decrement the day if we already passed the desired time unit\n    recurrify.D.val(d) + (hour > recurrify.h.val(d) ? -1 : 0),\n    hour\n  );\n}\n```\n\n### Full implementation\nHere is the code for the completed example.\nTo use the time unit, just add this code after including `recurrify` into your project and before you use it in any recurrences.\n\n```javascript\nrecurrify.partOfDay = recurrify.pd = {\n\n  name: 'part of day',\n\n  range: recurrify.h.range * 6,\n\n  val: function(d) {\n    return recurrify.h.val(d) < 12 ? 0 :\n           recurrify.h.val(d) < 18 ? 1 :\n           2;\n  },\n\n  isValid: function(d, val) {\n    return recurrify.pd.val(d) === val;\n  },\n\n  extent: function(d) { return [0, 2]; },\n\n  start: function(d) {\n    const hour = recurrify.pd.val(d) === 0 ? 0 :\n                  recurrify.pd.val(d) === 1 ? 12 :\n                  18;\n\n    return recurrify.date.next(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      recurrify.D.val(d),\n      hour\n    );\n  },\n\n  end: function(d) {\n    const hour = recurrify.pd.val(d) === 0 ? 11 :\n                  recurrify.pd.val(d) === 1 ? 5 :\n                  23;\n\n    return recurrify.date.prev(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      recurrify.D.val(d),\n      hour\n    );\n  },\n\n  next: function(d, val) {\n    const hour = val === 0 ? 0 : val === 1 ? 12 : 18;\n\n    return recurrify.date.next(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      // increment the day if we already passed the desired time unit\n      recurrify.D.val(d) + (hour < recurrify.h.val(d) ? 1 : 0),\n      hour\n    );\n  },\n\n  prev: function(d, val) {\n    const hour = val === 0 ? 11 : val === 1 ? 5 : 23;\n\n    return recurrify.date.prev(\n      recurrify.Y.val(d),\n      recurrify.M.val(d),\n      // decrement the day if we already passed the desired time unit\n      recurrify.D.val(d) + (hour > recurrify.h.val(d) ? -1 : 0),\n      hour\n    );\n  }\n};\n```\n\n### Usage\nUsing the custom time unit is exactly the same as using a built-in time unit.\n\n```javascript\n// use our new time unit to specify every 15 mins at night\nconst sched = recurrify.parse.recur().every(15).minute().on(2).customPeriod('pd'),\n    next = recurrify.recurrence(sched).next(1, new Date(2013, 3, 21));\n\nconsole.log(next.toUTCString());\n// --> Sun, 21 Apr 2013 18:00:00 GMT\n```\n\n## Modifiers\nWith `recurrify`, not only can you write your own custom time units, you can also write custom modifiers that can change the behavior of existing time units.\nThe modifiers sit in between the scheduling engine and the time unit allowing you to intercept and modify the results that are returned by the time unit.\n\nModifies are specified by attaching `_(modifier-id)` to the time unit id that you want to modify.\nThe same time unit can be used with different modifiers within the same recurrence.\n\n## after: `_a`\nModifies the corresponding time unit such that all values after and including the specified value is considered valid.\nThis modifier can be used with any time unit.\nUseful for creating more compact recurrences when a time unit has a lot of consecutive valid values.\n\n```javascript\n// all hours after 5:00pm will be valid\nconst sched = {recurrences: [{h_a: [17]}]};\n\n// equivalent to\nconst sched = {recurrences: [{h: [17,18,19,20,21,22,23]}]};\n```\n\n## before `_b`\nModifies the corresponding time unit such that all values before (but not including) the specified value is considered valid.\nThis modifier can be used with any time unit.\nUseful for creating more compact recurrences when a time unit has a lot of consecutive valid values.\n\n```javascript\n// all hours before 5:00pm will be valid\nconst sched = {recurrences: [{h_b: [17]}]};\n\n// equivalent to\nconst sched = {recurrences: [{h: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}]};\n```\n\n## Writing a custom modifier\nCustom modifiers are very similar to custom time units and share the same interface.\nTo keep things simple, we'll walk through creating a modifier to change the `month` period to work with values `0`-`11` instead of `1`-`12`.\n\nThe first step is to create a name and id for the modifier and add it to the modifier namespace.\nModifiers take the time unit that is being modified along with the specified values as arguments.\n\n```javascript\nrecurrify.modifier.month = recurrify.modifier.m = function(period, values) {\n  if(period.name !== 'month') {\n    throw new Error('Month modifier only works with months!');\n  }\n\n  return {\n    // interface implementation goes here\n  };\n};\n```\n\nNext, we need to implement the same interface that time units implement and modify them to work with the new set of values.\nFirst, we will just modify the `name` to include a reference to the modifier.\n\n`name: 'reIndexed ' + period.name,`\n\nThe range is the same, so we just pass it through.\n\n`range: period.range,`\n\nWe then modify the `val` returned by subtracting `1` since our new indices are one less than the original ones.\n\n`val: function(d) { return period.val(d) - 1; },`\n\nNext, `isValid` is modified by tweaking the value that is passed in so that it is in the range that the month time unit expects.\n\n`isValid: function(d, val) { return period.isValid(d, val+1); },`\n\nThe `extent` also needs to be modified to reflect the new extent that goes from `0`-`11`.\nNow that the extent starts at `0`, the recurrence engine will no longer assume that a `0` value means \"last\".\nThere is nothing else that we need to do to correct for that behavior.\n\n`extent: function(d) { return [0, 11]; },`\n\nThe `start` and `end` dates for the month will be the same, so we can just pass those through to the time unit without modification.\n\n```javascript\nstart: period.start,\nend: period.end,\n```\n\nFinally, the values passed into `next` and `prev` need to be updated to be in the range that the month time unit expects.\n\n```javascript\nnext: function(d, val) { return period.next(d, val+1); },\nprev: function(d, val) { return period.prev(d, val+1); }\n```\n\n### Full implementation\nHere is the code for the completed example.\nTo use the modifier, just add this code after including `recurrify` into your project and before you use it in any recurrences.\n\n```javascript\nrecurrify.modifier.month = recurrify.modifier.m = function(period, values) {\n  if(period.name !== 'month') {\n    throw new Error('Month modifier only works with months!');\n  }\n\n  return {\n    name:     'reIndexed ' + period.name,\n    range:    period.range,\n    val:      function(d) { return period.val(d) - 1; },\n    isValid:  function(d, val) { return period.isValid(d, val+1); },\n    extent:   function(d) { return [0, 11]; },\n    start:    period.start,\n    end:      period.end,\n    next:     function(d, val) { return period.next(d, val+1); },\n    prev:     function(d, val) { return period.prev(d, val+1); }\n  };\n};\n```\n\n### Usage\nUsing the custom modifier is exactly the same as using a built-in modifier.\n\n```javascript\n// without our modifier, 2 means February\nconst sched1 = {recurrences: [{M: [2]}]};\n\nrecurrify.recurrence(sched1).next(1, new Date(2013, 3, 21));\n// --> Sat, 01 Feb 2014 00:00:00 GMT\n\n// use our new modifier so that 2 now means March\nconst sched = recurrify.parse.recur().customModifier('m', 2).month();\n\nnext = recurrify.recurrence(sched2).next(1, new Date(2013, 3, 21));\n// --> Sat, 01 Mar 2014 00:00:00 GMT\n```\n\n## Time Slots\nOnce a recurrence has been defined, it can be used to calculate future and past time slots of that recurrence.\nA time slot is a date that meets all of the constraints imposed by the recurrence.\n\nIn order to improve performance, recurrences are first compiled prior to time slots being calculated.\nThe compiled version of the recurrence can be reused to find additional time slots as needed.\n\nTo compile a recurrence, pass the recurrence definition to `recurrify.recurrence`.\n\n`const sched = recurrify.recurrence(recurrence)`\n\n> TIP: All recurrence definitions are timezone agnostic.\nWhen you need to calculate time slots, you can decide to perform the calculation using local time or UTC.\n\n```javascript\n// set recurrify to use UTC time (the default)\nrecurrify.date.UTC();\n\n// set recurrify to use local time\nrecurrify.date.localTime();\n```\n\n## `isValid(date)`\nReturns true if the `date` passed in is a valid time slot of the recurrence, false otherwise.\n\n`const valid = recurrify.recurrence(recurrence).isValid(date)`\n\n### Examples\n\n```javascript\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute());\n\nsched.isValid(new Date('2013-03-22T10:02:00Z'));\n// --> true\n\nsched.isValid(new Date('2013-03-22T10:22:00Z'));\n// --> false\n\nsched.isValid(new Date('2013-03-22T10:02:05Z'));\n// --> false\n```\n\n## Calculating instances\nInstances are individual dates that meet all of the constraints that are imposed by the recurrence.\nInstances can be calculated both forwards and backwards, in any quantity, and optionally between a start and end date.\nWhen calculating multiple instances, the minimum time between instances is based on the smallest ranged time unit.\n\n**`recurrify.recurrence(recurrence).all(start, end)`**\nCalculates *all* time slots of `recurrence` starting from the `start` date and ending before the `end` date.\nIf an end date is not specified, the maximum results returned is `1000000`.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).all(startDate, endDate)`\n\n**`recurrify.recurrence(recurrence*).next(*count, start, end*)`**\nCalculates the next `count` time slots of `recurrence`, optionally starting from the `start` date and ending before the `end` date.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).next(2)`\n\n\n**`recurrify.recurrence(recurrence).prev(count, start, end)`**\nCalculates the previous `count` time slots of `recurrence`, optionally starting from the `start` date and ending before the `end` date.\nWhen using previous, the `start` date must be greater than the `end` date.\n\n`recurrify.recurrence({recurrences: [{m: [5]}]}).prev(2)`\n\n### Examples\n```javascript\n// sched for minute equal to 1,2, or 3\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute()),\n    start = new Date('2013-05-22T10:22:00Z');\n\n// get the next instance\nsched.next(1, start);\n// --> Mon, 22 May 2013 11:01:00 GMT\n\n// get the next 5 instances\nsched.next(5, start);\n// --> [ 'Mon, 22 May 2013 11:01:00 GMT',\n      'Mon, 22 May 2013 11:02:00 GMT',\n      'Mon, 22 May 2013 11:03:00 GMT',\n      'Mon, 22 May 2013 12:01:00 GMT',\n      'Mon, 22 May 2013 12:02:00 GMT' ]\n\n// get the previous instance\nsched.prev(1, start);\n// --> Mon, 22 May 2013 10:03:00 GMT\n```\n\n## Calculating ranges\nRanges combine consecutively valid instances into a single start and end block of time.\nThe start time is the first valid instance of the block of time.\nThe end time is the first invalid time after the block.\n\nRanges are useful when scheduling blocks of time such as a meeting or activity.\nThe recurrence definition defines the start and end time of the activity and then ranges are used to find their time slots.\n\n**`recurrify.recurrence(recurrence).nextRange(count, start, end)`**\nCalculates the next count ranges of recurrence, optionally starting from the start date and ending before the end date.\n\n`recurrify.recurrence({recurrences: [{m:[5,6,7]}]}).nextRange(2)`\n\n**`recurrify.recurrence(recurrence).prevRange(count, start, end)`**\nCalculates the previous count ranges of recurrence, optionally starting from the start date and ending before the end date.\n\n`recurrify.recurrence({recurrences: [{m:[5,6,7]}]}).prevRange(2)`\n\n### Examples\n```javascript\n// sched for minute equal to 1,2, or 3\nconst sched = recurrify.recurrence(recurrify.parse.recur().on(1,2,3).minute()),\n    start = new Date('2013-05-22T10:22:00Z');\n\n// get the next range\nsched.nextRange(1, start);\n// --> ['Mon, 22 May 2013 11:01:00 GMT', 'Mon, 22 May 2013 11:04:00 GMT']\n\n// get the next 5 ranges\nsched.nextRange(5, start);\n// --> [\n      ['Mon, 22 May 2013 11:01:00 GMT', 'Mon, 22 May 2013 11:04:00 GMT']\n      ['Mon, 22 May 2013 12:01:00 GMT', 'Mon, 22 May 2013 12:04:00 GMT']\n      ['Mon, 22 May 2013 13:01:00 GMT', 'Mon, 22 May 2013 13:04:00 GMT']\n      ['Mon, 22 May 2013 14:01:00 GMT', 'Mon, 22 May 2013 14:04:00 GMT']\n      ['Mon, 22 May 2013 15:01:00 GMT', 'Mon, 22 May 2013 15:04:00 GMT']\n    ]\n\n// get the previous range\nsched.prevRange(1, start);\n// --> ['Mon, 22 May 2013 10:01:00 GMT', 'Mon, 22 May 2013 10:04:00 GMT']\n```\n","readmeFilename":"README.md"}