PHPackages                             iansltx/business-days - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. iansltx/business-days

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

iansltx/business-days
=====================

A way to calculate date periods based on the concept of business days rather than calendar days

1.4.0(11y ago)16454[1 issues](https://github.com/iansltx/BusinessDays/issues)BSD-2-ClausePHPPHP &gt;=5.5.0

Since Feb 15Pushed 10y ago1 watchersCompare

[ Source](https://github.com/iansltx/BusinessDays)[ Packagist](https://packagist.org/packages/iansltx/business-days)[ Docs](https://github.com/iansltx/BusinessDays)[ RSS](/packages/iansltx-business-days/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (5)Dependencies (3)Versions (6)Used By (0)

BusinessDays
============

[](#businessdays)

[![Author](https://camo.githubusercontent.com/d6df2f9eb6f5dde6cc2b1888217c5261753ed234819e5e7018b03d72340d24f6/687474703a2f2f696d672e736869656c64732e696f2f62616467652f617574686f722d4069616e736c74782d626c75652e7376673f7374796c653d666c61742d737175617265)](https://twitter.com/iansltx)[![Latest Version](https://camo.githubusercontent.com/7fb4bc3ad50f1bb4ade03bac714732b90711e98b6b7ad84c7c2de69bcebd2e9b/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652f69616e736c74782f427573696e657373446179732e7376673f7374796c653d666c61742d737175617265)](https://github.com/iansltx/BusinessDays/releases)[![Software License](https://camo.githubusercontent.com/b60331a2084501dc07cf6d6964c0da58dd005d89c45cf3b28b4b22b60f5ec00f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4253442d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![Build Status](https://camo.githubusercontent.com/662190122977b66e32490b7425ac1bf55abb033789614ad8d2b6b56fb4a282be/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f69616e736c74782f427573696e657373446179732f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://travis-ci.org/iansltx/BusinessDays)[![HHVM Status](https://camo.githubusercontent.com/3ecc1aba2ac8599004840ba265f1cb3e0e986a4d8be824a103be890bb35c3ec1/68747470733a2f2f696d672e736869656c64732e696f2f6868766d2f69616e736c74782f627573696e6573732d646179732e7376673f7374796c653d666c61742d737175617265)](http://hhvm.h4cc.de/package/iansltx/business-days)[![Coverage Status](https://camo.githubusercontent.com/a5e7fdedb02164948f2c79e004ec12321a174f256d1e5fd57e07dc56eee115f5/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636c696d6174652f636f7665726167652f6769746875622f69616e736c74782f427573696e657373446179732e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/iansltx/BusinessDays/code-structure)[![Quality Score](https://camo.githubusercontent.com/f7cc30c82b92a6463886fb77232cc2462598ab297f05709fe332d57fcdc4b1ec/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f69616e736c74782f427573696e657373446179732e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/iansltx/BusinessDays)[![Code Climate](https://camo.githubusercontent.com/63e8c90c20f989fb12370d1d8f1c2c5164c325b1b0c2d46f706265600630723c/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636c696d6174652f6769746875622f69616e736c74782f427573696e657373446179732e7376673f7374796c653d666c61742d737175617265)](https://codeclimate.com/github/iansltx/BusinessDays)[![SensioLabs Insight](https://camo.githubusercontent.com/b0df51ed7609be53f48848007191a31a621b0d26946c870a46f3a7180eef0354/68747470733a2f2f696d672e736869656c64732e696f2f73656e73696f6c6162732f692f39333361666263662d376132382d346461302d393939382d3334306263376462613637632e7376673f7374796c653d666c61742d737175617265)](https://insight.sensiolabs.com/projects/933afbcf-7a28-4da0-9998-340bc7dba67c)[![Total Downloads](https://camo.githubusercontent.com/bf2ad449d06af648690f8c5cc0889cf0c9dc0248704135cb2dc09490dec2a3bd/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f69616e736c74782f627573696e6573732d646179732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/iansltx/business-days)

BusinessDays is a set of tools for dealing with date calculations when certain (non-business/holiday) days don't count.

This package contains two calculators, FastForwarder and Rewinder, plus a set of filters that can be used with them.

FastForwarder is configured with a day count (N) and a set of callbacks defining what *isn't* a business day, Once configured, provide it with a start date and it'll return a date that is the first business day at least N business days in the future. Rewinder does the same thing, except working in the opposite temporal direction.

The start date passed into the calculators can be a DateTime or DateTimeImmutable, or a subclass thereof. The value returned will be a clone of whatever was passed in, with its timestamp updated.

This library should conform to PSRs 1, 2 and 4, and requires PHP 5.5 or newer (5.6 recommended). 5.6 is the oldest version of the runtime that has automated tests running.

Install
-------

[](#install)

Via Composer

```
$ composer require iansltx/business-days
```

If you don't want Composer, you may download the source zipball directly from GitHub and load it using a PSR-4 compliant autoloader. If you don't have such an autoloader, require `autoload.php` to get one that works for this library.

Usage
-----

[](#usage)

### Filters

[](#filters)

Calculators (FastForwarder and Rewinder) evaluate filter functions/methods/closures to see whether a given date should be classified as a business day or not. You can provide a closure directly, or use one of the convenience methods to compose a filter more quickly.

The convenience methods simply wrap either functions from StaticFilter or create closures via FilterFactory. Either of these filter utility classes may be used standalone to filter DateTime objects. Additionally, these two filter classes provide more filters than are exposed via the convenience methods on FastForwarder/Rewinder, so check them before rebuilding a filter definition from scratch.

Note that filters requiring numeric input have internal checks that require an explicit int cast for all parameters. Also, while skipWhen filters are executed on-add to check argument and return types, non-closure arguments are allowed, so you can use object methods, utility functions or global functions can be used as filters in addition to closures.

Let's set up a FastForwarder, add some filters, and do some calculations...

```
use iansltx\BusinessDays\FilterFactory;
use iansltx\BusinessDays\StaticFilter;

// set up the instance with a day count
$ff = new iansltx\BusinessDays\FastForwarder(10);

// add a closure-based filter
$ff->skipWhen(function (\DateTimeInterface $dt) { //
    return $dt->format('m') == 12 && $dt->format('d') == 25;
}, 'christmas');

// define a closure from the filter factory, then pass it in
$dayAfterChristmasFriday = FilterFactory::monthAndDayOnDayOfWeek(12, 26, 5);
$ff->skipWhen($dayAfterChristmasFriday, 'day_after_christmas_friday');

// convenience method; saved to filter slot 'weekend'
$ff->skipWhenWeekend();

// overwrites 'weekend' slot with an identical call
$ff->skipWhen(['iansltx\BusinessDays\StaticFilter', 'isWeekend'], 'weekend');

// use some other convenience methods, this time pulling from FilterFactory and using method chaining
$ff->skipWhenNthDayOfWeekOfMonth(3, 1, 2, 'presidents_day') // third Monday of February
   ->skipWhenNthDayOfWeekOfMonth(4, 4, 11, 'thanksgiving') // fourth Thursday of November
   ->skipWhenMonthAndDay(1, 1) // auto-named to md_1_1 since a filter name wasn't provided
   ->skipWhen([StaticFilter::class, 'isGoodFriday'], 'good_friday')
   ->skipWhen([StaticFilter::class, 'isEasterMonday'], 'easter_monday');

// calculate some dates
echo $ff->exec(new \DateTime('2015-11-20 09:00:00'))->format('Y-m-d H:i:s'); // 2015-12-07 09:00:00
echo $ff->exec(new \DateTimeImmutable('2015-02-12 09:00:00'))->format('Y-m-d H:i:s'); // 2015-02-27 09:00:00
```

**NOTE:** Fractional days are rounded up to the next whole day amount; e.g. 2.5 days will be treated as 3.

### Exporting/Importing Filter Sets

[](#exportingimporting-filter-sets)

The list of filters associated with a given calculator may be dumped as an array via `getSkipWhenFilters()`. This array can be set as filter storage for another calculation class when that class is created, so a set of filters only needs to be defined once.

Some notes on this functionality:

1. Any filters added after construction won't propagate to the calculation object that you got the original filter set from.
2. Passing in a set of filters at construction time will not run argument and return type tests on filters contained therein.

With that, let's copy our filters to a new Rewinder, which as its name suggests goes in the opposite direction, and calculate a few more dates.

```
// you could also create a FastForwarder with a negative day count with the same effect
$rw = new iansltx\BusinessDays\Rewinder(2, $ff->getSkipWhenFilters());

echo $rw->exec(new \DateTime('2015-02-10 09:00:00'))->format('Y-m-d H:i:s'); // 2015-02-06 09:00:00
echo $rw->exec(new \DateTime('2015-02-18 09:00:00'))->format('Y-m-d H:i:s'); // 2015-02-13 09:00:00
```

### For More Info

[](#for-more-info)

For more information on filter arguments etc., take a look at the source. All methods and classes have docblocks. The callable-based syntax of skipWhen() allows for arbitrarily complex definitions of whether a date should be skipped, as can be seen in more complex filters like isEasterMonday.

Testing
-------

[](#testing)

```
$ composer test
```

`humbug.json` is included if you want to do mutation testing with [Humbug](https://github.com/padraic/humbug). Currently, not all mutations are caught; PRs are welcome to help rectify this issue. `composer test` only runs PHPUnit tests.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details. Additional static filters/filter factories are welcome!

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Ian Littman](https://github.com/iansltx)
- [All Contributors](../../contributors)

License
-------

[](#license)

This library is BSD 2-clause licensed. Please see [License File](LICENSE.md) for more information.

###  Health Score

32

—

LowBetter than 72% of packages

Maintenance19

Infrequent updates — may be unmaintained

Popularity23

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 97.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

5

Last Release

4098d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/fa8d95bd3b52b4acddd5eb36c627e3aee0794115b78e7a0dd449d6212236b3fe?d=identicon)[iansltx](/maintainers/iansltx)

---

Top Contributors

[![iansltx](https://avatars.githubusercontent.com/u/472804?v=4)](https://github.com/iansltx "iansltx (39 commits)")[![harikt](https://avatars.githubusercontent.com/u/120454?v=4)](https://github.com/harikt "harikt (1 commits)")

---

Tags

business days

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/iansltx-business-days/health.svg)

```
[![Health](https://phpackages.com/badges/iansltx-business-days/health.svg)](https://phpackages.com/packages/iansltx-business-days)
```

###  Alternatives

[spatie/laravel-event-sourcing

The easiest way to get started with event sourcing in Laravel

9003.7M26](/packages/spatie-laravel-event-sourcing)[spatie/ssl-certificate

A class to easily query the properties of an ssl certificate

7414.8M35](/packages/spatie-ssl-certificate)[eventsauce/object-hydrator

Converts structured data into strict objects.

3322.3M20](/packages/eventsauce-object-hydrator)[tinymce/tinymce

Web based JavaScript HTML WYSIWYG editor control.

1697.5M106](/packages/tinymce-tinymce)[typo3/cms-redirects

TYPO3 CMS Redirects - Create manual redirects, list existing redirects and automatically createredirects on slug changes.

167.0M55](/packages/typo3-cms-redirects)[typo3/cms-recycler

TYPO3 CMS Recycler - Restore deleted records or remove them from the database permanently.

105.7M45](/packages/typo3-cms-recycler)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
