Skip to main content

Date calculations based on business calendars.

Project description

Business (Python)

circleci-badge pypi-badge

Date calculations based on business calendars. (Python 3.8+)

Python implementation of https://github.com/gocardless/business

Documentation

To get business, simply:

$ pip install business-python

Version 2.1.0 breaking changes

In version 2.1.0 we have dropped support for End-of-Life Python version 3.6 and 3.7. Last release supporting these versions is v2.0.3.

Version 2.0.0 breaking changes

In version 2.0.0 we have removed the bundled calendars. If you still need these they are available on v1.0.1.

Migration

  • Download/create calendars to a directory within your project eg: lib/calendars
  • Change your code to include the load_path for your calendars
  • Continue using .load("my_calendar") as usual
# lib/calendars contains yml files
Calendar.load_paths = ['lib/calendars']
calendar = Calendar.load("my_calendar")

Getting started

Get started with business by creating an instance of the calendar class, passing in a hash that specifies which days of the week are considered working days, and which days are holidays.

from business.calendar import Calendar

calendar = Calendar(
  working_days=["monday", "tuesday", "wednesday", "thursday", "friday"],
  # array items are either parseable date strings, or real datetime.date objects
  holidays=["January 1st, 2020", "April 10th, 2020"],
  extra_working_dates=[],
)

extra_working_dates key makes the calendar to consider a weekend day as a working day.

If working_days is missing, then common default is used (mon-fri). If holidays is missing, "no holidays" assumed. If extra_working_dates is missing, then no changes in working_days will happen.

Elements of holidays and extra_working_dates may be either strings that Calendar.parse_date() can understand, or YYYY-MM-DD (which is considered as a Date by Python YAML itself).

Calendar YAML file example

# lib/calendars/my_calendar.yml
working_days:
  - Monday
  - Sunday
holidays:
  - 2017-01-08 # Same as January 8th, 2017
extra_working_dates:
  - 2020-12-26 # Will consider 26 Dec 2020 (A Saturday), a working day

The load_cache method allows a thread safe way to avoid reloading the same calendar multiple times, and provides a performant way to dynamically load calendars for different requests.

Using business-python

Define your calendars in a folder eg: lib/calendars and set this directory on Calendar.load_paths=

Calendar.load_paths = ['lib/calendars']
calendar = Calendar.load_cache("my_calendar")

Input data types

The parse_date method is used to process the input date(s) in each method and return a datetime.date object.

Calendar.parse_date("2019-01-01")
# => datetime.date(2019, 1, 1)

Supported data types are:

  • datetime.date
  • datetime.datetime
  • pandas.Timestamp (treated as datetime.datetime)
  • date string parseable by dateutil.parser.parse

numpy.datetime64 is not supported, but can be converted to datetime.date:

numpy.datetime64('2014-06-01T23:00:05.453000000').astype('M8[D]').astype('O')
# =>  datetime.date(2014, 6, 1)

Checking for business days

To check whether a given date is a business day (falls on one of the specified working days or extra working dates, and is not a holiday), use the is_business_day method on Calendar.

calendar.is_business_day("Monday, 8 June 2020")
# => true
calendar.is_business_day("Sunday, 7 June 2020")
# => false

Business day arithmetic

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.

The add_business_days method is used to perform business day arithmetic on dates.

input_date = Calendar.parse_date("Thursday, 12 June 2014")
calendar.add_business_days(input_date, 4).strftime("%A, %d %B %Y")
# => "Wednesday, 18 June 2014"
calendar.add_business_days(input_date, -4).strftime("%A, %d %B %Y")
# => "Friday, 06 June 2014"

The 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.

input_date = Calendar.parse_date("Saturday, 14 June 2014")
calendar.roll_forward(input_date).strftime("%A, %d %B %Y")
# => "Monday, 16 June 2014"
calendar.roll_backward(input_date).strftime("%A, %d %B %Y")
# => "Friday, 13 June 2014"

In contrast, the next_business_day and previous_business_day methods will always move to a next or previous date until a business day is found, regardless if the input provided is a business day.

input_date = Calendar.parse_date("Monday, 9 June 2014")
calendar.roll_forward(input_date).strftime("%A, %d %B %Y")
# => "Monday, 09 June 2014"
calendar.next_business_day(input_date).strftime("%A, %d %B %Y")
# => "Tuesday, 10 June 2014"
calendar.previous_business_day(input_date).strftime("%A, %d %B %Y")
# => "Friday, 06 June 2014"

To 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.

from datetime import timedelta

input_date = Calendar.parse_date("Saturday, 14 June 2014")
calendar.business_days_between(input_date, input_date + timedelta(days=7))
# => 5

The get_business_day_of_month method return the running total of business days for a given date in that month. This method counts the number of business days from the start of the first day of the month to the given input date.

input_date = Calendar.parse_date("Thursday, 12 June 2014")
calendar.get_business_day_of_month(input_date)
# => 9

License & Contributing

GoCardless ♥ open source. If you do too, come join us.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

business_python-2.1.0.tar.gz (7.8 kB view details)

Uploaded Source

Built Distribution

business_python-2.1.0-py3-none-any.whl (8.2 kB view details)

Uploaded Python 3

File details

Details for the file business_python-2.1.0.tar.gz.

File metadata

  • Download URL: business_python-2.1.0.tar.gz
  • Upload date:
  • Size: 7.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.4.2 CPython/3.11.3 Darwin/22.5.0

File hashes

Hashes for business_python-2.1.0.tar.gz
Algorithm Hash digest
SHA256 0aea27ab44e11f9d8be7e5b530659c3b18651f01470288797151e10651898d8e
MD5 6873f68b75edc01d5f406ee85b7f172a
BLAKE2b-256 d96b367ce424f7c10aca63bc16bf303370b4ecb9539586a66c23eeac1c41f15a

See more details on using hashes here.

File details

Details for the file business_python-2.1.0-py3-none-any.whl.

File metadata

  • Download URL: business_python-2.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.4.2 CPython/3.11.3 Darwin/22.5.0

File hashes

Hashes for business_python-2.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 71facfdc48f3d4f9701b700846a283ee7e46f5062124ad09544866ab253df8ea
MD5 158c0308bd035d42c1c54564fcd96efa
BLAKE2b-256 c4159b1bda482237cb9b5e5b19b094f5524e6a28a1a8f906f47d5c27dc8f68f5

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page