{"id":13879483,"url":"https://github.com/gocardless/business","last_synced_at":"2025-05-15T06:02:17.336Z","repository":{"id":17821231,"uuid":"20720932","full_name":"gocardless/business","owner":"gocardless","description":"Ruby business day calculations","archived":false,"fork":false,"pushed_at":"2024-04-22T22:46:43.000Z","size":221,"stargazers_count":501,"open_issues_count":6,"forks_count":41,"subscribers_count":100,"default_branch":"master","last_synced_at":"2025-04-14T08:12:19.556Z","etag":null,"topics":["calendar","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/gocardless.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-06-11T10:13:24.000Z","updated_at":"2025-04-10T14:25:52.000Z","dependencies_parsed_at":"2024-01-13T20:57:19.195Z","dependency_job_id":"8662211a-9546-4b57-89ac-cf6c7d3b7e84","html_url":"https://github.com/gocardless/business","commit_stats":{"total_commits":171,"total_committers":34,"mean_commits":5.029411764705882,"dds":0.8596491228070176,"last_synced_commit":"f2f9dc15cf48f944456e2867f84cc0bad65013fe"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fbusiness","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fbusiness/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fbusiness/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fbusiness/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gocardless","download_url":"https://codeload.github.com/gocardless/business/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254283336,"owners_count":22045140,"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":["calendar","ruby"],"created_at":"2024-08-06T08:02:22.480Z","updated_at":"2025-05-15T06:02:17.211Z","avatar_url":"https://github.com/gocardless.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Business\n\n[![Gem version](https://badge.fury.io/rb/business.svg)](http://badge.fury.io/rb/business)\n[![CircleCI](https://circleci.com/gh/gocardless/business.svg?style=svg)](https://circleci.com/gh/gocardless/business)\n\nDate calculations based on business calendars.\n\n- [v2.0.0 breaking changes](#v200-breaking-changes)\n- [Getting Started](#getting-started)\n  - [Creating a calendar](#creating-a-calendar)\n  - [Using a calendar file](#use-a-calendar-file)\n- [Checking for business days](#checking-for-business-days)\n- [Business day arithmetic](#business-day-arithmetic)\n- [But other libraries already do this](#but-other-libraries-already-do-this)\n- [License \u0026 Contributing](#license--contributing)\n\n## v2.0.0 breaking changes\n\nWe have removed the bundled calendars as of version 2.0.0, if you need the calendars that were included:\n\n- Download the calendars you wish to use from [v1.18.0](https://github.com/gocardless/business/tree/b12c186ca6fd4ffdac85175742ff7e4d0a705ef4/lib/business/data)\n- Place them in a suitable directory in your project, typically `lib/calendars`\n-  Add this directory path to your instance of `Business::Calendar` using the `load_paths` method.dd the directory to where you placed the yml files before you load the calendar\n\n```ruby\nBusiness::Calendar.load_paths = [\"lib/calendars\"] # your_project/lib/calendars/ contains bacs.yml\nBusiness::Calendar.load(\"bacs\")\n```\n\nIf you wish to stay on the last version that contained bundled calendars, pin `business` to `v1.18.0`\n\n```ruby\n# Gemfile\ngem \"business\", \"v1.18.0\"\n```\n\n## Getting started\n\nTo install business, simply:\n\n```bash\ngem install business\n```\n\nIf you are using a Gemfile:\n\n```ruby\ngem \"business\", \"~\u003e 2.0\"\n```\n\n### Creating a calendar\n\nGet started with business by creating an instance of the calendar class, that accepts a hash that specifies which days of the week are considered working days, which days are holidays and which are extra working dates.\n\nAdditionally each calendar instance can be given a name. This can come in handy if you use multiple calendars.\n\n```ruby\ncalendar = Business::Calendar.new(\n  name: 'my calendar',\n  working_days: %w( mon tue wed thu fri ),\n  holidays: [\"01/01/2014\", \"03/01/2014\"],    # array items are either parseable date strings, or real Date objects\n  extra_working_dates: [nil], # Makes the calendar to consider a weekend day as a working day.\n)\n```\n\n### Use a calendar file\n\nDefining a calendar as a Ruby object may not be convenient, so we provide a way of defining these calendars as YAML. Below we will walk through the necessary [steps](#example-calendar) to build your first calendar. All keys are optional and will default to the following:\n\nNote: Elements of `holidays` and `extra_working_dates` may be either strings that `Date.parse()` [can understand](https://ruby-doc.org/stdlib-2.7.1/libdoc/date/rdoc/Date.html#method-c-parse), or `YYYY-MM-DD` (which is considered as a Date by Ruby YAML itself)[https://github.com/ruby/psych/blob/6ec6e475e8afcf7868b0407fc08014aed886ecf1/lib/psych/scalar_scanner.rb#L60].\n\n#### YAML file Structure\n\n```yml\nworking_days: # Optional, default [Monday-Friday]\n  -\nholidays: # Optional, default: []  ie: \"no holidays\" assumed\n  -\nextra_working_dates: # Optional, default: [], ie: no changes in `working_days` will happen\n  -\n```\n\n#### Example calendar\n\n```yaml\n# lib/calendars/my_calendar.yml\nworking_days:\n  - Monday\n  - Wednesday\n  - Friday\nholidays:\n  - 1st April 2020\n  - 2021-04-01\nextra_working_dates:\n  - 9th March 2020 # A Saturday\n```\n\nEnsure the calendar file is saved to a directory that will hold all your calendars, typically `lib/calendars`, then add this directory to your instance of `Business::Calendar` using the `load_paths` method before you call your calendar.\n\n`load_paths` also accepts an array of plain Ruby hashes with the format:\n\n```ruby\n  { \"calendar_name\" =\u003e { \"working_days\" =\u003e [] }\n```\n\n#### Example loading both a path and ruby hashes\n\n```ruby\nBusiness::Calendar.load_paths = [\n  \"lib/calendars\",\n  { \"foo_calendar\" =\u003e { \"working_days\" =\u003e [\"monday\"] } },\n  { \"bar_calendar\" =\u003e { \"working_days\" =\u003e [\"sunday\"] } },\n]\n```\n\nNow you can load the calendar by calling the `Business::Calendar.load(calendar_name)`. In order to avoid parsing the calendar file multiple times, there is a `Business::Calendar.load_cached(calendar_name)` method that caches the calendars by name after loading them.\n\n```ruby\ncalendar = Business::Calendar.load(\"my_calendar\") # lib/calendars/my_calendar.yml\ncalendar = Business::Calendar.load(\"foo_calendar\")\n# or\ncalendar = Business::Calendar.load_cached(\"my_calendar\")\ncalendar = Business::Calendar.load_cached(\"foo_calendar\")\n```\n\n## Checking for business days\n\nTo check whether a given date is a business day (falls on one of the specified working days or working dates, and is not a holiday), use the `business_day?` method on `Business::Calendar`.\n\n```ruby\ncalendar.business_day?(Date.parse(\"Monday, 9 June 2014\"))\n# =\u003e true\ncalendar.business_day?(Date.parse(\"Sunday, 8 June 2014\"))\n# =\u003e false\n```\n\nMore specifically you can check if a given `business_day?` is either a `working_day?` or a `holiday?` using methods on `Business::Calendar`.\n\n```ruby\n# Assuming \"Monday, 9 June 2014\" is a holiday\ncalendar.working_day?(Date.parse(\"Monday, 9 June 2014\"))\n# =\u003e true\ncalendar.holiday?(Date.parse(\"Monday, 9 June 2014\"))\n# =\u003e true\n# Monday is a working day, but we have a holiday so it's not\n# a business day\ncalendar.business_day?(Date.parse(\"Monday, 9 June 2014\"))\n# =\u003e false\n```\n\n## Business day arithmetic\n\nThe `add_business_days` and `subtract_business_days` are used to perform business day arithmetic on dates.\n\n```ruby\ndate = Date.parse(\"Thursday, 12 June 2014\")\ncalendar.add_business_days(date, 4).strftime(\"%A, %d %B %Y\")\n# =\u003e \"Wednesday, 18 June 2014\"\ncalendar.subtract_business_days(date, 4).strftime(\"%A, %d %B %Y\")\n# =\u003e \"Friday, 06 June 2014\"\n```\n\nThe `roll_forward` and `roll_backward` methods snap a date to a nearby business day. If provided with a business day, they will return that date. Otherwise, they will advance (forward for `roll_forward` and backward for `roll_backward`) until a business day is found.\n\n```ruby\ndate = Date.parse(\"Saturday, 14 June 2014\")\ncalendar.roll_forward(date).strftime(\"%A, %d %B %Y\")\n# =\u003e \"Monday, 16 June 2014\"\ncalendar.roll_backward(date).strftime(\"%A, %d %B %Y\")\n# =\u003e \"Friday, 13 June 2014\"\n```\n\nTo count the number of business days between two dates, pass the dates to `business_days_between`. This method counts from start of the first date to start of the second date. So, assuming no holidays, there would be two business days between a Monday and a Wednesday.\n\n```ruby\ndate = Date.parse(\"Saturday, 14 June 2014\")\ncalendar.business_days_between(date, date + 7)\n# =\u003e 5\n```\n\n## But other libraries already do this\n\nAnother gem, [business_time](https://github.com/bokmann/business_time), also exists for this purpose. We previously used business_time, but encountered several issues that prompted us to start business.\n\nFirstly, business_time works by monkey-patching `Date`, `Time`, and `FixNum`. While this enables syntax like `Time.now + 1.business_day`, it means that all configuration has to be global. GoCardless handles payments across several geographies, so being able to work with multiple working-day calendars is\nessential for us. Business provides a simple `Calendar` class, that is initialized with a configuration that specifies which days of the week are considered to be working days, and which dates are holidays.\n\nSecondly, business_time supports calculations on times as well as dates. For our purposes, date-based calculations are sufficient. Supporting time-based calculations as well makes the code significantly more complex. We chose to avoid this extra complexity by sticking solely to date-based mathematics.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"http://3.bp.blogspot.com/-aq4iOz2OZzs/Ty8xaQwMhtI/AAAAAAAABrM/-vn4tcRA9-4/s1600/daily-morning-awesomeness-243.jpeg\" alt=\"I'm late for business\" width=\"250\"/\u003e\u003c/p\u003e\n\n## License \u0026 Contributing\n- business is available as open source under the terms of the [MIT License](LICENSE).\n- Bug reports and pull requests are welcome on GitHub at https://github.com/gocardless/business.\n\nGoCardless ♥ open source. If you do too, come [join us](https://gocardless.com/about/jobs).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgocardless%2Fbusiness","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgocardless%2Fbusiness","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgocardless%2Fbusiness/lists"}