{"id":13828564,"url":"https://github.com/hughgrigg/php-business-time","last_synced_at":"2026-01-11T11:52:08.523Z","repository":{"id":53466897,"uuid":"126614310","full_name":"hughgrigg/php-business-time","owner":"hughgrigg","description":"Business time logic for PHP","archived":false,"fork":false,"pushed_at":"2023-03-18T11:53:24.000Z","size":258,"stargazers_count":31,"open_issues_count":1,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-01T12:48:08.300Z","etag":null,"topics":["business-hours","business-time","calendar","carbon","date-time","opening-hours","php","time"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hughgrigg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-03-24T15:44:12.000Z","updated_at":"2024-08-07T08:08:02.000Z","dependencies_parsed_at":"2023-12-16T12:05:26.044Z","dependency_job_id":"d6bc735c-0d0a-487c-b14e-8e744665d84c","html_url":"https://github.com/hughgrigg/php-business-time","commit_stats":{"total_commits":163,"total_committers":7,"mean_commits":"23.285714285714285","dds":"0.20858895705521474","last_synced_commit":"ee3baa63fbd9efbd96f5e434df738b7f61a5092a"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hughgrigg%2Fphp-business-time","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hughgrigg%2Fphp-business-time/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hughgrigg%2Fphp-business-time/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hughgrigg%2Fphp-business-time/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hughgrigg","download_url":"https://codeload.github.com/hughgrigg/php-business-time/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225492420,"owners_count":17482869,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["business-hours","business-time","calendar","carbon","date-time","opening-hours","php","time"],"created_at":"2024-08-04T09:02:52.413Z","updated_at":"2026-01-11T11:52:08.463Z","avatar_url":"https://github.com/hughgrigg.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"# Business Time in PHP\n\n[![Build Status](https://travis-ci.org/hughgrigg/php-business-time.svg?branch=master)](https://travis-ci.org/hughgrigg/php-business-time)\n[![Coverage Status](https://coveralls.io/repos/github/hughgrigg/php-business-time/badge.svg)](https://coveralls.io/github/hughgrigg/php-business-time)\n[![StyleCI](https://styleci.io/repos/126614310/shield?branch=master)](https://styleci.io/repos/126614310)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n\"Business time\" logic in PHP (aka \"business hours\", \"working days\" etc). This\ncan be useful for calculating shipping dates, for example.\n\nThis library provides an extension for the `Carbon` class in the\n[Carbon](http://carbon.nesbot.com/docs/) date time library.\n\nWhile Carbon already has methods like `diffInWeekendDays()`, this extension lets\nyou handle business time more precisely and flexibly. It can use your own\ncustomised times which can be specified directly or with constraint-matching.\n\n[Official music video for this library](https://www.youtube.com/watch?v=WGOohBytKTU)\n\n[BusinessTime in TypeScript](https://github.com/freetrade-io/ts-business-time \"Business time / market hours logic for TypeScript\")\n\n## Contents\n\n  * [Installation](#installation)\n  * [Usage](#usage)\n    + [Business days](#business-days)\n      - [Adding or subtracting business days](#adding-or-subtracting-business-days)\n      - [Diff in business days](#diff-in-business-days)\n      - [Whole vs partial business days](#whole-vs-partial-business-days)\n      - [Length of a business day](#length-of-a-business-day)\n    + [Business hours](#business-hours)\n  * [Describing business times](#describing-business-times)\n  * [Start and end of business day](#start-and-end-of-business-day)\n  * [Determining business time](#determining-business-time)\n    + [Business time constraints](#business-time-constraints)\n      - [Inversion of business time constraints](#inversion-of-business-time-constraints)\n      - [Exceptions to business time constraints](#exceptions-to-business-time-constraints)\n      - [Custom business time constraints](#custom-business-time-constraints)\n      - [Business time constraints example](#business-time-constraints-example)\n  * [Incorporating business time data from a remote source](#incorporating-business-time-data-from-a-remote-source)\n    + [Custom remote sources](#custom-remote-sources)\n  * [Recurring business deadlines](#recurring-business-deadlines)\n  * [Business time factory](#business-time-factory)\n  * [Precision](#precision)\n  * [Testing](#testing)\n\n## Installation\n\nInstall via Composer:\n\n```bash\ncomposer require hughgrigg/php-business-time\n```\n\n## Usage\n\nThe `BusinessTime` class in this package extends `Carbon`. This means that you\ncan use all of the methods from `Carbon` and the native `DateTime`, as well as\nthe ones described here.\n\n### Business days\n\nYou'll probably be dealing with business days most often.\n\n#### Adding or subtracting business days\n\nYou can add or subtract business days from a given starting date:\n\n```php\n$friday = new BusinessTime\\BusinessTime('Friday 10am');\n$nextBusinessDay = $friday-\u003eaddBusinessDay();\n// = Monday 10am\n$threeBusinessDays = $friday-\u003eaddBusinessDays(3);\n// = Wednesday 10am\n```\n\n```php\n$monday = new BusinessTime\\BusinessTime('Monday 10am');\n$previousBusinessDay = $now-\u003esubBusinessDay();\n// = Friday 10am\n$threeBusinessDaysAgo = $now-\u003esubBusinessDays(3);\n// = Wednesday 10am\n```\n\n#### Diff in business days\n\nBesides adding or subtracting business days, you can also calculate the number\nof business days between two given dates.\n\n```php\n$now = BusinessTime\\BusinessTime::now();\n$nextWeek = $now-\u003eaddWeek(); // a full 7-day week.\n$diff = $now-\u003ediffInBusinessDays($nextWeek);\n// = 5\n```\n\n#### Whole vs partial business days\n\nThe examples above deal with *whole* business days. You could also describe this\nas *integer days*. This means that any fractional part of a day is not\nconsidered to be a business day and is not counted.\n\nFor example, if we ask how many business days there are between 10am Friday and\n10am Saturday, the answer is zero:\n\n```php\n$fridayTenAm = new BusinessTime\\BusinessTime('Friday 10am');\n$saturdayTenAm = $fridayTenAm-\u003eaddDay(); // Add a full day.\n$fridayTenAm-\u003ediffInBusinessDays($saturdayTenAm);\n// = 0\n```\n\nThis may be surprising if you were expecting the business hours on Friday to be\nincluded. The reason the result is zero is because no *whole* business day has\npassed in that time; even most of a business day is not enough to be counted.\n\nIf you do want to consider partial days, you can use the equivalent partial\nmethods to get a float value.\n\n```php\n$fridayTenAm = new BusinessTime\\BusinessTime('Friday 10am');\n$fridayTenAm-\u003ediffInPartialBusinessDays('Saturday 10am');\n// = 0.875\n```\n\nThese are kept separate because usually people do not want to deal with the\nconcept of fractional business time: either a business day has passed or it has\nnot. The `partial` methods let you access the floating point number when you\nwant to.\n\n#### Length of a business day\n\nTo calculate a partial business day, we need to know the total length of time of\na business day. For example, 09:00 to 17:00 could be 100% of a business day if\nthose are the business hours, but only 80% of a business day if the hours are\n09:00 to 19:00.\n\nOut of the box, BusinessTime treats a business day as being 8 hours long (09:00\nto 17:00). You can adjust this to suit your needs, though.\n\nThe simplest way to configure this is to directly set the length of a business\nday:\n\n```php\n$businessTime = new BusinessTime\\BusinessTime();\n$businessTime-\u003esetLengthOfBusinessDay(BusinessTime\\Interval::hours(6));\n```\n\nIf you have complicated business time constraints (see below), it might be\nhelpful to let BusinessTime calculate the length of a business day for you. You\ncan do that by passing in a `DateTime` representing your standard business day\nto the `determineLengthOfBusinessDay()` method. BusinessTime will then calculate\nthe length of the business day based on that using its constraints.\n\n```php\n$businessTime = new BusinessTime\\BusinessTime();\n$businessTime-\u003edetermineLengthOfBusinessDay(new DateTime('Monday'));\n```\n\n### Business hours\n\nYou can also make business time calculations in hours:\n\n```php\n$now = new BusinessTime\\BusinessTime();\n$now-\u003eaddBusinessHour();\n$now-\u003eaddBusinessHours(3);\n```\n\n```php\n$now = new BusinessTime\\BusinessTime();\n$now-\u003ediffInBusinessHours();\n$now-\u003ediffInPartialBusinessHours();\n```\n\nThe reason a day is the largest unit included out-of-the-box is because people\nand organisations have different understandings of what is meant by larger units\nof time. Not having built-in methods for those prevents assumptions being made\nand forces explicitness, e.g. with `$now-\u003eaddBusinessDays(30)`.\n\nSimilarly, no unit smaller than an hour is included out-of-the-box because the\nconcept of a \"business minute\" is questionable for most use cases. You can\ncalculate minutes by multiplying by 60 if you do need them. Note that because\nthe default precision is one hour, you may need to adjust the precision to e.g\n15 minutes to get accurate calculations (see the note on precision and\nperformance).\n\n## Describing business times\n\nIn some situations it's useful to have meaningful descriptions for business and\nnon-business times. For example, you might want to tell your customer that you\nwon't deliver their order until next week because the weekend is in between.\n\nYou can use the `BusinessTimePeriod` class for this. You can make an instance\nwith start and end times like this:\n\n```php\n$start = new BusinessTime\\BusinessTime('today');\n$end = $start-\u003eaddBusinessDays(3);\n$timePeriod = new BusinessTime\\BusinessTimePeriod($start, $end);\n```\n\nYou can then use the `businessDays()` and `nonBusinessDays()` methods on the\ntime period to get that information. For example:\n\n```php\n$businessDays = $timePeriod-\u003ebusinessDays();\n$nonBusinessDays = $timePeriod-\u003enonBusinessDays();\n```\n\nThis returns an array of `BusinessTime` objects for each non-business day, which\ncan tell you their name:\n\n```php\n$nonBusinessDays[0]-\u003ebusinessName();\n// = e.g. \"the weekend\"\n```\n\nWhat intervals and descriptions you get depends on which business time\nconstraints have been used.\n\nYou can also ask a `BusinessTimePeriod` for its business and non-business sub-\nperiods, for example:\n\n```php\n$start = new BusinessTime\\BusinessTime('today');\n$end = new BusinessTime\\BusinessTime('tomorrow');\n$timePeriod = new BusinessTime\\BusinessTimePeriod($start, $end);\n\n$businessPeriods = $timePeriod-\u003ebusinessPeriods();\n// = array of BusinessTimePeriod instances for each period of business time.\n$nonBusinessPeriods = $timePeriod-\u003enonBusinessPeriods();\n// = array of BusinessTimePeriod instances for each period of non-business time.\n```\n\nThis lets you see the business timings that make up the whole time period. You\ncan ask each sub-period for its business-relevant name with the `businessName()`\nmethod.\n\n## Start and end of business day\n\nYou can get the start or end of the business day based on the business time\nconstraints like this:\n\n```php\n$businessTime = new BusinessTime\\BusinessTime();\n$businessTime-\u003estartOfBusinessDay();\n// = BusinessTime instance for e.g. 09:00\n$businessTime-\u003eendOfBusinessDay();\n// = BusinessTime instance for e.g. 17:00\n```\n\n## Determining business time\n\nBy default, this library considers Monday to Friday, 9am to 5pm to be business\ntime. You can configure this to suit your needs, though.\n\n### Business time constraints\n\nYou can set the constraints to determine business time on the `BusinessTime`\nclass like this:\n\n```php\n$businessTime = new BusinessTime\\BusinessTime();\n$businessTime-\u003esetConstraints(\n    new BusinessTime\\Constraint\\WeekDays(),\n    new BusinessTime\\Constraint\\BetweenHoursOfDay(9, 17),\n);\n```\n\nYou can pass as many constraints as you need; *all* of the constraints must be\nsatisfied for a given time to be considered business time.\n\nCalling `setBusinessTimeConstraints()` replaces any existing constraints on the\n`BusinessTime` instance.\n\nThe following constraints are available out-of-the-box, some of which can be\ncustomised via their constructors:\n\n```php\nnew BusinessTime\\Constraint\\HoursOfDay(10, 13, 17);\nnew BusinessTime\\Constraint\\BetweenHoursOfDay(9, 17);\nnew BusinessTime\\Constraint\\BetweenTimesOfDay('08:45', '17:30');\nnew BusinessTime\\Constraint\\WeekDays();\nnew BusinessTime\\Constraint\\Weekends();\nnew BusinessTime\\Constraint\\DaysOfWeek('Monday', 'Wednesday', 'Friday');\nnew BusinessTime\\Constraint\\BetweenDaysOfWeek('Monday', 'Friday');\nnew BusinessTime\\Constraint\\DaysOfMonth(1, 8, 23);\nnew BusinessTime\\Constraint\\BetweenDaysOfMonth(1, 20);\nnew BusinessTime\\Constraint\\MonthsOfYear('January', 'March', 'July');\nnew BusinessTime\\Constraint\\BetweenMonthsOfYear('January', 'November');\nnew BusinessTime\\Constraint\\DaysOfYear('January 8th', 'March 16th', 'July 4th');\nnew BusinessTime\\Constraint\\BetweenDaysOfYear('January 1st', 'December 5th');\nnew BusinessTime\\Constraint\\Dates('2019-01-17', '2019-09-23', '2020-05-11');\nnew BusinessTime\\Constraint\\BetweenDates('2018-01-11', '2018-12-31');\nnew BusinessTime\\Constraint\\AnyTime(); // Oh dear.\n```\n\n#### Inversion of business time constraints\n\nYou can wrap any business time constraint in a `Not` constraint to invert it.\n\nFor example:\n\n```php\n$decemberOff = new BusinessTime\\Constraint\\Composite\\Not(\n    BusinessTime\\Constraint\\MonthsOfYear('December')\n);\n```\n\nThis constraint now matches any time that is *not* in the month of December. You\ncan pass as many other constraints as you need into the `Not` constructor.\n\n#### Exceptions to business time constraints\n\nThe constraints above have an `except()` method that takes one or more other\nconstraints. This creates a composite constraint that lets you add exceptions to\nyour business time rules.\n\nFor example:\n\n```php\n$lunchTimeOff = (new BusinessTime\\Constraint\\BetweenHoursOfDay(9, 17))-\u003eexcept(\n    new BusinessTime\\Constraint\\HoursOfDay(13)\n);\n```\n\nThat constraint now matches any time between 9am and 5pm *except* for the hour\nbetween 1pm and 2pm. You can pass as many exceptional constraints as you need\ninto the `except()` method.\n\n*Note*: You can use the `except()` method on the `AnyTime` constraint as an\nalternative way to define your constraints:\n\n```php\n(new BusinessTime\\Constraint\\AnyTime())-\u003eexcept(\n    new BusinessTime\\Constraint\\DaysOfWeek('Friday')\n);\n// All times except Fridays are considered business time.\n```\n\nIf `except()` is not enough for your needs, you can also use the `andAlso()` and\n`orAlternatively()` methods to build different types of composite constraints.\n\n#### Custom business time constraints\n\nYou can implement your own custom constraints by implementing the\n`BusinessTime\\Constraint\\Constraint` interface:\n\n```php\ninterface BusinessTimeConstraint\n{\n    public function isBusinessTime(DateTimeInterface $time): bool;\n}\n```\n\nThe constraint must take an instance of `DateTimeInterface` and return whether\nor not it should be considered business time.\n\nIf you want to enable combinatorial logic for your custom constraint, use the\n`BusinessTime\\Constraint\\Composite\\Combinations` trait.\n\n*Tip*: It's usually better to use multiple simple constraints together than to\nmake one big, complex one.\n\n#### Business time constraints example\n\nHere's a somewhat complicated example of using business time constraints:\n\n```php\n$businessTime = new BusinessTime\\BusinessTime();\n$businessTime-\u003esetConstraints(\n    (new BusinessTime\\Constraint\\BetweenHoursOfDay(10, 18))-\u003eexcept(\n        new BusinessTime\\Constraint\\BetweenTimesOfDay('13:00', '14:00')\n    ), // 9-6 every day, with an hour for lunch.\n    (new BusinessTime\\Constraint\\WeekDays())-\u003eexcept(\n        new BusinessTime\\Constraint\\WeekDays('Thursday')\n    ), // Week days, but let's take Thursdays off.\n    new BusinessTime\\Constraint\\BetweenMonthsOfYear('January', 'November'),\n    // No-one does any work in December anyway.\n    new BusinessTime\\Constraint\\Composite\\Not(\n        new BusinessTime\\Constraint\\DaysOfYear('August 23rd', 'October 20th')\n    ) // Why not take off your birthday and wedding anniversary?\n);\n```\n\n## Incorporating business time data from a remote source\n\nWhilst you could try to set up constraints covering all the public holidays in\nyour country, it's probably easier to just retrieve them from a remote source.\n\n### Custom remote sources\n\nYou can add any other source you like by implementing the `Constraint` interface\ndescribed above.\n\n## Recurring business deadlines\n\nAs well as calculating business time, it's often useful to make calculations\nabout deadlines or \"cut-off\" times. For example, the cut-off time for\ndispatching orders might be 11am on week days. BusinessTime provides logic for\ndealing with this.\n\nYou can create deadlines using the same time constraints described above:\n\n```php\n$deadline = new BusinessTime\\Deadline\\RecurringDeadline(\n    new BusinessTime\\Constraint\\Weekdays(),\n    new BusinessTime\\Constraint\\HoursOfDay(11)\n);\n```\n\nAny time matching all the constraints is considered an occurrence of the\ndeadline. This means the deadline recurs on a regular basis (it is not a single\nmoment in time).\n\nTo find out when the deadline next occurs, you can use the\n`nextOccurrenceFrom()` method:\n\n```php\n$businessTime = new BusinessTime\\BusinessTime();\n$deadline-\u003enextOccurrenceFrom($businessTime);\n// = a new business time instance for the time the deadline next occurs.\n```\n\nIn this example, this might give you 11am today, or 11am next Monday if it's now\nalready later than 11am on a Friday.\n\nThere is a `previousOccurrenceFrom()` that does the equivalent going back from\nthe given time.\n\nYou can also see if a deadline has passed in a given time period:\n\n```php\n$deadline-\u003ehasPassedToday();\n// = true if the deadline has been passed today.\n$deadline-\u003ehasPassedBetween(\n    BusinessTime\\BusinessTime::now-\u003esubWeek(),\n    BusinessTime\\BusinessTime::now-\u003eaddWeek()\n);\n// = true if the deadline is ever passed in the given time period.\n```\n\n*Important*: the deadlines described above are designed to handle recurring\ndeadlines. They not appropriate for determining singular moments in time. To\nmake comparisons against a single moment, you should simply use the comparison\nmethods provided by Carbon:\n\n```php\n$time = new BusinessTime\\BusinessTime();\n$deadline = new BusinessTime\\BusinessTime('2018-12-08 17:00');\n$time-\u003egt($deadline);\n// = true if the moment has passed.\n```\n\n## Business time factory\n\nYou probably don't want to have to set up an instance of\n`BusinessTime\\BusinessTime` in every place you want to use one in your code.\n\nTo avoid that, you can set up a `BusinessTime\\Factory` with the constraints you\nneed once and then use that everywhere.\n\nFor example:\n\n```php\n$factory = new BusinessTime\\BusinessTimeFactory();\n$factory-\u003esetConstraints(\n    new BusinessTime\\Constraint\\DaysOfWeek('Saturday', 'Sunday'),\n    new BusinessTime\\Constraint\\Dates('2018-12-25'),\n);\n```\n\nOnce the factory is set up, you can share it in whatever way you usually share\ndependencies. For example, you might add it to the container in a framework like\nLaravel or Symfony.\n\nWhen you've got the instance of the factory, you can get a ready-made instance\nof `BusinessTime\\BusinessTime` from it:\n\n```php\n$date = $factory-\u003emake('2018-03-21');\n$now = $factory-\u003enow();\n```\n\nThe `BusinessTimeFactory` instance can be serialized, which makes it easy to\nstore in a cache or the filesystem.\n\n## Precision\n\nBy default, BusinessTime uses hour precision. This means that it calculates\nbusiness time roughly accurate to an hour.\n\nIf you need better precision than this, you can set it to what you want:\n\n```php\n$businessTime = new BusinessTime\\BusinessTime();\n$businessTime-\u003esetPrecision(BusinessTime\\Interval::minutes(30)); // Half-hour precision.\n$businessTime-\u003esetPrecision(BusinessTime\\Interval::minutes(15)); // Quarter-hour precision.\n```\n\nYou can also set precision on the business time factory in the same way.\n\nNote that the higher the precision, the lower the performance is. This is\nbecause BusinessTime must check each interval of the size you specify. For\nexample, at hour precision, dealing with one week requires `7 * 24 = 168`\niterations. At minute precision, this becomes `7 * 24 * 60 = 10080`\niterations, which is 60× slower.\n\nAlways try to set the largest precision interval that covers your needs.\n\n## Testing\n\nYou can use the testing facilities from Carbon described here:\n\nhttp://carbon.nesbot.com/docs/#api-testing\n\nFor example, you can mock the current time like this:\n\n```php\nCarbon::setTestNow($knownDate);\n```\n\nAnd proceed from there in your testing.\n\nTo run the tests for the BusinessTime package itself, you can run the tests in\nthis directory:\n\n```bash\nmake test\n```\n\nYou can also read the tests for more detailed usage examples of the library.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhughgrigg%2Fphp-business-time","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhughgrigg%2Fphp-business-time","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhughgrigg%2Fphp-business-time/lists"}