├── Actual.cal ├── MANIFEST ├── Test.cal ├── docs ├── requirements.txt ├── Makefile ├── make.bat └── source │ ├── api.rst │ ├── index.rst │ └── conf.py ├── .gitignore ├── test_calendar.py ├── .github └── workflows │ ├── sphinx.yml │ └── pytest.yml ├── setup.cfg ├── pyproject.toml ├── LICENSE.txt ├── README.md ├── CHANGELOG.md ├── test_bizdays_pandas.py ├── getdate.ipynb ├── B3.cal ├── quick.ipynb ├── index.md ├── calendars.ipynb ├── ANBIMA.txt ├── ANBIMA.cal ├── pandas.ipynb ├── SITE.ipynb └── bizdays.py /Actual.cal: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | bizdays.py 2 | test_bizdays.py 3 | setup.py 4 | 5 | -------------------------------------------------------------------------------- /Test.cal: -------------------------------------------------------------------------------- 1 | Saturday 2 | Sunday 3 | 2001-01-01 4 | 2002-01-01 5 | 2012-12-25 6 | 2013-01-01 -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | pandas 3 | lxml 4 | numpy 5 | matplotlib 6 | nbsphinx 7 | nbconvert 8 | alabaster -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build/* 3 | dist/* 4 | build 5 | dist 6 | .python-version 7 | .DS_Store 8 | .vscode/settings.json 9 | pytest.ini 10 | bizdays.egg-info/* 11 | bizdays.egg-info 12 | venv 13 | _templates 14 | _static 15 | _build 16 | .pytest_cache 17 | .vscode 18 | __pycache__ 19 | .ipynb_checkpoints/* 20 | docs/source/*.ipynb 21 | -------------------------------------------------------------------------------- /test_calendar.py: -------------------------------------------------------------------------------- 1 | from bizdays import Calendar 2 | import pytest 3 | 4 | 5 | def test_calendar_load(): 6 | cal = Calendar.load("B3") 7 | assert cal.name == "B3" 8 | cal = Calendar.load("ANBIMA") 9 | assert cal.name == "ANBIMA" 10 | 11 | 12 | def test_calendar_load_invalid(): 13 | with pytest.raises(Exception): 14 | cal = Calendar.load("B1") 15 | 16 | 17 | def test_calendar_load_pmc(): 18 | cal = Calendar.load("PMC/B3") 19 | assert cal.name == "PMC/B3" 20 | assert len(cal.holidays) > 4000 21 | -------------------------------------------------------------------------------- /.github/workflows/sphinx.yml: -------------------------------------------------------------------------------- 1 | name: Sphinx build 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Build HTML 11 | uses: wilsonfreitas/sphinx-action@master 12 | - name: Upload artifacts 13 | uses: actions/upload-artifact@v4 14 | with: 15 | name: html-docs 16 | path: docs/build/html/ 17 | - name: Deploy 18 | uses: peaceiris/actions-gh-pages@v4 19 | with: 20 | github_token: ${{ secrets.GITHUB_TOKEN }} 21 | publish_dir: docs/build/html -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | 2 | [metadata] 3 | name = bizdays 4 | version = 1.0.2 5 | author = Wilson Freitas 6 | author_email = wilsonfreitas@gmail.com 7 | description = Functions to handle business days calculations 8 | long_description = file: README.md 9 | long_description_content_type = text/markdown 10 | url = https://github.com/wilsonfreitas/python-bizdays 11 | project_urls = 12 | Bug Tracker = https://github.com/wilsonfreitas/python-bizdays/issues 13 | classifiers = 14 | Programming Language :: Python :: 3 15 | License :: OSI Approved :: MIT License 16 | Operating System :: OS Independent 17 | 18 | [options] 19 | py_modules = bizdays 20 | python_requires = >=3 21 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "bizdays" 3 | version = "1.0.16" 4 | description = "Functions to handle business days calculations" 5 | authors = ["wilsonfreitas "] 6 | readme = "README.md" 7 | include = ["ANBIMA.cal", "B3.cal", "Actual.cal"] 8 | 9 | [tool.poetry.dependencies] 10 | python = ">=3.9" 11 | pandas-market-calendars = "^4" 12 | pandas = "^2" 13 | 14 | [tool.poetry.group.dev.dependencies] 15 | pytest = "*" 16 | pycodestyle = "*" 17 | ipykernel = "*" 18 | 19 | [tool.poetry.group.docs.dependencies] 20 | Sphinx = "*" 21 | nbsphinx = "*" 22 | matplotlib = "*" 23 | lxml = "*" 24 | alabaster = "*" 25 | ipywidgets = "*" 26 | 27 | [build-system] 28 | requires = ["poetry-core"] 29 | build-backend = "poetry.core.masonry.api" 30 | -------------------------------------------------------------------------------- /.github/workflows/pytest.yml: -------------------------------------------------------------------------------- 1 | name: Test Python package with Poetry 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | pytest: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | python-version: ["3.8", "3.9", "3.10", "3.11"] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Install Poetry 25 | run: | 26 | pip install poetry 27 | - name: Install dependencies 28 | run: | 29 | poetry install 30 | - name: Run tests 31 | run: | 32 | poetry run pytest 33 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | API 2 | === 3 | 4 | .. module:: bizdays 5 | 6 | :class:`Calendar` 7 | ----------------- 8 | 9 | .. autoclass:: Calendar 10 | :members: bizdays, isbizday, offset, seq, getdate, getbizdays, following, preceding, modified_following, modified_preceding, load, diff 11 | :undoc-members: 12 | 13 | options 14 | ------- 15 | 16 | .. autofunction:: set_option 17 | 18 | Options: 19 | 20 | - `mode`: accepts `pandas` and `python` (default). 21 | Pandas mode enable integration with pandas, 22 | check out :doc:`pandas` for more information. 23 | - `mode.datetype`: specify the date type returned by 24 | Calendar's methods that return dates (`seq`, `following`, `preceding`, ...). 25 | Accepts `date` (default), `datetime` and `iso` for ISO formated strings. 26 | In pandas mode this option is ignored. 27 | 28 | .. code-block:: python 29 | 30 | from bizdays import set_option 31 | set_option('mode', 'pandas') 32 | 33 | .. autofunction:: get_option 34 | 35 | .. code-block:: python 36 | 37 | from bizdays import get_option 38 | get_option('mode') 39 | 40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2022 Wilson Freitas, 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software 6 | without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to 9 | whom the Software is furnished to do so, subject to the 10 | following conditions: 11 | 12 | The above copyright notice and this permission notice shall 13 | be included in all copies or substantial portions of the 14 | Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 17 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 18 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 19 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Downloads](https://img.shields.io/pypi/dm/bizdays.svg)](https://pypi.python.org/pypi/bizdays/) 2 | [![Latest Version](https://img.shields.io/pypi/v/bizdays.svg)](https://pypi.python.org/pypi/bizdays/) 3 | [![Supported Python versions](https://img.shields.io/pypi/pyversions/bizdays.svg)](https://pypi.python.org/pypi/bizdays/) 4 | 5 | # [python-bizdays](http://wilsonfreitas.github.io/python-bizdays/) 6 | 7 | In several countries and markets, the accountability of the price of a financial 8 | instrument, mainly bonds and derivatives, involves the use of different rules to 9 | compute the way the days go by. 10 | In Brazil, several financial instruments pay interest according to the business 11 | days along their life cycle. 12 | So, having a way to compute the number of business days between 2 dates is 13 | fairly useful to price financial instruments. 14 | **bizdays** was created to make it easier. 15 | 16 | **bizdays** computes business days between two dates based on the definition of 17 | nonworking days (usually holidays and weekends). 18 | It also computes other collateral effects like adjust dates for the next or 19 | previous business day, check whether a date is a business day, create sequences 20 | of business days, and much more. 21 | 22 | Several financial libraries compute the holidays, giving no option to users set 23 | it by their own. 24 | Furtherly, the financial calendar is usually a small feature of a huge library, 25 | as quantlib, for example, and some users, including myself, don't want to put a 26 | hand in such a huge library only to use the financial calendar. 27 | 28 | **bizdays** is a pure Python module without strong dependencies, 29 | what makes it appropriated for small projects. 30 | 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### version 1.0.16 (2025-01-05) 2 | 3 | * Updated B3.cal with 2025's holidays (thanks @victorhugow) 4 | 5 | ### version 1.0.15 (2024-06-23) 6 | 7 | * Organized dependencies (Issue #51) 8 | 9 | ### version 1.0.14 (2024-06-17) 10 | 11 | * Support to Python 3.12 (Issue #50) 12 | 13 | ### version 1.0.13 (2024-xx-xx) 14 | 15 | * [BUG] bizdays doesn't handle correctly non-business days in edges (Issue #47) 16 | 17 | ### version 1.0.12 (2024-01-25) 18 | 19 | * Tests updated 20 | * New workflow created for pytest (Github Actions) 21 | 22 | ### version 1.0.11 (2023-12-29) 23 | 24 | * ANBIMA calendar updated with new holiday nov-20th 25 | 26 | ### version 1.0.10 (2023-12-12) 27 | 28 | * B3 calendar updated with 2024 dates 29 | * B3 calendar removed date 2023-02-22 (Issue #39) 30 | 31 | ### version 1.0.9 (2023-10-29) 32 | 33 | * Actual Calendar created 34 | * Integration with [pandas_market_calendar](https://github.com/rsheftel/pandas_market_calendars), all calendars from this package can be used. 35 | 36 | ### version 1.0.8 (2023-01-20) 37 | 38 | * B3 Calendar updated: corrected dates 39 | 40 | ### version 1.0.7 (2023-01-18) 41 | 42 | * B3 Calendar updated: included 2023 holidays 43 | 44 | ### version 1.0.6 (2022-11-20) 45 | 46 | * B3 Calendar updated: 2020-01-25 wasn't a holiday. 47 | 48 | ### version 1.0.5 (2022-11-18) 49 | 50 | * B3 Calendar updated: 2020-07-09 wasn't a holiday due to decisions made by the municipal government of São Paulo. 51 | 52 | ### version 0.1.2 (2013-09-15) 53 | 54 | * Calendar's `offset` method implemented 55 | * Calendar's read-only attribute `name` implemented 56 | * Calendar's constructor receives startdate and enddate as optional parameters 57 | 58 | ### version 0.1.1 (2013-09-04) 59 | 60 | * dates passed to `bizdays`, `seq` and `currentdays` weren't being checked 61 | * updated docstrings 62 | 63 | ### version 0.1.0 (2013-09-01) 64 | 65 | * First commit -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to bizdays's documentation! 2 | =================================== 3 | 4 | In several countries and markets, the accountability of the price of a financial 5 | instrument, mainly bonds and derivatives, involves the use of different rules to 6 | compute the way the days go by. 7 | In Brazil, several financial instruments pay interest according to the business 8 | days along their life cycle. 9 | So, having a way to compute the number of business days between 2 dates is 10 | fairly useful to price financial instruments. 11 | **bizdays** was created to make it easier. 12 | 13 | **bizdays** computes business days between two dates based on the definition of 14 | nonworking days (usually holidays and weekends). 15 | It also computes other collateral effects like adjust dates for the next or 16 | previous business day, check whether a date is a business day, create sequences 17 | of business days, and much more. 18 | 19 | Several financial libraries compute the holidays, giving no option to users set 20 | it by their own. 21 | Furtherly, the financial calendar is usually a small feature of a huge library, 22 | as quantlib, for example, and some users, including myself, don't want to put a 23 | hand in such a huge library only to use the financial calendar. 24 | 25 | **bizdays** is a pure Python module without strong dependencies, 26 | what makes it appropriated for small projects. 27 | 28 | Install 29 | ------- 30 | 31 | **bizdays** is avalilable at PyPI, so it is pip instalable: 32 | 33 | .. code-block:: shell 34 | 35 | pip install bizdays 36 | 37 | 38 | Check out the :doc:`quick` section for further information. 39 | 40 | 41 | Contents 42 | -------- 43 | 44 | .. toctree:: 45 | :maxdepth: 2 46 | 47 | quick 48 | pandas 49 | calendars 50 | getdate 51 | api 52 | 53 | 54 | Indices and tables 55 | ================== 56 | 57 | * :ref:`genindex` 58 | * :ref:`modindex` 59 | 60 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | import shutil 16 | from datetime import datetime 17 | sys.path.insert(0, os.path.abspath(os.path.join('..', '..'))) 18 | shutil.copy('../../pandas.ipynb', '.') 19 | shutil.copy('../../quick.ipynb', '.') 20 | shutil.copy('../../calendars.ipynb', '.') 21 | shutil.copy('../../getdate.ipynb', '.') 22 | 23 | # -- Project information ----------------------------------------------------- 24 | 25 | project = 'bizdays' 26 | author = 'Wilson Freitas' 27 | year = datetime.now().year 28 | copyright = f'{year}, {author}' 29 | 30 | 31 | # The full version, including alpha/beta/rc tags 32 | release = '1.0.0' 33 | 34 | 35 | # -- General configuration --------------------------------------------------- 36 | 37 | # Add any Sphinx extension module names here, as strings. They can be 38 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 39 | # ones. 40 | extensions = [ 41 | 'sphinx.ext.duration', 42 | 'sphinx.ext.doctest', 43 | 'sphinx.ext.autodoc', 44 | 'sphinx.ext.autosummary', 45 | 'sphinx.ext.napoleon', 46 | 'nbsphinx', 47 | ] 48 | 49 | # Add any paths that contain templates here, relative to this directory. 50 | templates_path = ['_templates'] 51 | 52 | # List of patterns, relative to source directory, that match files and 53 | # directories to ignore when looking for source files. 54 | # This pattern also affects html_static_path and html_extra_path. 55 | exclude_patterns = [] 56 | 57 | 58 | # -- Options for HTML output ------------------------------------------------- 59 | 60 | # The theme to use for HTML and HTML Help pages. See the documentation for 61 | # a list of builtin themes. 62 | # 63 | html_theme = 'alabaster' 64 | html_theme_options = { 65 | 'description': 'Business Days Calculations and Utilities', 66 | 'github_user': 'wilsonfreitas', 67 | 'github_repo': 'python-bizdays', 68 | 'fixed_sidebar': True 69 | } 70 | 71 | # Add any paths that contain custom static files (such as style sheets) here, 72 | # relative to this directory. They are copied after the builtin static files, 73 | # so a file named "default.css" will overwrite the builtin "default.css". 74 | html_static_path = ['_static'] 75 | -------------------------------------------------------------------------------- /test_bizdays_pandas.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from bizdays import * 3 | import pandas as pd 4 | import numpy as np 5 | 6 | 7 | @pytest.fixture() 8 | def actual(): 9 | return Calendar(name="actual") 10 | 11 | @pytest.fixture(autouse=True) 12 | def setup_data(): 13 | set_option("mode", "pandas") 14 | 15 | 16 | def test_isbizday_with_timestamp_and_nat(actual): 17 | assert pd.isna(actual.isbizday(pd.NaT)) 18 | 19 | 20 | def test_isbizday_with_timestamp(actual): 21 | dt = pd.to_datetime("2021-12-30") 22 | assert actual.isbizday(dt) 23 | dt = pd.Timestamp("2021-12-30") 24 | assert actual.isbizday(dt) 25 | 26 | 27 | def test_bizdays_with_timestamp(actual): 28 | dt1 = pd.to_datetime("2021-12-30") 29 | dt2 = pd.to_datetime("2021-12-30") 30 | assert actual.bizdays(dt1, dt2) == 0 31 | dt1 = pd.to_datetime("2021-12-29") 32 | dt2 = pd.to_datetime("2021-12-30") 33 | assert actual.bizdays(dt1, dt2) == 1 34 | assert actual.bizdays(dt2, dt1) == -1 35 | 36 | 37 | def test_adjust_with_timestamp(actual): 38 | dt = pd.to_datetime("2021-12-30") 39 | assert isinstance(actual.following(dt), pd.Timestamp) 40 | assert actual.following(dt) == pd.Timestamp(dt.date()) 41 | assert actual.preceding(dt) == pd.Timestamp(dt.date()) 42 | assert actual.modified_following(dt) == pd.Timestamp(dt.date()) 43 | assert actual.modified_preceding(dt) == pd.Timestamp(dt.date()) 44 | 45 | 46 | def test_offset_with_timestamp(actual): 47 | dt = pd.to_datetime("2021-01-01") 48 | assert isinstance(actual.offset(dt, 5), pd.Timestamp) 49 | assert actual.offset(dt, 5) == pd.to_datetime("2021-01-06") 50 | 51 | 52 | def test_isbizday_with_datetimeindex(actual): 53 | dt = pd.to_datetime(["2021-12-30", "2021-11-30"]) 54 | x = actual.isbizday(dt) 55 | assert isinstance(x, np.ndarray) 56 | assert np.all(x) 57 | 58 | 59 | def test_isbizday_with_datetimeindex_and_nat(actual): 60 | dt = pd.to_datetime(["2021-12-30", "2021-11-30", None]) 61 | x = actual.isbizday(dt) 62 | assert isinstance(x, np.ndarray) 63 | assert x[0] 64 | assert pd.isna(x[2]) 65 | assert [pd.NaT] == [pd.NaT] 66 | 67 | 68 | def test_bizdays_with_datetimeindex(actual): 69 | dt1 = pd.to_datetime(["2021-12-30", "2021-11-30"]) 70 | dt2 = pd.to_datetime(["2021-12-30", "2021-11-30"]) 71 | x = actual.bizdays(dt1, dt2) 72 | assert isinstance(x, np.ndarray) 73 | assert np.all(x == np.array([0, 0])) 74 | 75 | 76 | def test_adjust_with_datetimeindex(actual): 77 | dt = pd.to_datetime(["2021-12-30", "2021-11-30"]) 78 | x = actual.following(dt) 79 | assert isinstance(x, pd.DatetimeIndex) 80 | assert all(actual.following(dt) == dt.date) 81 | assert all(actual.preceding(dt) == dt.date) 82 | assert all(actual.modified_following(dt) == dt.date) 83 | assert all(actual.modified_preceding(dt) == dt.date) 84 | 85 | 86 | def test_offset_with_datetimeindex(actual): 87 | dt = pd.to_datetime(["2021-01-01", "2021-01-02"]) 88 | dts = pd.to_datetime(["2021-01-06", "2021-01-07"]) 89 | x = actual.offset(dt, 5) 90 | assert isinstance(x, pd.DatetimeIndex) 91 | 92 | assert all(x == dts) 93 | dt = pd.to_datetime(["2021-01-01", "2021-01-02"]) 94 | dts = pd.to_datetime(["2021-01-06", "2021-01-08"]) 95 | assert all(actual.offset(dt, [5, 6]) == dts.date) 96 | dt = pd.to_datetime("2021-01-01") 97 | dts = pd.to_datetime(["2021-01-06", "2021-01-07"]) 98 | assert all(actual.offset(dt, [5, 6]) == dts.date) 99 | 100 | 101 | def test_offset_with_datetimeindex_and_nat(actual): 102 | dt = pd.to_datetime(["2021-01-01", "2021-01-02", pd.NaT]) 103 | x = actual.offset(dt, 5) 104 | assert isinstance(x, type(dt)) 105 | 106 | 107 | def test_seq_return_datetimeindex(actual): 108 | actual.seq("2014-01-02", "2014-01-07") 109 | -------------------------------------------------------------------------------- /getdate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# getdate\n", 8 | "\n", 9 | "The `getdate` method returns a date for a given expression, specifying the date by its position inside a time span which can be of a month or a year." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "from bizdays import Calendar\n", 19 | "cal = Calendar.load(filename='ANBIMA.cal')" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "The desired date is the 15th day of May 2002.\n", 27 | "\n", 28 | "The expression is `15th day`, year is 2002 and month is 5." 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "data": { 38 | "text/plain": [ 39 | "datetime.date(2002, 5, 15)" 40 | ] 41 | }, 42 | "execution_count": 2, 43 | "metadata": {}, 44 | "output_type": "execute_result" 45 | } 46 | ], 47 | "source": [ 48 | "cal.getdate('15th day', 2002, 5)" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "If the month is omited the reference is the year of 2002." 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 3, 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "data": { 65 | "text/plain": [ 66 | "datetime.date(2002, 1, 15)" 67 | ] 68 | }, 69 | "execution_count": 3, 70 | "metadata": {}, 71 | "output_type": "execute_result" 72 | } 73 | ], 74 | "source": [ 75 | "cal.getdate('15th day', 2002)" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "In the expression `15th day`:\n", 83 | "\n", 84 | "- `15th` is the position\n", 85 | "- `day` is the desired date that in this case is the current date.\n", 86 | "\n", 87 | "The expression accepts `first`, `second`, `third`, `1st`, `2nd`, `3rd`, `[n]th`, and `last` as valid positions and \n", 88 | "\n", 89 | "- `day` for regular days inside time span\n", 90 | "- `bizday` for business days inside time span\n", 91 | "- weekdays: `sun`, `mon`, `tue`, `wed`, `thu`, `fri`, `sat`\n", 92 | "\n", 93 | "Here it returns the last day and the last business day of 2006." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 4, 99 | "metadata": {}, 100 | "outputs": [ 101 | { 102 | "data": { 103 | "text/plain": [ 104 | "[datetime.date(2006, 12, 31), datetime.date(2006, 12, 29)]" 105 | ] 106 | }, 107 | "execution_count": 4, 108 | "metadata": {}, 109 | "output_type": "execute_result" 110 | } 111 | ], 112 | "source": [ 113 | "cal.getdate(['last day', 'last bizday'], 2006)" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "The first and last Monday of 2006." 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 5, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "data": { 130 | "text/plain": [ 131 | "[datetime.date(2006, 1, 2), datetime.date(2006, 12, 25)]" 132 | ] 133 | }, 134 | "execution_count": 5, 135 | "metadata": {}, 136 | "output_type": "execute_result" 137 | } 138 | ], 139 | "source": [ 140 | "cal.getdate(['first mon', 'last mon'], 2006)" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "#### Using date postions as a reference\n", 148 | "\n", 149 | "A day can be used as a reference, for example, the first Monday before the 30th day of July 2006." 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 6, 155 | "metadata": {}, 156 | "outputs": [ 157 | { 158 | "data": { 159 | "text/plain": [ 160 | "datetime.date(2006, 7, 24)" 161 | ] 162 | }, 163 | "execution_count": 6, 164 | "metadata": {}, 165 | "output_type": "execute_result" 166 | } 167 | ], 168 | "source": [ 169 | "cal.getdate('last mon before 30th day', 2006, 7)" 170 | ] 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "metadata": {}, 175 | "source": [ 176 | "Or the second business day after the 15th of 2006." 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 7, 182 | "metadata": {}, 183 | "outputs": [ 184 | { 185 | "data": { 186 | "text/plain": [ 187 | "datetime.date(2006, 1, 17)" 188 | ] 189 | }, 190 | "execution_count": 7, 191 | "metadata": {}, 192 | "output_type": "execute_result" 193 | } 194 | ], 195 | "source": [ 196 | "cal.getdate('second bizday after 15th day', 2006)" 197 | ] 198 | } 199 | ], 200 | "metadata": { 201 | "interpreter": { 202 | "hash": "4e6da9785688dca07ac236f8ec5f7e99c2c01a8eecf30ede8ddadf573b26e527" 203 | }, 204 | "kernelspec": { 205 | "display_name": "Python 3", 206 | "language": "python", 207 | "name": "python3" 208 | }, 209 | "language_info": { 210 | "codemirror_mode": { 211 | "name": "ipython", 212 | "version": 3 213 | }, 214 | "file_extension": ".py", 215 | "mimetype": "text/x-python", 216 | "name": "python", 217 | "nbconvert_exporter": "python", 218 | "pygments_lexer": "ipython3", 219 | "version": "3.7.6" 220 | } 221 | }, 222 | "nbformat": 4, 223 | "nbformat_minor": 2 224 | } 225 | -------------------------------------------------------------------------------- /B3.cal: -------------------------------------------------------------------------------- 1 | Saturday 2 | Sunday 3 | 2000-01-01 4 | 2000-01-25 5 | 2000-03-06 6 | 2000-03-07 7 | 2000-04-21 8 | 2000-04-23 9 | 2000-05-01 10 | 2000-06-22 11 | 2000-07-09 12 | 2000-09-07 13 | 2000-10-12 14 | 2000-11-02 15 | 2000-11-15 16 | 2000-12-24 17 | 2000-12-25 18 | 2000-12-29 19 | 2001-01-01 20 | 2001-01-25 21 | 2001-02-26 22 | 2001-02-27 23 | 2001-04-13 24 | 2001-04-21 25 | 2001-05-01 26 | 2001-06-14 27 | 2001-07-09 28 | 2001-09-07 29 | 2001-10-12 30 | 2001-11-02 31 | 2001-11-15 32 | 2001-12-24 33 | 2001-12-25 34 | 2001-12-31 35 | 2002-01-01 36 | 2002-01-25 37 | 2002-02-11 38 | 2002-02-12 39 | 2002-03-29 40 | 2002-04-21 41 | 2002-05-01 42 | 2002-05-30 43 | 2002-07-09 44 | 2002-09-07 45 | 2002-10-12 46 | 2002-11-02 47 | 2002-11-15 48 | 2002-12-24 49 | 2002-12-25 50 | 2002-12-31 51 | 2003-01-01 52 | 2003-01-25 53 | 2003-03-03 54 | 2003-03-04 55 | 2003-04-18 56 | 2003-04-21 57 | 2003-05-01 58 | 2003-06-19 59 | 2003-07-09 60 | 2003-09-07 61 | 2003-10-12 62 | 2003-11-02 63 | 2003-11-15 64 | 2003-12-24 65 | 2003-12-25 66 | 2003-12-31 67 | 2004-01-01 68 | 2004-01-25 69 | 2004-02-23 70 | 2004-02-24 71 | 2004-04-09 72 | 2004-04-21 73 | 2004-05-01 74 | 2004-06-10 75 | 2004-07-09 76 | 2004-09-07 77 | 2004-10-12 78 | 2004-11-02 79 | 2004-11-15 80 | 2004-12-24 81 | 2004-12-25 82 | 2004-12-31 83 | 2005-01-01 84 | 2005-01-25 85 | 2005-02-07 86 | 2005-02-08 87 | 2005-03-25 88 | 2005-04-21 89 | 2005-05-01 90 | 2005-05-26 91 | 2005-07-09 92 | 2005-09-07 93 | 2005-10-12 94 | 2005-11-02 95 | 2005-11-15 96 | 2005-12-24 97 | 2005-12-25 98 | 2005-12-30 99 | 2006-01-01 100 | 2006-01-25 101 | 2006-02-27 102 | 2006-02-28 103 | 2006-04-14 104 | 2006-04-21 105 | 2006-05-01 106 | 2006-06-15 107 | 2006-07-09 108 | 2006-09-07 109 | 2006-10-12 110 | 2006-11-02 111 | 2006-11-15 112 | 2006-11-20 113 | 2006-12-24 114 | 2006-12-25 115 | 2006-12-29 116 | 2007-01-01 117 | 2007-01-25 118 | 2007-02-19 119 | 2007-02-20 120 | 2007-04-06 121 | 2007-04-21 122 | 2007-05-01 123 | 2007-06-07 124 | 2007-07-09 125 | 2007-09-07 126 | 2007-10-12 127 | 2007-11-02 128 | 2007-11-15 129 | 2007-11-20 130 | 2007-12-24 131 | 2007-12-25 132 | 2007-12-31 133 | 2008-01-01 134 | 2008-01-25 135 | 2008-02-04 136 | 2008-02-05 137 | 2008-03-21 138 | 2008-04-21 139 | 2008-05-01 140 | 2008-05-22 141 | 2008-07-09 142 | 2008-09-07 143 | 2008-10-12 144 | 2008-11-02 145 | 2008-11-15 146 | 2008-11-20 147 | 2008-12-24 148 | 2008-12-25 149 | 2008-12-31 150 | 2009-01-01 151 | 2009-01-25 152 | 2009-02-23 153 | 2009-02-24 154 | 2009-04-10 155 | 2009-04-21 156 | 2009-05-01 157 | 2009-06-11 158 | 2009-07-09 159 | 2009-09-07 160 | 2009-10-12 161 | 2009-11-02 162 | 2009-11-15 163 | 2009-11-20 164 | 2009-12-24 165 | 2009-12-25 166 | 2009-12-31 167 | 2010-01-01 168 | 2010-01-25 169 | 2010-02-15 170 | 2010-02-16 171 | 2010-04-02 172 | 2010-04-21 173 | 2010-05-01 174 | 2010-06-03 175 | 2010-07-09 176 | 2010-09-07 177 | 2010-10-12 178 | 2010-11-02 179 | 2010-11-15 180 | 2010-11-20 181 | 2010-12-24 182 | 2010-12-25 183 | 2010-12-31 184 | 2011-01-01 185 | 2011-01-25 186 | 2011-03-07 187 | 2011-03-08 188 | 2011-04-21 189 | 2011-04-22 190 | 2011-05-01 191 | 2011-06-23 192 | 2011-07-09 193 | 2011-09-07 194 | 2011-10-12 195 | 2011-11-02 196 | 2011-11-15 197 | 2011-11-20 198 | 2011-12-24 199 | 2011-12-25 200 | 2011-12-30 201 | 2012-01-01 202 | 2012-01-25 203 | 2012-02-20 204 | 2012-02-21 205 | 2012-04-06 206 | 2012-04-21 207 | 2012-05-01 208 | 2012-06-07 209 | 2012-07-09 210 | 2012-09-07 211 | 2012-10-12 212 | 2012-11-02 213 | 2012-11-15 214 | 2012-11-20 215 | 2012-12-24 216 | 2012-12-25 217 | 2012-12-31 218 | 2013-01-01 219 | 2013-01-25 220 | 2013-02-11 221 | 2013-02-12 222 | 2013-03-29 223 | 2013-04-21 224 | 2013-05-01 225 | 2013-05-30 226 | 2013-07-09 227 | 2013-09-07 228 | 2013-10-12 229 | 2013-11-02 230 | 2013-11-15 231 | 2013-11-20 232 | 2013-12-24 233 | 2013-12-25 234 | 2013-12-31 235 | 2014-01-01 236 | 2014-01-25 237 | 2014-03-03 238 | 2014-03-04 239 | 2014-04-18 240 | 2014-04-21 241 | 2014-05-01 242 | 2014-06-12 243 | 2014-06-19 244 | 2014-07-09 245 | 2014-09-07 246 | 2014-10-12 247 | 2014-11-02 248 | 2014-11-15 249 | 2014-11-20 250 | 2014-12-24 251 | 2014-12-25 252 | 2014-12-31 253 | 2015-01-01 254 | 2015-01-25 255 | 2015-02-16 256 | 2015-02-17 257 | 2015-04-03 258 | 2015-04-21 259 | 2015-05-01 260 | 2015-06-04 261 | 2015-07-09 262 | 2015-09-07 263 | 2015-10-12 264 | 2015-11-02 265 | 2015-11-15 266 | 2015-11-20 267 | 2015-12-24 268 | 2015-12-25 269 | 2015-12-31 270 | 2016-01-01 271 | 2016-01-25 272 | 2016-02-08 273 | 2016-02-09 274 | 2016-03-25 275 | 2016-04-21 276 | 2016-05-01 277 | 2016-05-26 278 | 2016-07-09 279 | 2016-09-07 280 | 2016-10-12 281 | 2016-11-02 282 | 2016-11-15 283 | 2016-11-20 284 | 2016-12-24 285 | 2016-12-25 286 | 2016-12-30 287 | 2017-01-01 288 | 2017-01-25 289 | 2017-02-27 290 | 2017-02-28 291 | 2017-04-14 292 | 2017-04-21 293 | 2017-05-01 294 | 2017-06-15 295 | 2017-07-09 296 | 2017-09-07 297 | 2017-10-12 298 | 2017-11-02 299 | 2017-11-15 300 | 2017-11-20 301 | 2017-12-24 302 | 2017-12-25 303 | 2017-12-29 304 | 2018-01-01 305 | 2018-01-25 306 | 2018-02-12 307 | 2018-02-13 308 | 2018-03-30 309 | 2018-04-21 310 | 2018-05-01 311 | 2018-05-31 312 | 2018-07-09 313 | 2018-09-07 314 | 2018-10-12 315 | 2018-11-02 316 | 2018-11-15 317 | 2018-11-20 318 | 2018-12-24 319 | 2018-12-25 320 | 2018-12-31 321 | 2019-01-01 322 | 2019-01-25 323 | 2019-03-04 324 | 2019-03-05 325 | 2019-04-19 326 | 2019-04-21 327 | 2019-05-01 328 | 2019-06-20 329 | 2019-07-09 330 | 2019-09-07 331 | 2019-10-12 332 | 2019-11-02 333 | 2019-11-15 334 | 2019-11-20 335 | 2019-12-24 336 | 2019-12-25 337 | 2019-12-31 338 | 2020-01-01 339 | 2020-01-25 340 | 2020-02-24 341 | 2020-02-25 342 | 2020-04-10 343 | 2020-04-21 344 | 2020-05-01 345 | 2020-06-11 346 | 2020-09-07 347 | 2020-10-12 348 | 2020-11-02 349 | 2020-11-15 350 | 2020-12-24 351 | 2020-12-25 352 | 2020-12-31 353 | 2021-01-01 354 | 2021-01-25 355 | 2021-02-15 356 | 2021-02-16 357 | 2021-04-02 358 | 2021-04-21 359 | 2021-05-01 360 | 2021-06-03 361 | 2021-07-09 362 | 2021-09-07 363 | 2021-10-12 364 | 2021-11-02 365 | 2021-11-15 366 | 2021-11-20 367 | 2021-12-24 368 | 2021-12-25 369 | 2021-12-31 370 | 2022-01-01 371 | 2022-02-28 372 | 2022-03-01 373 | 2022-04-15 374 | 2022-04-21 375 | 2022-06-16 376 | 2022-09-07 377 | 2022-10-12 378 | 2022-11-02 379 | 2022-11-15 380 | 2022-12-30 381 | 2023-01-01 382 | 2023-02-20 383 | 2023-02-21 384 | 2023-04-07 385 | 2023-04-21 386 | 2023-05-01 387 | 2023-06-08 388 | 2023-09-07 389 | 2023-10-12 390 | 2023-11-02 391 | 2023-11-15 392 | 2023-12-25 393 | 2023-12-29 394 | 2024-01-01 395 | 2024-02-12 396 | 2024-02-13 397 | 2024-03-29 398 | 2024-05-01 399 | 2024-05-30 400 | 2024-11-15 401 | 2024-11-20 402 | 2024-12-24 403 | 2024-12-25 404 | 2024-12-31 405 | 2025-01-01 406 | 2025-03-03 407 | 2025-03-04 408 | 2025-04-18 409 | 2025-04-21 410 | 2025-05-01 411 | 2025-06-19 412 | 2025-11-20 413 | 2025-12-24 414 | 2025-12-25 415 | 2025-12-31 -------------------------------------------------------------------------------- /quick.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Quickstart\n", 8 | "\n", 9 | "Everything starts with one `Calendar` instance.\n", 10 | "\n", 11 | "The calendar can be loaded by its name.\n", 12 | "\n", 13 | "Here the `ANBIMA` calendar is loaded." 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 1, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "data": { 23 | "text/plain": [ 24 | "Calendar: ANBIMA\n", 25 | "Start: 2000-01-01\n", 26 | "End: 2078-12-25\n", 27 | "Weekdays: Saturday, Sunday\n", 28 | "Holidays: 948\n", 29 | "Financial: True" 30 | ] 31 | }, 32 | "execution_count": 1, 33 | "metadata": {}, 34 | "output_type": "execute_result" 35 | } 36 | ], 37 | "source": [ 38 | "from bizdays import Calendar\n", 39 | "cal = Calendar.load(filename='ANBIMA.cal')\n", 40 | "cal" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "Once a calendar is properly instanciated the methods can be directly called.\n", 48 | "\n", 49 | "Dates can be checked if they are business days or not with `isbizday`." 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 2, 55 | "metadata": {}, 56 | "outputs": [ 57 | { 58 | "data": { 59 | "text/plain": [ 60 | "False" 61 | ] 62 | }, 63 | "execution_count": 2, 64 | "metadata": {}, 65 | "output_type": "execute_result" 66 | } 67 | ], 68 | "source": [ 69 | "cal.isbizday('2014-01-12')" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 3, 75 | "metadata": {}, 76 | "outputs": [ 77 | { 78 | "data": { 79 | "text/plain": [ 80 | "True" 81 | ] 82 | }, 83 | "execution_count": 3, 84 | "metadata": {}, 85 | "output_type": "execute_result" 86 | } 87 | ], 88 | "source": [ 89 | "cal.isbizday('2014-01-13')" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "Note that the dates are passed as strings with dates ISO formated.\n", 97 | "\n", 98 | "This is to simplify the use of bizdays.\n", 99 | "\n", 100 | "The methods accepts the following date types:\n", 101 | "\n", 102 | "- string ISO formated YYYY-MM-DD\n", 103 | "- `datetime.datetime`\n", 104 | "- `datetime.date`\n", 105 | "- `pandas.Timestamp`" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "The amount of business days between two dates are obtained with the `bizdays` method." 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 4, 118 | "metadata": {}, 119 | "outputs": [ 120 | { 121 | "data": { 122 | "text/plain": [ 123 | "253" 124 | ] 125 | }, 126 | "execution_count": 4, 127 | "metadata": {}, 128 | "output_type": "execute_result" 129 | } 130 | ], 131 | "source": [ 132 | "cal.bizdays('2014-01-13', '2015-01-13')" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "`following` and `preceding` methods always return business days.\n", 140 | "\n", 141 | "The `following` method receives a date and if that date is a working date the same date is returned, otherwise, the next business date is returned.\n", 142 | "\n", 143 | "This is very handy with calculations involving bonds and the maturity date, for example, is not a business day, and in this situation the maturity is shifted to the following business day.\n", 144 | "This usually happens when the bond has a standardised maturity as January first, for example." 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 5, 150 | "metadata": {}, 151 | "outputs": [ 152 | { 153 | "data": { 154 | "text/plain": [ 155 | "datetime.date(2015, 12, 28)" 156 | ] 157 | }, 158 | "execution_count": 5, 159 | "metadata": {}, 160 | "output_type": "execute_result" 161 | } 162 | ], 163 | "source": [ 164 | "cal.following('2015-12-25')" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 6, 170 | "metadata": {}, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/plain": [ 175 | "datetime.date(2015, 12, 28)" 176 | ] 177 | }, 178 | "execution_count": 6, 179 | "metadata": {}, 180 | "output_type": "execute_result" 181 | } 182 | ], 183 | "source": [ 184 | "cal.following('2015-12-28')" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "The `preceding` method does the oposite, it returns the previous business date or the passed date." 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 7, 197 | "metadata": {}, 198 | "outputs": [ 199 | { 200 | "data": { 201 | "text/plain": [ 202 | "datetime.date(2013, 12, 31)" 203 | ] 204 | }, 205 | "execution_count": 7, 206 | "metadata": {}, 207 | "output_type": "execute_result" 208 | } 209 | ], 210 | "source": [ 211 | "cal.preceding('2014-01-01')" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": 8, 217 | "metadata": {}, 218 | "outputs": [ 219 | { 220 | "data": { 221 | "text/plain": [ 222 | "datetime.date(2014, 1, 2)" 223 | ] 224 | }, 225 | "execution_count": 8, 226 | "metadata": {}, 227 | "output_type": "execute_result" 228 | } 229 | ], 230 | "source": [ 231 | "cal.preceding('2014-01-02')" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": {}, 237 | "source": [ 238 | "A sequence of business dates can be easily obtained with the `seq` method." 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": 9, 244 | "metadata": {}, 245 | "outputs": [ 246 | { 247 | "data": { 248 | "text/plain": [ 249 | "[datetime.date(2014, 1, 2),\n", 250 | " datetime.date(2014, 1, 3),\n", 251 | " datetime.date(2014, 1, 6),\n", 252 | " datetime.date(2014, 1, 7)]" 253 | ] 254 | }, 255 | "execution_count": 9, 256 | "metadata": {}, 257 | "output_type": "execute_result" 258 | } 259 | ], 260 | "source": [ 261 | "cal.seq('2014-01-02', '2014-01-07')" 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "metadata": {}, 267 | "source": [ 268 | "Sometimes it is interesting to shift a date for a fixed amount of business days.\n", 269 | "\n", 270 | "The `offset` methods does that." 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 10, 276 | "metadata": {}, 277 | "outputs": [ 278 | { 279 | "data": { 280 | "text/plain": [ 281 | "datetime.date(2014, 1, 9)" 282 | ] 283 | }, 284 | "execution_count": 10, 285 | "metadata": {}, 286 | "output_type": "execute_result" 287 | } 288 | ], 289 | "source": [ 290 | "cal.offset('2014-01-02', 5)" 291 | ] 292 | }, 293 | { 294 | "cell_type": "markdown", 295 | "metadata": {}, 296 | "source": [ 297 | "The `getdate` method uses the calendar to get general dates.\n", 298 | "\n", 299 | "For example, to get the 15th day of May 2020, just use:" 300 | ] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": 11, 305 | "metadata": {}, 306 | "outputs": [ 307 | { 308 | "data": { 309 | "text/plain": [ 310 | "datetime.date(2002, 5, 15)" 311 | ] 312 | }, 313 | "execution_count": 11, 314 | "metadata": {}, 315 | "output_type": "execute_result" 316 | } 317 | ], 318 | "source": [ 319 | "cal.getdate('15th day', 2002, 5)" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": {}, 325 | "source": [ 326 | "If instead the desired date is the 15th business day of the same month, try:" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": 12, 332 | "metadata": {}, 333 | "outputs": [ 334 | { 335 | "data": { 336 | "text/plain": [ 337 | "datetime.date(2002, 5, 22)" 338 | ] 339 | }, 340 | "execution_count": 12, 341 | "metadata": {}, 342 | "output_type": "execute_result" 343 | } 344 | ], 345 | "source": [ 346 | "cal.getdate('15th bizday', 2002, 5)" 347 | ] 348 | }, 349 | { 350 | "cell_type": "markdown", 351 | "metadata": {}, 352 | "source": [ 353 | "It supports many other features like weekdays and some positional references." 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 13, 359 | "metadata": {}, 360 | "outputs": [ 361 | { 362 | "data": { 363 | "text/plain": [ 364 | "datetime.date(2002, 5, 29)" 365 | ] 366 | }, 367 | "execution_count": 13, 368 | "metadata": {}, 369 | "output_type": "execute_result" 370 | } 371 | ], 372 | "source": [ 373 | "cal.getdate('last wed', 2002, 5)" 374 | ] 375 | }, 376 | { 377 | "cell_type": "code", 378 | "execution_count": 14, 379 | "metadata": {}, 380 | "outputs": [ 381 | { 382 | "data": { 383 | "text/plain": [ 384 | "datetime.date(2002, 5, 24)" 385 | ] 386 | }, 387 | "execution_count": 14, 388 | "metadata": {}, 389 | "output_type": "execute_result" 390 | } 391 | ], 392 | "source": [ 393 | "cal.getdate('first fri before last day ', 2002, 5)" 394 | ] 395 | }, 396 | { 397 | "cell_type": "markdown", 398 | "metadata": {}, 399 | "source": [ 400 | "If the desired information is the amount of business days for a specific year or month, the method `getbizdays` is recommended." 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": 15, 406 | "metadata": {}, 407 | "outputs": [ 408 | { 409 | "data": { 410 | "text/plain": [ 411 | "22" 412 | ] 413 | }, 414 | "execution_count": 15, 415 | "metadata": {}, 416 | "output_type": "execute_result" 417 | } 418 | ], 419 | "source": [ 420 | "cal.getbizdays(2001, 5)" 421 | ] 422 | }, 423 | { 424 | "cell_type": "markdown", 425 | "metadata": {}, 426 | "source": [ 427 | "For a sequence of dates, the same way `diff` takes the difference between elements, `Calendar.diff` takes the difference in business days between given dates." 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": 16, 433 | "metadata": {}, 434 | "outputs": [ 435 | { 436 | "data": { 437 | "text/plain": [ 438 | "[2, 3]" 439 | ] 440 | }, 441 | "execution_count": 16, 442 | "metadata": {}, 443 | "output_type": "execute_result" 444 | } 445 | ], 446 | "source": [ 447 | "dates = ('2017-05-10', '2017-05-12', '2017-05-17')\n", 448 | "cal.diff(dates)" 449 | ] 450 | } 451 | ], 452 | "metadata": { 453 | "interpreter": { 454 | "hash": "4e6da9785688dca07ac236f8ec5f7e99c2c01a8eecf30ede8ddadf573b26e527" 455 | }, 456 | "kernelspec": { 457 | "display_name": "Python 3", 458 | "language": "python", 459 | "name": "python3" 460 | }, 461 | "language_info": { 462 | "codemirror_mode": { 463 | "name": "ipython", 464 | "version": 3 465 | }, 466 | "file_extension": ".py", 467 | "mimetype": "text/x-python", 468 | "name": "python", 469 | "nbconvert_exporter": "python", 470 | "pygments_lexer": "ipython3", 471 | "version": "3.7.6" 472 | } 473 | }, 474 | "nbformat": 4, 475 | "nbformat_minor": 2 476 | } 477 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | 2 | **bizdays** computes business days between two dates based on the definition of nonworking days (usually holidays and weekends—nonworking weekdays). 3 | It also computes other collateral effects like adjust dates for the next or previous business day, check whether a date is a business day, create generators of business days sequences, and so forth. 4 | 5 | ## Install 6 | 7 | **bizdays** is avalilable at PyPI, so it is pip instalable. 8 | 9 | pip install bizdays 10 | 11 | ## Using 12 | 13 | Business days calculations are done defining a `Calendar` object. 14 | 15 | 16 | ```python 17 | from bizdays import Calendar 18 | cal = Calendar.load('ANBIMA') 19 | cal 20 | ``` 21 | 22 | 23 | 24 | 25 | Calendar: ANBIMA 26 | Start: 2000-01-01 27 | End: 2078-12-25 28 | Holidays: 948 29 | Financial: True 30 | 31 | 32 | 33 | where `holidays` is a sequence of dates which represents nonworking dates and the second argument, `weekdays`, is a sequence with nonworking weekdays. 34 | `holidays` must be a sequence of strings with ISO formatted dates or `datetime.date` objects and `weekdays` a sequence of weekdays in words. 35 | 36 | Once you have a `Calendar` you can 37 | 38 | 39 | ```python 40 | cal.isbizday('2014-01-12') 41 | ``` 42 | 43 | 44 | 45 | 46 | False 47 | 48 | 49 | 50 | 51 | ```python 52 | cal.isbizday('2014-01-13') 53 | ``` 54 | 55 | 56 | 57 | 58 | True 59 | 60 | 61 | 62 | 63 | ```python 64 | cal.bizdays('2014-01-13', '2015-01-13') 65 | ``` 66 | 67 | 68 | 69 | 70 | 253 71 | 72 | 73 | 74 | 75 | ```python 76 | cal.following('2015-12-25') 77 | ``` 78 | 79 | 80 | 81 | 82 | datetime.date(2015, 12, 28) 83 | 84 | 85 | 86 | 87 | ```python 88 | cal.following('2015-12-28') 89 | ``` 90 | 91 | 92 | 93 | 94 | datetime.date(2015, 12, 28) 95 | 96 | 97 | 98 | 99 | ```python 100 | cal.preceding('2014-01-01') 101 | ``` 102 | 103 | 104 | 105 | 106 | datetime.date(2013, 12, 31) 107 | 108 | 109 | 110 | 111 | ```python 112 | cal.preceding('2014-01-02') 113 | ``` 114 | 115 | 116 | 117 | 118 | datetime.date(2014, 1, 2) 119 | 120 | 121 | 122 | 123 | ```python 124 | cal.seq('2014-01-02', '2014-01-07') 125 | ``` 126 | 127 | 128 | 129 | 130 | [datetime.date(2014, 1, 2), 131 | datetime.date(2014, 1, 3), 132 | datetime.date(2014, 1, 6), 133 | datetime.date(2014, 1, 7)] 134 | 135 | 136 | 137 | 138 | ```python 139 | cal.offset('2014-01-02', 5) 140 | ``` 141 | 142 | 143 | 144 | 145 | datetime.date(2014, 1, 9) 146 | 147 | 148 | 149 | 150 | ```python 151 | cal.getdate('15th day', 2002, 5) 152 | ``` 153 | 154 | 155 | 156 | 157 | datetime.date(2002, 5, 15) 158 | 159 | 160 | 161 | 162 | ```python 163 | cal.getdate('15th bizday', 2002, 5) 164 | ``` 165 | 166 | 167 | 168 | 169 | datetime.date(2002, 5, 22) 170 | 171 | 172 | 173 | 174 | ```python 175 | cal.getdate('last wed', 2002, 5) 176 | ``` 177 | 178 | 179 | 180 | 181 | datetime.date(2002, 5, 29) 182 | 183 | 184 | 185 | 186 | ```python 187 | cal.getdate('first fri before last day ', 2002, 5) 188 | ``` 189 | 190 | 191 | 192 | 193 | datetime.date(2002, 5, 24) 194 | 195 | 196 | 197 | 198 | ```python 199 | cal.getbizdays(2001, 5) 200 | ``` 201 | 202 | 203 | 204 | 205 | 22 206 | 207 | 208 | 209 | In this example I used the list of holidays released by [ANBIMA](http://www.anbima.com.br/feriados/feriados.asp). 210 | 211 | > **Important note on date arguments and returning dates** 212 | > 213 | > As you can see in the examples all date arguments are strings ISO formatted (`YYYY-mm-dd` or `%Y-%m-%d`), but they can also be passed as `datetime.date` objects. 214 | > All returning dates are `datetime.date` objects (or a sequence of it), unless you set `iso=True`, that will return an ISO formatted string. 215 | 216 | > The `startdate` and `enddate` of a `Calendar` are defined accordingly the first and last given holidays. 217 | 218 | ### bizdays 219 | 220 | To compute the business days between two dates you call `bizdays` passing `from` and `to` dates as arguments. 221 | 222 | 223 | 224 | ```python 225 | cal.bizdays('2012-12-31', '2013-01-03') 226 | ``` 227 | 228 | 229 | 230 | 231 | 2 232 | 233 | 234 | 235 | ### getdate 236 | 237 | You specify dates by its position or related to other dates, for example: 238 | 239 | 240 | 241 | ```python 242 | cal.getdate('15th day', 2002, 5) 243 | ``` 244 | 245 | 246 | 247 | 248 | datetime.date(2002, 5, 15) 249 | 250 | 251 | 252 | it returns the 15th day of 2002 may. You can also reffer to the whole year. 253 | 254 | 255 | ```python 256 | cal.getdate('150th day', 2002) 257 | ``` 258 | 259 | 260 | 261 | 262 | datetime.date(2002, 5, 30) 263 | 264 | 265 | 266 | It accepts `day`, `bizday` and weekdays by: `sun`, `mon`, `tue`, `wed`, `thu`, `fri`, and `sat`. 267 | 268 | 269 | ```python 270 | cal.getdate('last day', 2006) 271 | ``` 272 | 273 | 274 | 275 | 276 | datetime.date(2006, 12, 31) 277 | 278 | 279 | 280 | 281 | ```python 282 | cal.getdate('last bizday', 2006) 283 | ``` 284 | 285 | 286 | 287 | 288 | datetime.date(2006, 12, 29) 289 | 290 | 291 | 292 | 293 | ```python 294 | cal.getdate('last mon', 2006) 295 | ``` 296 | 297 | 298 | 299 | 300 | datetime.date(2006, 12, 25) 301 | 302 | 303 | 304 | For postion use: `first`, `second`, `third`, `1st`, `2nd`, `3rd`, `[n]th`, and `last`. 305 | 306 | #### Using date postions as a reference 307 | 308 | You can find before and after other date positions (using date positions as a reference). 309 | 310 | 311 | 312 | ```python 313 | cal.getdate('last mon before 30th day', 2006, 7) 314 | ``` 315 | 316 | 317 | 318 | 319 | datetime.date(2006, 7, 24) 320 | 321 | 322 | 323 | 324 | ```python 325 | cal.getdate('second bizday after 15th day', 2006) 326 | ``` 327 | 328 | 329 | 330 | 331 | datetime.date(2006, 1, 17) 332 | 333 | 334 | 335 | ### Business days for years or months 336 | 337 | You get the number of business days for a year or a month calling `getbizdays`. 338 | 339 | 340 | ```python 341 | cal.getbizdays(2021) 342 | ``` 343 | 344 | 345 | 346 | 347 | 251 348 | 349 | 350 | 351 | 352 | ```python 353 | cal.getbizdays(2021, 12) 354 | ``` 355 | 356 | 357 | 358 | 359 | 23 360 | 361 | 362 | 363 | #### following and preceding 364 | 365 | Several contracts, by default, always expiry in the same day, for example, 1st Januray, which isn't a business day, so instead of carrying your code with awful checks you could call `following` which returns the given date 366 | whether it is a business day or the next business day. 367 | 368 | 369 | ```python 370 | cal.following('2013-01-01') 371 | ``` 372 | 373 | 374 | 375 | 376 | datetime.date(2013, 1, 2) 377 | 378 | 379 | 380 | 381 | ```python 382 | cal.following('2013-01-02') 383 | ``` 384 | 385 | 386 | 387 | 388 | datetime.date(2013, 1, 2) 389 | 390 | 391 | 392 | We also have `preceding`, although I suppose it is unusual, too. 393 | 394 | 395 | 396 | ```python 397 | cal.preceding('2013-01-01') 398 | ``` 399 | 400 | 401 | 402 | 403 | datetime.date(2012, 12, 31) 404 | 405 | 406 | 407 | 408 | #### modified_following and modified_preceding 409 | 410 | `modified_following` and `modified_preceding` are common functions used to specify maturity of contracts. 411 | They work the same way `following` and `preceding` but once the returning date is a different month it is adjusted to the `following` or `preceding` business day in the same month. 412 | 413 | 414 | 415 | ```python 416 | dt = cal.getdate('last day', 2002, 3) 417 | dt 418 | ``` 419 | 420 | 421 | 422 | 423 | datetime.date(2002, 3, 31) 424 | 425 | 426 | 427 | 428 | ```python 429 | cal.modified_following(dt) 430 | ``` 431 | 432 | 433 | 434 | 435 | datetime.date(2002, 3, 28) 436 | 437 | 438 | 439 | 440 | ```python 441 | cal.isbizday('2002-03-29') 442 | ``` 443 | 444 | 445 | 446 | 447 | False 448 | 449 | 450 | 451 | 452 | ```python 453 | dt = cal.getdate('first day', 2002, 6) 454 | dt 455 | ``` 456 | 457 | 458 | 459 | 460 | datetime.date(2002, 6, 1) 461 | 462 | 463 | 464 | 465 | ```python 466 | cal.modified_preceding(dt) 467 | ``` 468 | 469 | 470 | 471 | 472 | datetime.date(2002, 6, 3) 473 | 474 | 475 | 476 | ### seq 477 | 478 | To execute calculations through sequential dates, sometimes you must consider only business days. 479 | For example, you want to compute the price of a bond from its issue date up to its maturity. 480 | You have to walk over business days in order to carry the contract up to maturity. 481 | To accomplish that you use the `seq` method (stolen from R) which returns a sequence generator of business days. 482 | 483 | 484 | ```python 485 | cal.seq('2012-12-31', '2013-01-03') 486 | ``` 487 | 488 | 489 | 490 | 491 | [datetime.date(2012, 12, 31), 492 | datetime.date(2013, 1, 2), 493 | datetime.date(2013, 1, 3)] 494 | 495 | 496 | 497 | ### offset 498 | 499 | This method offsets the given date by `n` days respecting the calendar, so it obligatorily returns a business day. 500 | 501 | 502 | ```python 503 | cal.offset('2013-01-02', 1) 504 | ``` 505 | 506 | 507 | 508 | 509 | datetime.date(2013, 1, 3) 510 | 511 | 512 | 513 | 514 | ```python 515 | cal.offset('2013-01-02', 3) 516 | ``` 517 | 518 | 519 | 520 | 521 | datetime.date(2013, 1, 7) 522 | 523 | 524 | 525 | 526 | ```python 527 | cal.offset('2013-01-02', 0) 528 | ``` 529 | 530 | 531 | 532 | 533 | datetime.date(2013, 1, 2) 534 | 535 | 536 | 537 | Obviously, if you want to offset backwards you can use `-n`. 538 | 539 | 540 | ```python 541 | cal.offset('2013-01-02', -1) 542 | ``` 543 | 544 | 545 | 546 | 547 | datetime.date(2012, 12, 31) 548 | 549 | 550 | 551 | 552 | ```python 553 | cal.offset('2013-01-02', -3) 554 | ``` 555 | 556 | 557 | 558 | 559 | datetime.date(2012, 12, 27) 560 | 561 | 562 | 563 | Once the given date is a business day there is no problems, but if instead it isn't a working day the offset can lead to unexpected results. For example: 564 | 565 | 566 | 567 | ```python 568 | cal.offset('2013-01-01', 1) 569 | ``` 570 | 571 | 572 | 573 | 574 | datetime.date(2013, 1, 2) 575 | 576 | 577 | 578 | 579 | ```python 580 | cal.offset('2013-01-01', 0) 581 | ``` 582 | 583 | 584 | 585 | 586 | datetime.date(2013, 1, 1) 587 | 588 | 589 | 590 | 591 | ```python 592 | cal.offset('2013-01-01', -1) 593 | ``` 594 | 595 | 596 | 597 | 598 | datetime.date(2012, 12, 31) 599 | 600 | 601 | 602 | ## Actual Calendar 603 | 604 | The Actual Calendar can be defined as 605 | 606 | 607 | 608 | ```python 609 | cal = Calendar(name='actual') 610 | cal 611 | ``` 612 | 613 | 614 | 615 | 616 | Calendar: actual 617 | Start: 1970-01-01 618 | End: 2071-01-01 619 | Holidays: 0 620 | Financial: True 621 | 622 | 623 | 624 | The Actual Calendar doesn't consider holidays, nor nonworking weekdays for counting business days between 2 dates. 625 | This is the same of subtracting 2 dates, and adjust methods will return the given argument. 626 | But the idea of using the Actual Calendar is working with the same interface for any calendar you work with. 627 | When you price financial instruments you don't have to check if it uses business days or not. 628 | 629 | > `startdate` and `enddate` defaults to `1970-01-01` and `2071-01-01`, but they can be set during Calendar's instanciation. 630 | 631 | ## Vectorized operations 632 | 633 | The Calendar's methods: `isbizday`, `bizdays`, `adjust_previous`, `adjust_next`, and `offset`, have a vectorized counterparty, inside `Calendar.vec` attribute. 634 | 635 | 636 | 637 | 638 | ```python 639 | cal = Calendar.load('ANBIMA') 640 | dates = ('2002-01-01', '2002-01-02', '2002-01-03') 641 | cal.following(dates) 642 | ``` 643 | 644 | 645 | 646 | 647 | [datetime.date(2002, 1, 2), 648 | datetime.date(2002, 1, 2), 649 | datetime.date(2002, 1, 3)] 650 | 651 | 652 | 653 | 654 | ```python 655 | cal.bizdays('2001-12-31', dates) 656 | ``` 657 | 658 | 659 | 660 | 661 | [0, 1, 2] 662 | 663 | 664 | 665 | These functions accept sequences and single values and return lists. 666 | When `bizdays` was called, a date and a sequence have been passed, computing business days between the given date and all the others. 667 | 668 | ### Recycle rule 669 | 670 | Once you pass 2 sequences, for methods that allow it, and those sequences doesn't have the same length, no problem. 671 | The shorter collection is cycled to fit the longer's length. 672 | This is the recycle rule. 673 | 674 | 675 | ```python 676 | dates = ('2002-01-01', '2002-01-02', '2002-01-03', '2002-01-04', '2002-01-05') 677 | cal.offset(dates, (1, 2, 3)) 678 | ``` 679 | 680 | 681 | 682 | 683 | [datetime.date(2002, 1, 2), 684 | datetime.date(2002, 1, 4), 685 | datetime.date(2002, 1, 8), 686 | datetime.date(2002, 1, 7), 687 | datetime.date(2002, 1, 8)] 688 | 689 | 690 | 691 | ## Pandas integration 692 | 693 | `bizdays` has an integration with pandas. 694 | To get this integration working the option `mode` must be set to `pandas`. 695 | 696 | 697 | ```python 698 | import bizdays 699 | 700 | bizdays.set_option('mode', 'pandas') 701 | ``` 702 | 703 | Once `mode` is set to `pandas`, the Calendar's methods return `Timestamp` (for single dates), `DatetimeIndex` (for sequece of dates) and `numpy.ndarray` (for other sequences). 704 | 705 | 706 | ```python 707 | cal.seq('2012-12-31', '2013-01-03') 708 | ``` 709 | 710 | 711 | 712 | 713 | DatetimeIndex(['2012-12-31', '2013-01-02', '2013-01-03'], dtype='datetime64[ns]', freq=None) 714 | 715 | 716 | 717 | 718 | ```python 719 | dates = ('2002-01-01', '2002-01-02', '2002-01-03', '2002-01-04', '2002-01-05') 720 | cal.offset(dates, (1, 2, 3)) 721 | ``` 722 | 723 | 724 | 725 | 726 | DatetimeIndex(['2002-01-02', '2002-01-04', '2002-01-08', '2002-01-07', 727 | '2002-01-08'], 728 | dtype='datetime64[ns]', freq=None) 729 | 730 | 731 | 732 | 733 | ```python 734 | dates = ('2002-01-01', '2002-01-02', '2002-01-03') 735 | cal.following(dates) 736 | ``` 737 | 738 | 739 | 740 | 741 | DatetimeIndex(['2002-01-02', '2002-01-02', '2002-01-03'], dtype='datetime64[ns]', freq=None) 742 | 743 | 744 | 745 | 746 | ```python 747 | cal.bizdays('2001-12-31', dates) 748 | ``` 749 | 750 | 751 | 752 | 753 | array([0, 1, 2]) 754 | 755 | 756 | 757 | 758 | ```python 759 | cal.isbizday(dates) 760 | ``` 761 | 762 | 763 | 764 | 765 | array([False, True, True]) 766 | 767 | 768 | 769 | 770 | ```python 771 | cal.getbizdays([2021, 2022], [12, 1]) 772 | ``` 773 | 774 | 775 | 776 | 777 | array([23, 21]) 778 | 779 | 780 | 781 | 782 | ```python 783 | cal.getdate('last fri', [2021, 2022], [12, 1]) 784 | ``` 785 | 786 | 787 | 788 | 789 | DatetimeIndex(['2021-12-31', '2022-01-28'], dtype='datetime64[ns]', freq=None) 790 | 791 | 792 | -------------------------------------------------------------------------------- /calendars.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Calendars\n", 8 | "\n", 9 | "The `Calendar` can be built in three ways:\n", 10 | "\n", 11 | "- using the constructor `Calendar` passing the arguments\n", 12 | "- with the `Calendar.load(name=\"\")` passing the name of a calendar delivered with the package (`B3`, `ANBIMA`, `Actual`) or calendars from `pandas_market_calendar` package\n", 13 | "- with the `Calendar.load(filename=\"\")` passing a file that describes a valid calendar\n", 14 | "\n", 15 | "## Calendars from file\n", 16 | "\n", 17 | "The file describing the calendar is a text file with the weekdays and holidays that represents the nonworking days." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [ 25 | { 26 | "name": "stdout", 27 | "output_type": "stream", 28 | "text": [ 29 | "Saturday\n", 30 | "Sunday\n", 31 | "2001-01-01\n", 32 | "2002-01-01\n", 33 | "2012-12-25\n", 34 | "2013-01-01\n" 35 | ] 36 | } 37 | ], 38 | "source": [ 39 | "! type Test.cal" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "Here Saturday and Sunday are nonworking week days and the other lines bring the holidays." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 2, 52 | "metadata": {}, 53 | "outputs": [ 54 | { 55 | "data": { 56 | "text/plain": [ 57 | "Calendar: Test\n", 58 | "Start: 2001-01-01\n", 59 | "End: 2013-01-01\n", 60 | "Weekdays: Saturday, Sunday\n", 61 | "Holidays: 4\n", 62 | "Financial: True" 63 | ] 64 | }, 65 | "execution_count": 2, 66 | "metadata": {}, 67 | "output_type": "execute_result" 68 | } 69 | ], 70 | "source": [ 71 | "from bizdays import Calendar\n", 72 | "\n", 73 | "Calendar.load(filename='Test.cal')" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "## Predefined Calendars\n", 81 | "\n", 82 | "`bizdays` comes with some predefined calendars: `ANBIMA`, `B3`, `Actual`, that can be directly loaded by its names.\n", 83 | "\n", 84 | "`ANBIMA` is the standard calendar used in the brazilian fixed income markets." 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 3, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "data": { 94 | "text/plain": [ 95 | "Calendar: ANBIMA\n", 96 | "Start: 2000-01-01\n", 97 | "End: 2078-12-25\n", 98 | "Weekdays: Saturday, Sunday\n", 99 | "Holidays: 948\n", 100 | "Financial: True" 101 | ] 102 | }, 103 | "execution_count": 3, 104 | "metadata": {}, 105 | "output_type": "execute_result" 106 | } 107 | ], 108 | "source": [ 109 | "Calendar.load(\"ANBIMA\")" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "`Actual` is a calendar with no holiday and nonworking days." 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": 4, 122 | "metadata": {}, 123 | "outputs": [ 124 | { 125 | "data": { 126 | "text/plain": [ 127 | "Calendar: Actual\n", 128 | "Start: 1970-01-01\n", 129 | "End: 2071-01-01\n", 130 | "Weekdays: \n", 131 | "Holidays: 0\n", 132 | "Financial: True" 133 | ] 134 | }, 135 | "execution_count": 4, 136 | "metadata": {}, 137 | "output_type": "execute_result" 138 | } 139 | ], 140 | "source": [ 141 | "Calendar.load(\"Actual\")" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "## Calendars from `pandas_market_calendars`\n", 149 | "\n", 150 | "The package [`pandas_market_calendars`](https://github.com/rsheftel/pandas_market_calendars) comes with many predefined calendars." 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 5, 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "data": { 160 | "text/plain": [ 161 | "['ASX',\n", 162 | " 'BMF',\n", 163 | " 'B3',\n", 164 | " 'BSE',\n", 165 | " 'NSE',\n", 166 | " 'CFE',\n", 167 | " 'CBOE_Futures',\n", 168 | " 'CBOE_Equity_Options',\n", 169 | " 'CBOE_Index_Options',\n", 170 | " 'CME_Equity',\n", 171 | " 'CBOT_Equity',\n", 172 | " 'CME_Agriculture',\n", 173 | " 'CBOT_Agriculture',\n", 174 | " 'COMEX_Agriculture',\n", 175 | " 'NYMEX_Agriculture',\n", 176 | " 'CME_Rate',\n", 177 | " 'CBOT_Rate',\n", 178 | " 'CME_InterestRate',\n", 179 | " 'CBOT_InterestRate',\n", 180 | " 'CME_Bond',\n", 181 | " 'CBOT_Bond',\n", 182 | " 'CMEGlobex_Livestock',\n", 183 | " 'CMEGlobex_Live_Cattle',\n", 184 | " 'CMEGlobex_Feeder_Cattle',\n", 185 | " 'CMEGlobex_Lean_Hog',\n", 186 | " 'CMEGlobex_Port_Cutout',\n", 187 | " 'CME Globex Cryptocurrencies',\n", 188 | " 'CME Globex Crypto',\n", 189 | " 'CMEGlobex_EnergyAndMetals',\n", 190 | " 'CMEGlobex_Energy',\n", 191 | " 'CMEGlobex_CrudeAndRefined',\n", 192 | " 'CMEGlobex_NYHarbor',\n", 193 | " 'CMEGlobex_HO',\n", 194 | " 'HO',\n", 195 | " 'CMEGlobex_Crude',\n", 196 | " 'CMEGlobex_CL',\n", 197 | " 'CL',\n", 198 | " 'CMEGlobex_Gas',\n", 199 | " 'CMEGlobex_RB',\n", 200 | " 'RB',\n", 201 | " 'CMEGlobex_MicroCrude',\n", 202 | " 'CMEGlobex_MCL',\n", 203 | " 'MCL',\n", 204 | " 'CMEGlobex_NatGas',\n", 205 | " 'CMEGlobex_NG',\n", 206 | " 'NG',\n", 207 | " 'CMEGlobex_Dutch_NatGas',\n", 208 | " 'CMEGlobex_TTF',\n", 209 | " 'TTF',\n", 210 | " 'CMEGlobex_LastDay_NatGas',\n", 211 | " 'CMEGlobex_NN',\n", 212 | " 'NN',\n", 213 | " 'CMEGlobex_CarbonOffset',\n", 214 | " 'CMEGlobex_CGO',\n", 215 | " 'CGO',\n", 216 | " 'C-GEO',\n", 217 | " 'CMEGlobex_NGO',\n", 218 | " 'NGO',\n", 219 | " 'CMEGlobex_GEO',\n", 220 | " 'GEO',\n", 221 | " 'CMEGlobex_Metals',\n", 222 | " 'CMEGlobex_PreciousMetals',\n", 223 | " 'CMEGlobex_Gold',\n", 224 | " 'CMEGlobex_GC',\n", 225 | " 'GC',\n", 226 | " 'CMEGlobex_SilverCMEGlobex_SI',\n", 227 | " 'SI',\n", 228 | " 'CMEGlobex_Platinum',\n", 229 | " 'CMEGlobex_PL',\n", 230 | " 'PL',\n", 231 | " 'CMEGlobex_BaseMetals',\n", 232 | " 'CMEGlobex_Copper',\n", 233 | " 'CMEGlobex_HG',\n", 234 | " 'HG',\n", 235 | " 'CMEGlobex_Aluminum',\n", 236 | " 'CMEGlobex_ALI',\n", 237 | " 'ALI',\n", 238 | " 'CMEGlobex_QC',\n", 239 | " 'QC',\n", 240 | " 'CMEGlobex_FerrousMetals',\n", 241 | " 'CMEGlobex_HRC',\n", 242 | " 'HRC',\n", 243 | " 'CMEGlobex_BUS',\n", 244 | " 'BUS',\n", 245 | " 'CMEGlobex_TIO',\n", 246 | " 'TIO',\n", 247 | " 'CME Globex Equity',\n", 248 | " 'CMEGlobex_FX',\n", 249 | " 'CME_FX',\n", 250 | " 'CME_Currency',\n", 251 | " 'CME Globex Fixed Income',\n", 252 | " 'CME Globex Interest Rate Products',\n", 253 | " 'EUREX',\n", 254 | " 'HKEX',\n", 255 | " 'ICE',\n", 256 | " 'ICEUS',\n", 257 | " 'NYFE',\n", 258 | " 'NYSE',\n", 259 | " 'stock',\n", 260 | " 'NASDAQ',\n", 261 | " 'BATS',\n", 262 | " 'DJIA',\n", 263 | " 'DOW',\n", 264 | " 'IEX',\n", 265 | " 'Investors_Exchange',\n", 266 | " 'JPX',\n", 267 | " 'LSE',\n", 268 | " 'OSE',\n", 269 | " 'SIFMAUS',\n", 270 | " 'SIFMA_US',\n", 271 | " 'Capital_Markets_US',\n", 272 | " 'Financial_Markets_US',\n", 273 | " 'Bond_Markets_US',\n", 274 | " 'SIFMAUK',\n", 275 | " 'SIFMA_UK',\n", 276 | " 'Capital_Markets_UK',\n", 277 | " 'Financial_Markets_UK',\n", 278 | " 'Bond_Markets_UK',\n", 279 | " 'SIFMAJP',\n", 280 | " 'SIFMA_JP',\n", 281 | " 'Capital_Markets_JP',\n", 282 | " 'Financial_Markets_JP',\n", 283 | " 'Bond_Markets_JP',\n", 284 | " 'SIX',\n", 285 | " 'SSE',\n", 286 | " 'TASE',\n", 287 | " 'TSX',\n", 288 | " 'TSXV',\n", 289 | " 'AIXK',\n", 290 | " 'ASEX',\n", 291 | " 'BVMF',\n", 292 | " 'CMES',\n", 293 | " 'IEPA',\n", 294 | " 'XAMS',\n", 295 | " 'XASX',\n", 296 | " 'XBKK',\n", 297 | " 'XBOG',\n", 298 | " 'XBOM',\n", 299 | " 'XBRU',\n", 300 | " 'XBSE',\n", 301 | " 'XBUD',\n", 302 | " 'XBUE',\n", 303 | " 'XCBF',\n", 304 | " 'XCSE',\n", 305 | " 'XDUB',\n", 306 | " 'XFRA',\n", 307 | " 'XETR',\n", 308 | " 'XHEL',\n", 309 | " 'XHKG',\n", 310 | " 'XICE',\n", 311 | " 'XIDX',\n", 312 | " 'XIST',\n", 313 | " 'XJSE',\n", 314 | " 'XKAR',\n", 315 | " 'XKLS',\n", 316 | " 'XKRX',\n", 317 | " 'XLIM',\n", 318 | " 'XLIS',\n", 319 | " 'XLON',\n", 320 | " 'XMAD',\n", 321 | " 'XMEX',\n", 322 | " 'XMIL',\n", 323 | " 'XMOS',\n", 324 | " 'XNYS',\n", 325 | " 'XNZE',\n", 326 | " 'XOSL',\n", 327 | " 'XPAR',\n", 328 | " 'XPHS',\n", 329 | " 'XPRA',\n", 330 | " 'XSAU',\n", 331 | " 'XSES',\n", 332 | " 'XSGO',\n", 333 | " 'XSHG',\n", 334 | " 'XSTO',\n", 335 | " 'XSWX',\n", 336 | " 'XTAE',\n", 337 | " 'XTAI',\n", 338 | " 'XTKS',\n", 339 | " 'XTSE',\n", 340 | " 'XWAR',\n", 341 | " 'XWBO',\n", 342 | " 'us_futures',\n", 343 | " '24/7',\n", 344 | " '24/5']" 345 | ] 346 | }, 347 | "execution_count": 5, 348 | "metadata": {}, 349 | "output_type": "execute_result" 350 | } 351 | ], 352 | "source": [ 353 | "import pandas_market_calendars as mcal\n", 354 | "\n", 355 | "mcal.get_calendar_names()" 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "These calendars can be loaded in bizdays with the prefix `PMC/` in its name.\n", 363 | "For example, use `PMC/NYSE` to load the NYSE calendar." 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": 6, 369 | "metadata": {}, 370 | "outputs": [ 371 | { 372 | "data": { 373 | "text/plain": [ 374 | "Calendar: PMC/NYSE\n", 375 | "Start: 1885-01-01\n", 376 | "End: 2200-12-25\n", 377 | "Weekdays: Saturday, Sunday\n", 378 | "Holidays: 3552\n", 379 | "Financial: True" 380 | ] 381 | }, 382 | "execution_count": 6, 383 | "metadata": {}, 384 | "output_type": "execute_result" 385 | } 386 | ], 387 | "source": [ 388 | "Calendar.load(\"PMC/NYSE\")" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "## Create your own Calendars\n", 396 | "\n", 397 | "`Calendar` can be called with no arguments and it returns a calendar without nonworking days." 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": 4, 403 | "metadata": {}, 404 | "outputs": [ 405 | { 406 | "data": { 407 | "text/plain": [ 408 | "Calendar: None\n", 409 | "Start: 1970-01-01\n", 410 | "End: 2071-01-01\n", 411 | "Weekdays: \n", 412 | "Holidays: 0\n", 413 | "Financial: True" 414 | ] 415 | }, 416 | "execution_count": 4, 417 | "metadata": {}, 418 | "output_type": "execute_result" 419 | } 420 | ], 421 | "source": [ 422 | "Calendar()" 423 | ] 424 | }, 425 | { 426 | "cell_type": "markdown", 427 | "metadata": {}, 428 | "source": [ 429 | "Naming the calendar is a good idea." 430 | ] 431 | }, 432 | { 433 | "cell_type": "code", 434 | "execution_count": 5, 435 | "metadata": {}, 436 | "outputs": [ 437 | { 438 | "data": { 439 | "text/plain": [ 440 | "Calendar: actual\n", 441 | "Start: 1970-01-01\n", 442 | "End: 2071-01-01\n", 443 | "Weekdays: \n", 444 | "Holidays: 0\n", 445 | "Financial: True" 446 | ] 447 | }, 448 | "execution_count": 5, 449 | "metadata": {}, 450 | "output_type": "execute_result" 451 | } 452 | ], 453 | "source": [ 454 | "Calendar(name='actual')" 455 | ] 456 | }, 457 | { 458 | "cell_type": "markdown", 459 | "metadata": {}, 460 | "source": [ 461 | "## Financial Calendars\n", 462 | "\n", 463 | "`bizdays` was designed to be used with financial calculations, for this reason all calendars are financial by default.\n", 464 | "\n", 465 | "In finance, the amount of business days between two consecutive dates is 1, which indicates one day of compounding interest rates.\n", 466 | "\n", 467 | "For example." 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": 6, 473 | "metadata": {}, 474 | "outputs": [ 475 | { 476 | "data": { 477 | "text/plain": [ 478 | "1" 479 | ] 480 | }, 481 | "execution_count": 6, 482 | "metadata": {}, 483 | "output_type": "execute_result" 484 | } 485 | ], 486 | "source": [ 487 | "cal = Calendar()\n", 488 | "cal.bizdays('2021-01-01', '2021-01-02')" 489 | ] 490 | }, 491 | { 492 | "cell_type": "markdown", 493 | "metadata": {}, 494 | "source": [ 495 | "For this calendar both dates are business days, but the return is one business day.\n", 496 | "\n", 497 | "This behavior can changed by setting the attribute `financial` to `False`." 498 | ] 499 | }, 500 | { 501 | "cell_type": "code", 502 | "execution_count": 7, 503 | "metadata": {}, 504 | "outputs": [ 505 | { 506 | "data": { 507 | "text/plain": [ 508 | "2" 509 | ] 510 | }, 511 | "execution_count": 7, 512 | "metadata": {}, 513 | "output_type": "execute_result" 514 | } 515 | ], 516 | "source": [ 517 | "cal.financial = False\n", 518 | "cal.bizdays('2021-01-01', '2021-01-02')" 519 | ] 520 | } 521 | ], 522 | "metadata": { 523 | "kernelspec": { 524 | "display_name": "Python 3.7.11 ('venv': venv)", 525 | "language": "python", 526 | "name": "python3" 527 | }, 528 | "language_info": { 529 | "codemirror_mode": { 530 | "name": "ipython", 531 | "version": 3 532 | }, 533 | "file_extension": ".py", 534 | "mimetype": "text/x-python", 535 | "name": "python", 536 | "nbconvert_exporter": "python", 537 | "pygments_lexer": "ipython3", 538 | "version": "3.10.11" 539 | }, 540 | "vscode": { 541 | "interpreter": { 542 | "hash": "804a4a06e7ed5f90e0da5c1a0173147605cc28b78a3676d1fdeb0cd38c97fa0e" 543 | } 544 | } 545 | }, 546 | "nbformat": 4, 547 | "nbformat_minor": 2 548 | } 549 | -------------------------------------------------------------------------------- /ANBIMA.txt: -------------------------------------------------------------------------------- 1 | 2000-01-01 2 | 2000-03-06 3 | 2000-03-07 4 | 2000-04-21 5 | 2000-04-23 6 | 2000-05-01 7 | 2000-06-22 8 | 2000-09-07 9 | 2000-10-12 10 | 2000-11-02 11 | 2000-11-15 12 | 2000-12-25 13 | 2001-01-01 14 | 2001-02-26 15 | 2001-02-27 16 | 2001-04-13 17 | 2001-04-21 18 | 2001-05-01 19 | 2001-06-14 20 | 2001-09-07 21 | 2001-10-12 22 | 2001-11-02 23 | 2001-11-15 24 | 2001-12-25 25 | 2002-01-01 26 | 2002-02-11 27 | 2002-02-12 28 | 2002-03-29 29 | 2002-04-21 30 | 2002-05-01 31 | 2002-05-30 32 | 2002-09-07 33 | 2002-10-12 34 | 2002-11-02 35 | 2002-11-15 36 | 2002-12-25 37 | 2003-01-01 38 | 2003-03-03 39 | 2003-03-04 40 | 2003-04-18 41 | 2003-04-21 42 | 2003-05-01 43 | 2003-06-19 44 | 2003-09-07 45 | 2003-10-12 46 | 2003-11-02 47 | 2003-11-15 48 | 2003-12-25 49 | 2004-01-01 50 | 2004-02-23 51 | 2004-02-24 52 | 2004-04-09 53 | 2004-04-21 54 | 2004-05-01 55 | 2004-06-10 56 | 2004-09-07 57 | 2004-10-12 58 | 2004-11-02 59 | 2004-11-15 60 | 2004-12-25 61 | 2005-01-01 62 | 2005-02-07 63 | 2005-02-08 64 | 2005-03-25 65 | 2005-04-21 66 | 2005-05-01 67 | 2005-05-26 68 | 2005-09-07 69 | 2005-10-12 70 | 2005-11-02 71 | 2005-11-15 72 | 2005-12-25 73 | 2006-01-01 74 | 2006-02-27 75 | 2006-02-28 76 | 2006-04-14 77 | 2006-04-21 78 | 2006-05-01 79 | 2006-06-15 80 | 2006-09-07 81 | 2006-10-12 82 | 2006-11-02 83 | 2006-11-15 84 | 2006-12-25 85 | 2007-01-01 86 | 2007-02-19 87 | 2007-02-20 88 | 2007-04-06 89 | 2007-04-21 90 | 2007-05-01 91 | 2007-06-07 92 | 2007-09-07 93 | 2007-10-12 94 | 2007-11-02 95 | 2007-11-15 96 | 2007-12-25 97 | 2008-01-01 98 | 2008-02-04 99 | 2008-02-05 100 | 2008-03-21 101 | 2008-04-21 102 | 2008-05-01 103 | 2008-05-22 104 | 2008-09-07 105 | 2008-10-12 106 | 2008-11-02 107 | 2008-11-15 108 | 2008-12-25 109 | 2009-01-01 110 | 2009-02-23 111 | 2009-02-24 112 | 2009-04-10 113 | 2009-04-21 114 | 2009-05-01 115 | 2009-06-11 116 | 2009-09-07 117 | 2009-10-12 118 | 2009-11-02 119 | 2009-11-15 120 | 2009-12-25 121 | 2010-01-01 122 | 2010-02-15 123 | 2010-02-16 124 | 2010-04-02 125 | 2010-04-21 126 | 2010-05-01 127 | 2010-06-03 128 | 2010-09-07 129 | 2010-10-12 130 | 2010-11-02 131 | 2010-11-15 132 | 2010-12-25 133 | 2011-01-01 134 | 2011-03-07 135 | 2011-03-08 136 | 2011-04-21 137 | 2011-04-22 138 | 2011-05-01 139 | 2011-06-23 140 | 2011-09-07 141 | 2011-10-12 142 | 2011-11-02 143 | 2011-11-15 144 | 2011-12-25 145 | 2012-01-01 146 | 2012-02-20 147 | 2012-02-21 148 | 2012-04-06 149 | 2012-04-21 150 | 2012-05-01 151 | 2012-06-07 152 | 2012-09-07 153 | 2012-10-12 154 | 2012-11-02 155 | 2012-11-15 156 | 2012-12-25 157 | 2013-01-01 158 | 2013-02-11 159 | 2013-02-12 160 | 2013-03-29 161 | 2013-04-21 162 | 2013-05-01 163 | 2013-05-30 164 | 2013-09-07 165 | 2013-10-12 166 | 2013-11-02 167 | 2013-11-15 168 | 2013-12-25 169 | 2014-01-01 170 | 2014-03-03 171 | 2014-03-04 172 | 2014-04-18 173 | 2014-04-21 174 | 2014-05-01 175 | 2014-06-19 176 | 2014-09-07 177 | 2014-10-12 178 | 2014-11-02 179 | 2014-11-15 180 | 2014-12-25 181 | 2015-01-01 182 | 2015-02-16 183 | 2015-02-17 184 | 2015-04-03 185 | 2015-04-21 186 | 2015-05-01 187 | 2015-06-04 188 | 2015-09-07 189 | 2015-10-12 190 | 2015-11-02 191 | 2015-11-15 192 | 2015-12-25 193 | 2016-01-01 194 | 2016-02-08 195 | 2016-02-09 196 | 2016-03-25 197 | 2016-04-21 198 | 2016-05-01 199 | 2016-05-26 200 | 2016-09-07 201 | 2016-10-12 202 | 2016-11-02 203 | 2016-11-15 204 | 2016-12-25 205 | 2017-01-01 206 | 2017-02-27 207 | 2017-02-28 208 | 2017-04-14 209 | 2017-04-21 210 | 2017-05-01 211 | 2017-06-15 212 | 2017-09-07 213 | 2017-10-12 214 | 2017-11-02 215 | 2017-11-15 216 | 2017-12-25 217 | 2018-01-01 218 | 2018-02-12 219 | 2018-02-13 220 | 2018-03-30 221 | 2018-04-21 222 | 2018-05-01 223 | 2018-05-31 224 | 2018-09-07 225 | 2018-10-12 226 | 2018-11-02 227 | 2018-11-15 228 | 2018-12-25 229 | 2019-01-01 230 | 2019-03-04 231 | 2019-03-05 232 | 2019-04-19 233 | 2019-04-21 234 | 2019-05-01 235 | 2019-06-20 236 | 2019-09-07 237 | 2019-10-12 238 | 2019-11-02 239 | 2019-11-15 240 | 2019-12-25 241 | 2020-01-01 242 | 2020-02-24 243 | 2020-02-25 244 | 2020-04-10 245 | 2020-04-21 246 | 2020-05-01 247 | 2020-06-11 248 | 2020-09-07 249 | 2020-10-12 250 | 2020-11-02 251 | 2020-11-15 252 | 2020-12-25 253 | 2021-01-01 254 | 2021-02-15 255 | 2021-02-16 256 | 2021-04-02 257 | 2021-04-21 258 | 2021-05-01 259 | 2021-06-03 260 | 2021-09-07 261 | 2021-10-12 262 | 2021-11-02 263 | 2021-11-15 264 | 2021-12-25 265 | 2022-01-01 266 | 2022-02-28 267 | 2022-03-01 268 | 2022-04-15 269 | 2022-04-21 270 | 2022-05-01 271 | 2022-06-16 272 | 2022-09-07 273 | 2022-10-12 274 | 2022-11-02 275 | 2022-11-15 276 | 2022-12-25 277 | 2023-01-01 278 | 2023-02-20 279 | 2023-02-21 280 | 2023-04-07 281 | 2023-04-21 282 | 2023-05-01 283 | 2023-06-08 284 | 2023-09-07 285 | 2023-10-12 286 | 2023-11-02 287 | 2023-11-15 288 | 2023-12-25 289 | 2024-01-01 290 | 2024-02-12 291 | 2024-02-13 292 | 2024-03-29 293 | 2024-04-21 294 | 2024-05-01 295 | 2024-05-30 296 | 2024-09-07 297 | 2024-10-12 298 | 2024-11-02 299 | 2024-11-15 300 | 2024-12-25 301 | 2025-01-01 302 | 2025-03-03 303 | 2025-03-04 304 | 2025-04-18 305 | 2025-04-21 306 | 2025-05-01 307 | 2025-06-19 308 | 2025-09-07 309 | 2025-10-12 310 | 2025-11-02 311 | 2025-11-15 312 | 2025-12-25 313 | 2026-01-01 314 | 2026-02-16 315 | 2026-02-17 316 | 2026-04-03 317 | 2026-04-21 318 | 2026-05-01 319 | 2026-06-04 320 | 2026-09-07 321 | 2026-10-12 322 | 2026-11-02 323 | 2026-11-15 324 | 2026-12-25 325 | 2027-01-01 326 | 2027-02-08 327 | 2027-02-09 328 | 2027-03-26 329 | 2027-04-21 330 | 2027-05-01 331 | 2027-05-27 332 | 2027-09-07 333 | 2027-10-12 334 | 2027-11-02 335 | 2027-11-15 336 | 2027-12-25 337 | 2028-01-01 338 | 2028-02-28 339 | 2028-02-29 340 | 2028-04-14 341 | 2028-04-21 342 | 2028-05-01 343 | 2028-06-15 344 | 2028-09-07 345 | 2028-10-12 346 | 2028-11-02 347 | 2028-11-15 348 | 2028-12-25 349 | 2029-01-01 350 | 2029-02-12 351 | 2029-02-13 352 | 2029-03-30 353 | 2029-04-21 354 | 2029-05-01 355 | 2029-05-31 356 | 2029-09-07 357 | 2029-10-12 358 | 2029-11-02 359 | 2029-11-15 360 | 2029-12-25 361 | 2030-01-01 362 | 2030-03-04 363 | 2030-03-05 364 | 2030-04-19 365 | 2030-04-21 366 | 2030-05-01 367 | 2030-06-20 368 | 2030-09-07 369 | 2030-10-12 370 | 2030-11-02 371 | 2030-11-15 372 | 2030-12-25 373 | 2031-01-01 374 | 2031-02-24 375 | 2031-02-25 376 | 2031-04-11 377 | 2031-04-21 378 | 2031-05-01 379 | 2031-06-12 380 | 2031-09-07 381 | 2031-10-12 382 | 2031-11-02 383 | 2031-11-15 384 | 2031-12-25 385 | 2032-01-01 386 | 2032-02-09 387 | 2032-02-10 388 | 2032-03-26 389 | 2032-04-21 390 | 2032-05-01 391 | 2032-05-27 392 | 2032-09-07 393 | 2032-10-12 394 | 2032-11-02 395 | 2032-11-15 396 | 2032-12-25 397 | 2033-01-01 398 | 2033-02-28 399 | 2033-03-01 400 | 2033-04-15 401 | 2033-04-21 402 | 2033-05-01 403 | 2033-06-16 404 | 2033-09-07 405 | 2033-10-12 406 | 2033-11-02 407 | 2033-11-15 408 | 2033-12-25 409 | 2034-01-01 410 | 2034-02-20 411 | 2034-02-21 412 | 2034-04-07 413 | 2034-04-21 414 | 2034-05-01 415 | 2034-06-08 416 | 2034-09-07 417 | 2034-10-12 418 | 2034-11-02 419 | 2034-11-15 420 | 2034-12-25 421 | 2035-01-01 422 | 2035-02-05 423 | 2035-02-06 424 | 2035-03-23 425 | 2035-04-21 426 | 2035-05-01 427 | 2035-05-24 428 | 2035-09-07 429 | 2035-10-12 430 | 2035-11-02 431 | 2035-11-15 432 | 2035-12-25 433 | 2036-01-01 434 | 2036-02-25 435 | 2036-02-26 436 | 2036-04-11 437 | 2036-04-21 438 | 2036-05-01 439 | 2036-06-12 440 | 2036-09-07 441 | 2036-10-12 442 | 2036-11-02 443 | 2036-11-15 444 | 2036-12-25 445 | 2037-01-01 446 | 2037-02-16 447 | 2037-02-17 448 | 2037-04-03 449 | 2037-04-21 450 | 2037-05-01 451 | 2037-06-04 452 | 2037-09-07 453 | 2037-10-12 454 | 2037-11-02 455 | 2037-11-15 456 | 2037-12-25 457 | 2038-01-01 458 | 2038-03-08 459 | 2038-03-09 460 | 2038-04-21 461 | 2038-04-23 462 | 2038-05-01 463 | 2038-06-24 464 | 2038-09-07 465 | 2038-10-12 466 | 2038-11-02 467 | 2038-11-15 468 | 2038-12-25 469 | 2039-01-01 470 | 2039-02-21 471 | 2039-02-22 472 | 2039-04-08 473 | 2039-04-21 474 | 2039-05-01 475 | 2039-06-09 476 | 2039-09-07 477 | 2039-10-12 478 | 2039-11-02 479 | 2039-11-15 480 | 2039-12-25 481 | 2040-01-01 482 | 2040-02-13 483 | 2040-02-14 484 | 2040-03-30 485 | 2040-04-21 486 | 2040-05-01 487 | 2040-05-31 488 | 2040-09-07 489 | 2040-10-12 490 | 2040-11-02 491 | 2040-11-15 492 | 2040-12-25 493 | 2041-01-01 494 | 2041-03-04 495 | 2041-03-05 496 | 2041-04-19 497 | 2041-04-21 498 | 2041-05-01 499 | 2041-06-20 500 | 2041-09-07 501 | 2041-10-12 502 | 2041-11-02 503 | 2041-11-15 504 | 2041-12-25 505 | 2042-01-01 506 | 2042-02-17 507 | 2042-02-18 508 | 2042-04-04 509 | 2042-04-21 510 | 2042-05-01 511 | 2042-06-05 512 | 2042-09-07 513 | 2042-10-12 514 | 2042-11-02 515 | 2042-11-15 516 | 2042-12-25 517 | 2043-01-01 518 | 2043-02-09 519 | 2043-02-10 520 | 2043-03-27 521 | 2043-04-21 522 | 2043-05-01 523 | 2043-05-28 524 | 2043-09-07 525 | 2043-10-12 526 | 2043-11-02 527 | 2043-11-15 528 | 2043-12-25 529 | 2044-01-01 530 | 2044-02-29 531 | 2044-03-01 532 | 2044-04-15 533 | 2044-04-21 534 | 2044-05-01 535 | 2044-06-16 536 | 2044-09-07 537 | 2044-10-12 538 | 2044-11-02 539 | 2044-11-15 540 | 2044-12-25 541 | 2045-01-01 542 | 2045-02-20 543 | 2045-02-21 544 | 2045-04-07 545 | 2045-04-21 546 | 2045-05-01 547 | 2045-06-08 548 | 2045-09-07 549 | 2045-10-12 550 | 2045-11-02 551 | 2045-11-15 552 | 2045-12-25 553 | 2046-01-01 554 | 2046-02-05 555 | 2046-02-06 556 | 2046-03-23 557 | 2046-04-21 558 | 2046-05-01 559 | 2046-05-24 560 | 2046-09-07 561 | 2046-10-12 562 | 2046-11-02 563 | 2046-11-15 564 | 2046-12-25 565 | 2047-01-01 566 | 2047-02-25 567 | 2047-02-26 568 | 2047-04-12 569 | 2047-04-21 570 | 2047-05-01 571 | 2047-06-13 572 | 2047-09-07 573 | 2047-10-12 574 | 2047-11-02 575 | 2047-11-15 576 | 2047-12-25 577 | 2048-01-01 578 | 2048-02-17 579 | 2048-02-18 580 | 2048-04-03 581 | 2048-04-21 582 | 2048-05-01 583 | 2048-06-04 584 | 2048-09-07 585 | 2048-10-12 586 | 2048-11-02 587 | 2048-11-15 588 | 2048-12-25 589 | 2049-01-01 590 | 2049-03-01 591 | 2049-03-02 592 | 2049-04-16 593 | 2049-04-21 594 | 2049-05-01 595 | 2049-06-17 596 | 2049-09-07 597 | 2049-10-12 598 | 2049-11-02 599 | 2049-11-15 600 | 2049-12-25 601 | 2050-01-01 602 | 2050-02-21 603 | 2050-02-22 604 | 2050-04-08 605 | 2050-04-21 606 | 2050-05-01 607 | 2050-06-09 608 | 2050-09-07 609 | 2050-10-12 610 | 2050-11-02 611 | 2050-11-15 612 | 2050-12-25 613 | 2051-01-01 614 | 2051-02-13 615 | 2051-02-14 616 | 2051-03-31 617 | 2051-04-21 618 | 2051-05-01 619 | 2051-06-01 620 | 2051-09-07 621 | 2051-10-12 622 | 2051-11-02 623 | 2051-11-15 624 | 2051-12-25 625 | 2052-01-01 626 | 2052-03-04 627 | 2052-03-05 628 | 2052-04-19 629 | 2052-04-21 630 | 2052-05-01 631 | 2052-06-20 632 | 2052-09-07 633 | 2052-10-12 634 | 2052-11-02 635 | 2052-11-15 636 | 2052-12-25 637 | 2053-01-01 638 | 2053-02-17 639 | 2053-02-18 640 | 2053-04-04 641 | 2053-04-21 642 | 2053-05-01 643 | 2053-06-05 644 | 2053-09-07 645 | 2053-10-12 646 | 2053-11-02 647 | 2053-11-15 648 | 2053-12-25 649 | 2054-01-01 650 | 2054-02-09 651 | 2054-02-10 652 | 2054-03-27 653 | 2054-04-21 654 | 2054-05-01 655 | 2054-05-28 656 | 2054-09-07 657 | 2054-10-12 658 | 2054-11-02 659 | 2054-11-15 660 | 2054-12-25 661 | 2055-01-01 662 | 2055-03-01 663 | 2055-03-02 664 | 2055-04-16 665 | 2055-04-21 666 | 2055-05-01 667 | 2055-06-17 668 | 2055-09-07 669 | 2055-10-12 670 | 2055-11-02 671 | 2055-11-15 672 | 2055-12-25 673 | 2056-01-01 674 | 2056-02-14 675 | 2056-02-15 676 | 2056-03-31 677 | 2056-04-21 678 | 2056-05-01 679 | 2056-06-01 680 | 2056-09-07 681 | 2056-10-12 682 | 2056-11-02 683 | 2056-11-15 684 | 2056-12-25 685 | 2057-01-01 686 | 2057-03-05 687 | 2057-03-06 688 | 2057-04-20 689 | 2057-04-21 690 | 2057-05-01 691 | 2057-06-21 692 | 2057-09-07 693 | 2057-10-12 694 | 2057-11-02 695 | 2057-11-15 696 | 2057-12-25 697 | 2058-01-01 698 | 2058-02-25 699 | 2058-02-26 700 | 2058-04-12 701 | 2058-04-21 702 | 2058-05-01 703 | 2058-06-13 704 | 2058-09-07 705 | 2058-10-12 706 | 2058-11-02 707 | 2058-11-15 708 | 2058-12-25 709 | 2059-01-01 710 | 2059-02-10 711 | 2059-02-11 712 | 2059-03-28 713 | 2059-04-21 714 | 2059-05-01 715 | 2059-05-29 716 | 2059-09-07 717 | 2059-10-12 718 | 2059-11-02 719 | 2059-11-15 720 | 2059-12-25 721 | 2060-01-01 722 | 2060-03-01 723 | 2060-03-02 724 | 2060-04-16 725 | 2060-04-21 726 | 2060-05-01 727 | 2060-06-17 728 | 2060-09-07 729 | 2060-10-12 730 | 2060-11-02 731 | 2060-11-15 732 | 2060-12-25 733 | 2061-01-01 734 | 2061-02-21 735 | 2061-02-22 736 | 2061-04-08 737 | 2061-04-21 738 | 2061-05-01 739 | 2061-06-09 740 | 2061-09-07 741 | 2061-10-12 742 | 2061-11-02 743 | 2061-11-15 744 | 2061-12-25 745 | 2062-01-01 746 | 2062-02-06 747 | 2062-02-07 748 | 2062-03-24 749 | 2062-04-21 750 | 2062-05-01 751 | 2062-05-25 752 | 2062-09-07 753 | 2062-10-12 754 | 2062-11-02 755 | 2062-11-15 756 | 2062-12-25 757 | 2063-01-01 758 | 2063-02-26 759 | 2063-02-27 760 | 2063-04-13 761 | 2063-04-21 762 | 2063-05-01 763 | 2063-06-14 764 | 2063-09-07 765 | 2063-10-12 766 | 2063-11-02 767 | 2063-11-15 768 | 2063-12-25 769 | 2064-01-01 770 | 2064-02-18 771 | 2064-02-19 772 | 2064-04-04 773 | 2064-04-21 774 | 2064-05-01 775 | 2064-06-05 776 | 2064-09-07 777 | 2064-10-12 778 | 2064-11-02 779 | 2064-11-15 780 | 2064-12-25 781 | 2065-01-01 782 | 2065-02-09 783 | 2065-02-10 784 | 2065-03-27 785 | 2065-04-21 786 | 2065-05-01 787 | 2065-05-28 788 | 2065-09-07 789 | 2065-10-12 790 | 2065-11-02 791 | 2065-11-15 792 | 2065-12-25 793 | 2066-01-01 794 | 2066-02-22 795 | 2066-02-23 796 | 2066-04-09 797 | 2066-04-21 798 | 2066-05-01 799 | 2066-06-10 800 | 2066-09-07 801 | 2066-10-12 802 | 2066-11-02 803 | 2066-11-15 804 | 2066-12-25 805 | 2067-01-01 806 | 2067-02-14 807 | 2067-02-15 808 | 2067-04-01 809 | 2067-04-21 810 | 2067-05-01 811 | 2067-06-02 812 | 2067-09-07 813 | 2067-10-12 814 | 2067-11-02 815 | 2067-11-15 816 | 2067-12-25 817 | 2068-01-01 818 | 2068-03-05 819 | 2068-03-06 820 | 2068-04-20 821 | 2068-04-21 822 | 2068-05-01 823 | 2068-06-21 824 | 2068-09-07 825 | 2068-10-12 826 | 2068-11-02 827 | 2068-11-15 828 | 2068-12-25 829 | 2069-01-01 830 | 2069-02-25 831 | 2069-02-26 832 | 2069-04-12 833 | 2069-04-21 834 | 2069-05-01 835 | 2069-06-13 836 | 2069-09-07 837 | 2069-10-12 838 | 2069-11-02 839 | 2069-11-15 840 | 2069-12-25 841 | 2070-01-01 842 | 2070-02-10 843 | 2070-02-11 844 | 2070-03-28 845 | 2070-04-21 846 | 2070-05-01 847 | 2070-05-29 848 | 2070-09-07 849 | 2070-10-12 850 | 2070-11-02 851 | 2070-11-15 852 | 2070-12-25 853 | 2071-01-01 854 | 2071-03-02 855 | 2071-03-03 856 | 2071-04-17 857 | 2071-04-21 858 | 2071-05-01 859 | 2071-06-18 860 | 2071-09-07 861 | 2071-10-12 862 | 2071-11-02 863 | 2071-11-15 864 | 2071-12-25 865 | 2072-01-01 866 | 2072-02-22 867 | 2072-02-23 868 | 2072-04-08 869 | 2072-04-21 870 | 2072-05-01 871 | 2072-06-09 872 | 2072-09-07 873 | 2072-10-12 874 | 2072-11-02 875 | 2072-11-15 876 | 2072-12-25 877 | 2073-01-01 878 | 2073-02-06 879 | 2073-02-07 880 | 2073-03-24 881 | 2073-04-21 882 | 2073-05-01 883 | 2073-05-25 884 | 2073-09-07 885 | 2073-10-12 886 | 2073-11-02 887 | 2073-11-15 888 | 2073-12-25 889 | 2074-01-01 890 | 2074-02-26 891 | 2074-02-27 892 | 2074-04-13 893 | 2074-04-21 894 | 2074-05-01 895 | 2074-06-14 896 | 2074-09-07 897 | 2074-10-12 898 | 2074-11-02 899 | 2074-11-15 900 | 2074-12-25 901 | 2075-01-01 902 | 2075-02-18 903 | 2075-02-19 904 | 2075-04-05 905 | 2075-04-21 906 | 2075-05-01 907 | 2075-06-06 908 | 2075-09-07 909 | 2075-10-12 910 | 2075-11-02 911 | 2075-11-15 912 | 2075-12-25 913 | 2076-01-01 914 | 2076-03-02 915 | 2076-03-03 916 | 2076-04-17 917 | 2076-04-21 918 | 2076-05-01 919 | 2076-06-18 920 | 2076-09-07 921 | 2076-10-12 922 | 2076-11-02 923 | 2076-11-15 924 | 2076-12-25 925 | 2077-01-01 926 | 2077-02-22 927 | 2077-02-23 928 | 2077-04-09 929 | 2077-04-21 930 | 2077-05-01 931 | 2077-06-10 932 | 2077-09-07 933 | 2077-10-12 934 | 2077-11-02 935 | 2077-11-15 936 | 2077-12-25 937 | 2078-01-01 938 | 2078-02-14 939 | 2078-02-15 940 | 2078-04-01 941 | 2078-04-21 942 | 2078-05-01 943 | 2078-06-02 944 | 2078-09-07 945 | 2078-10-12 946 | 2078-11-02 947 | 2078-11-15 948 | 2078-12-25 949 | -------------------------------------------------------------------------------- /ANBIMA.cal: -------------------------------------------------------------------------------- 1 | Saturday 2 | Sunday 3 | 2000-01-01 4 | 2000-03-06 5 | 2000-03-07 6 | 2000-04-21 7 | 2000-04-23 8 | 2000-05-01 9 | 2000-06-22 10 | 2000-09-07 11 | 2000-10-12 12 | 2000-11-02 13 | 2000-11-15 14 | 2000-12-25 15 | 2001-01-01 16 | 2001-02-26 17 | 2001-02-27 18 | 2001-04-13 19 | 2001-04-21 20 | 2001-05-01 21 | 2001-06-14 22 | 2001-09-07 23 | 2001-10-12 24 | 2001-11-02 25 | 2001-11-15 26 | 2001-12-25 27 | 2002-01-01 28 | 2002-02-11 29 | 2002-02-12 30 | 2002-03-29 31 | 2002-04-21 32 | 2002-05-01 33 | 2002-05-30 34 | 2002-09-07 35 | 2002-10-12 36 | 2002-11-02 37 | 2002-11-15 38 | 2002-12-25 39 | 2003-01-01 40 | 2003-03-03 41 | 2003-03-04 42 | 2003-04-18 43 | 2003-04-21 44 | 2003-05-01 45 | 2003-06-19 46 | 2003-09-07 47 | 2003-10-12 48 | 2003-11-02 49 | 2003-11-15 50 | 2003-12-25 51 | 2004-01-01 52 | 2004-02-23 53 | 2004-02-24 54 | 2004-04-09 55 | 2004-04-21 56 | 2004-05-01 57 | 2004-06-10 58 | 2004-09-07 59 | 2004-10-12 60 | 2004-11-02 61 | 2004-11-15 62 | 2004-12-25 63 | 2005-01-01 64 | 2005-02-07 65 | 2005-02-08 66 | 2005-03-25 67 | 2005-04-21 68 | 2005-05-01 69 | 2005-05-26 70 | 2005-09-07 71 | 2005-10-12 72 | 2005-11-02 73 | 2005-11-15 74 | 2005-12-25 75 | 2006-01-01 76 | 2006-02-27 77 | 2006-02-28 78 | 2006-04-14 79 | 2006-04-21 80 | 2006-05-01 81 | 2006-06-15 82 | 2006-09-07 83 | 2006-10-12 84 | 2006-11-02 85 | 2006-11-15 86 | 2006-12-25 87 | 2007-01-01 88 | 2007-02-19 89 | 2007-02-20 90 | 2007-04-06 91 | 2007-04-21 92 | 2007-05-01 93 | 2007-06-07 94 | 2007-09-07 95 | 2007-10-12 96 | 2007-11-02 97 | 2007-11-15 98 | 2007-12-25 99 | 2008-01-01 100 | 2008-02-04 101 | 2008-02-05 102 | 2008-03-21 103 | 2008-04-21 104 | 2008-05-01 105 | 2008-05-22 106 | 2008-09-07 107 | 2008-10-12 108 | 2008-11-02 109 | 2008-11-15 110 | 2008-12-25 111 | 2009-01-01 112 | 2009-02-23 113 | 2009-02-24 114 | 2009-04-10 115 | 2009-04-21 116 | 2009-05-01 117 | 2009-06-11 118 | 2009-09-07 119 | 2009-10-12 120 | 2009-11-02 121 | 2009-11-15 122 | 2009-12-25 123 | 2010-01-01 124 | 2010-02-15 125 | 2010-02-16 126 | 2010-04-02 127 | 2010-04-21 128 | 2010-05-01 129 | 2010-06-03 130 | 2010-09-07 131 | 2010-10-12 132 | 2010-11-02 133 | 2010-11-15 134 | 2010-12-25 135 | 2011-01-01 136 | 2011-03-07 137 | 2011-03-08 138 | 2011-04-21 139 | 2011-04-22 140 | 2011-05-01 141 | 2011-06-23 142 | 2011-09-07 143 | 2011-10-12 144 | 2011-11-02 145 | 2011-11-15 146 | 2011-12-25 147 | 2012-01-01 148 | 2012-02-20 149 | 2012-02-21 150 | 2012-04-06 151 | 2012-04-21 152 | 2012-05-01 153 | 2012-06-07 154 | 2012-09-07 155 | 2012-10-12 156 | 2012-11-02 157 | 2012-11-15 158 | 2012-12-25 159 | 2013-01-01 160 | 2013-02-11 161 | 2013-02-12 162 | 2013-03-29 163 | 2013-04-21 164 | 2013-05-01 165 | 2013-05-30 166 | 2013-09-07 167 | 2013-10-12 168 | 2013-11-02 169 | 2013-11-15 170 | 2013-12-25 171 | 2014-01-01 172 | 2014-03-03 173 | 2014-03-04 174 | 2014-04-18 175 | 2014-04-21 176 | 2014-05-01 177 | 2014-06-19 178 | 2014-09-07 179 | 2014-10-12 180 | 2014-11-02 181 | 2014-11-15 182 | 2014-12-25 183 | 2015-01-01 184 | 2015-02-16 185 | 2015-02-17 186 | 2015-04-03 187 | 2015-04-21 188 | 2015-05-01 189 | 2015-06-04 190 | 2015-09-07 191 | 2015-10-12 192 | 2015-11-02 193 | 2015-11-15 194 | 2015-12-25 195 | 2016-01-01 196 | 2016-02-08 197 | 2016-02-09 198 | 2016-03-25 199 | 2016-04-21 200 | 2016-05-01 201 | 2016-05-26 202 | 2016-09-07 203 | 2016-10-12 204 | 2016-11-02 205 | 2016-11-15 206 | 2016-12-25 207 | 2017-01-01 208 | 2017-02-27 209 | 2017-02-28 210 | 2017-04-14 211 | 2017-04-21 212 | 2017-05-01 213 | 2017-06-15 214 | 2017-09-07 215 | 2017-10-12 216 | 2017-11-02 217 | 2017-11-15 218 | 2017-12-25 219 | 2018-01-01 220 | 2018-02-12 221 | 2018-02-13 222 | 2018-03-30 223 | 2018-04-21 224 | 2018-05-01 225 | 2018-05-31 226 | 2018-09-07 227 | 2018-10-12 228 | 2018-11-02 229 | 2018-11-15 230 | 2018-12-25 231 | 2019-01-01 232 | 2019-03-04 233 | 2019-03-05 234 | 2019-04-19 235 | 2019-04-21 236 | 2019-05-01 237 | 2019-06-20 238 | 2019-09-07 239 | 2019-10-12 240 | 2019-11-02 241 | 2019-11-15 242 | 2019-12-25 243 | 2020-01-01 244 | 2020-02-24 245 | 2020-02-25 246 | 2020-04-10 247 | 2020-04-21 248 | 2020-05-01 249 | 2020-06-11 250 | 2020-09-07 251 | 2020-10-12 252 | 2020-11-02 253 | 2020-11-15 254 | 2020-12-25 255 | 2021-01-01 256 | 2021-02-15 257 | 2021-02-16 258 | 2021-04-02 259 | 2021-04-21 260 | 2021-05-01 261 | 2021-06-03 262 | 2021-09-07 263 | 2021-10-12 264 | 2021-11-02 265 | 2021-11-15 266 | 2021-12-25 267 | 2022-01-01 268 | 2022-02-28 269 | 2022-03-01 270 | 2022-04-15 271 | 2022-04-21 272 | 2022-05-01 273 | 2022-06-16 274 | 2022-09-07 275 | 2022-10-12 276 | 2022-11-02 277 | 2022-11-15 278 | 2022-12-25 279 | 2023-01-01 280 | 2023-02-20 281 | 2023-02-21 282 | 2023-04-07 283 | 2023-04-21 284 | 2023-05-01 285 | 2023-06-08 286 | 2023-09-07 287 | 2023-10-12 288 | 2023-11-02 289 | 2023-11-15 290 | 2023-12-25 291 | 2024-01-01 292 | 2024-02-12 293 | 2024-02-13 294 | 2024-03-29 295 | 2024-04-21 296 | 2024-05-01 297 | 2024-05-30 298 | 2024-09-07 299 | 2024-10-12 300 | 2024-11-02 301 | 2024-11-15 302 | 2024-11-20 303 | 2024-12-25 304 | 2025-01-01 305 | 2025-03-03 306 | 2025-03-04 307 | 2025-04-18 308 | 2025-04-21 309 | 2025-05-01 310 | 2025-06-19 311 | 2025-09-07 312 | 2025-10-12 313 | 2025-11-02 314 | 2025-11-15 315 | 2025-11-20 316 | 2025-12-25 317 | 2026-01-01 318 | 2026-02-16 319 | 2026-02-17 320 | 2026-04-03 321 | 2026-04-21 322 | 2026-05-01 323 | 2026-06-04 324 | 2026-09-07 325 | 2026-10-12 326 | 2026-11-02 327 | 2026-11-15 328 | 2026-11-20 329 | 2026-12-25 330 | 2027-01-01 331 | 2027-02-08 332 | 2027-02-09 333 | 2027-03-26 334 | 2027-04-21 335 | 2027-05-01 336 | 2027-05-27 337 | 2027-09-07 338 | 2027-10-12 339 | 2027-11-02 340 | 2027-11-15 341 | 2027-11-20 342 | 2027-12-25 343 | 2028-01-01 344 | 2028-02-28 345 | 2028-02-29 346 | 2028-04-14 347 | 2028-04-21 348 | 2028-05-01 349 | 2028-06-15 350 | 2028-09-07 351 | 2028-10-12 352 | 2028-11-02 353 | 2028-11-15 354 | 2028-11-20 355 | 2028-12-25 356 | 2029-01-01 357 | 2029-02-12 358 | 2029-02-13 359 | 2029-03-30 360 | 2029-04-21 361 | 2029-05-01 362 | 2029-05-31 363 | 2029-09-07 364 | 2029-10-12 365 | 2029-11-02 366 | 2029-11-15 367 | 2029-11-20 368 | 2029-12-25 369 | 2030-01-01 370 | 2030-03-04 371 | 2030-03-05 372 | 2030-04-19 373 | 2030-04-21 374 | 2030-05-01 375 | 2030-06-20 376 | 2030-09-07 377 | 2030-10-12 378 | 2030-11-02 379 | 2030-11-15 380 | 2030-11-20 381 | 2030-12-25 382 | 2031-01-01 383 | 2031-02-24 384 | 2031-02-25 385 | 2031-04-11 386 | 2031-04-21 387 | 2031-05-01 388 | 2031-06-12 389 | 2031-09-07 390 | 2031-10-12 391 | 2031-11-02 392 | 2031-11-15 393 | 2031-11-20 394 | 2031-12-25 395 | 2032-01-01 396 | 2032-02-09 397 | 2032-02-10 398 | 2032-03-26 399 | 2032-04-21 400 | 2032-05-01 401 | 2032-05-27 402 | 2032-09-07 403 | 2032-10-12 404 | 2032-11-02 405 | 2032-11-15 406 | 2032-11-20 407 | 2032-12-25 408 | 2033-01-01 409 | 2033-02-28 410 | 2033-03-01 411 | 2033-04-15 412 | 2033-04-21 413 | 2033-05-01 414 | 2033-06-16 415 | 2033-09-07 416 | 2033-10-12 417 | 2033-11-02 418 | 2033-11-15 419 | 2033-11-20 420 | 2033-12-25 421 | 2034-01-01 422 | 2034-02-20 423 | 2034-02-21 424 | 2034-04-07 425 | 2034-04-21 426 | 2034-05-01 427 | 2034-06-08 428 | 2034-09-07 429 | 2034-10-12 430 | 2034-11-02 431 | 2034-11-15 432 | 2034-11-20 433 | 2034-12-25 434 | 2035-01-01 435 | 2035-02-05 436 | 2035-02-06 437 | 2035-03-23 438 | 2035-04-21 439 | 2035-05-01 440 | 2035-05-24 441 | 2035-09-07 442 | 2035-10-12 443 | 2035-11-02 444 | 2035-11-15 445 | 2035-11-20 446 | 2035-12-25 447 | 2036-01-01 448 | 2036-02-25 449 | 2036-02-26 450 | 2036-04-11 451 | 2036-04-21 452 | 2036-05-01 453 | 2036-06-12 454 | 2036-09-07 455 | 2036-10-12 456 | 2036-11-02 457 | 2036-11-15 458 | 2036-11-20 459 | 2036-12-25 460 | 2037-01-01 461 | 2037-02-16 462 | 2037-02-17 463 | 2037-04-03 464 | 2037-04-21 465 | 2037-05-01 466 | 2037-06-04 467 | 2037-09-07 468 | 2037-10-12 469 | 2037-11-02 470 | 2037-11-15 471 | 2037-11-20 472 | 2037-12-25 473 | 2038-01-01 474 | 2038-03-08 475 | 2038-03-09 476 | 2038-04-21 477 | 2038-04-23 478 | 2038-05-01 479 | 2038-06-24 480 | 2038-09-07 481 | 2038-10-12 482 | 2038-11-02 483 | 2038-11-15 484 | 2038-11-20 485 | 2038-12-25 486 | 2039-01-01 487 | 2039-02-21 488 | 2039-02-22 489 | 2039-04-08 490 | 2039-04-21 491 | 2039-05-01 492 | 2039-06-09 493 | 2039-09-07 494 | 2039-10-12 495 | 2039-11-02 496 | 2039-11-15 497 | 2039-11-20 498 | 2039-12-25 499 | 2040-01-01 500 | 2040-02-13 501 | 2040-02-14 502 | 2040-03-30 503 | 2040-04-21 504 | 2040-05-01 505 | 2040-05-31 506 | 2040-09-07 507 | 2040-10-12 508 | 2040-11-02 509 | 2040-11-15 510 | 2040-11-20 511 | 2040-12-25 512 | 2041-01-01 513 | 2041-03-04 514 | 2041-03-05 515 | 2041-04-19 516 | 2041-04-21 517 | 2041-05-01 518 | 2041-06-20 519 | 2041-09-07 520 | 2041-10-12 521 | 2041-11-02 522 | 2041-11-15 523 | 2041-11-20 524 | 2041-12-25 525 | 2042-01-01 526 | 2042-02-17 527 | 2042-02-18 528 | 2042-04-04 529 | 2042-04-21 530 | 2042-05-01 531 | 2042-06-05 532 | 2042-09-07 533 | 2042-10-12 534 | 2042-11-02 535 | 2042-11-15 536 | 2042-11-20 537 | 2042-12-25 538 | 2043-01-01 539 | 2043-02-09 540 | 2043-02-10 541 | 2043-03-27 542 | 2043-04-21 543 | 2043-05-01 544 | 2043-05-28 545 | 2043-09-07 546 | 2043-10-12 547 | 2043-11-02 548 | 2043-11-15 549 | 2043-11-20 550 | 2043-12-25 551 | 2044-01-01 552 | 2044-02-29 553 | 2044-03-01 554 | 2044-04-15 555 | 2044-04-21 556 | 2044-05-01 557 | 2044-06-16 558 | 2044-09-07 559 | 2044-10-12 560 | 2044-11-02 561 | 2044-11-15 562 | 2044-11-20 563 | 2044-12-25 564 | 2045-01-01 565 | 2045-02-20 566 | 2045-02-21 567 | 2045-04-07 568 | 2045-04-21 569 | 2045-05-01 570 | 2045-06-08 571 | 2045-09-07 572 | 2045-10-12 573 | 2045-11-02 574 | 2045-11-15 575 | 2045-11-20 576 | 2045-12-25 577 | 2046-01-01 578 | 2046-02-05 579 | 2046-02-06 580 | 2046-03-23 581 | 2046-04-21 582 | 2046-05-01 583 | 2046-05-24 584 | 2046-09-07 585 | 2046-10-12 586 | 2046-11-02 587 | 2046-11-15 588 | 2046-11-20 589 | 2046-12-25 590 | 2047-01-01 591 | 2047-02-25 592 | 2047-02-26 593 | 2047-04-12 594 | 2047-04-21 595 | 2047-05-01 596 | 2047-06-13 597 | 2047-09-07 598 | 2047-10-12 599 | 2047-11-02 600 | 2047-11-15 601 | 2047-11-20 602 | 2047-12-25 603 | 2048-01-01 604 | 2048-02-17 605 | 2048-02-18 606 | 2048-04-03 607 | 2048-04-21 608 | 2048-05-01 609 | 2048-06-04 610 | 2048-09-07 611 | 2048-10-12 612 | 2048-11-02 613 | 2048-11-15 614 | 2048-11-20 615 | 2048-12-25 616 | 2049-01-01 617 | 2049-03-01 618 | 2049-03-02 619 | 2049-04-16 620 | 2049-04-21 621 | 2049-05-01 622 | 2049-06-17 623 | 2049-09-07 624 | 2049-10-12 625 | 2049-11-02 626 | 2049-11-15 627 | 2049-11-20 628 | 2049-12-25 629 | 2050-01-01 630 | 2050-02-21 631 | 2050-02-22 632 | 2050-04-08 633 | 2050-04-21 634 | 2050-05-01 635 | 2050-06-09 636 | 2050-09-07 637 | 2050-10-12 638 | 2050-11-02 639 | 2050-11-15 640 | 2050-11-20 641 | 2050-12-25 642 | 2051-01-01 643 | 2051-02-13 644 | 2051-02-14 645 | 2051-03-31 646 | 2051-04-21 647 | 2051-05-01 648 | 2051-06-01 649 | 2051-09-07 650 | 2051-10-12 651 | 2051-11-02 652 | 2051-11-15 653 | 2051-11-20 654 | 2051-12-25 655 | 2052-01-01 656 | 2052-03-04 657 | 2052-03-05 658 | 2052-04-19 659 | 2052-04-21 660 | 2052-05-01 661 | 2052-06-20 662 | 2052-09-07 663 | 2052-10-12 664 | 2052-11-02 665 | 2052-11-15 666 | 2052-11-20 667 | 2052-12-25 668 | 2053-01-01 669 | 2053-02-17 670 | 2053-02-18 671 | 2053-04-04 672 | 2053-04-21 673 | 2053-05-01 674 | 2053-06-05 675 | 2053-09-07 676 | 2053-10-12 677 | 2053-11-02 678 | 2053-11-15 679 | 2053-11-20 680 | 2053-12-25 681 | 2054-01-01 682 | 2054-02-09 683 | 2054-02-10 684 | 2054-03-27 685 | 2054-04-21 686 | 2054-05-01 687 | 2054-05-28 688 | 2054-09-07 689 | 2054-10-12 690 | 2054-11-02 691 | 2054-11-15 692 | 2054-11-20 693 | 2054-12-25 694 | 2055-01-01 695 | 2055-03-01 696 | 2055-03-02 697 | 2055-04-16 698 | 2055-04-21 699 | 2055-05-01 700 | 2055-06-17 701 | 2055-09-07 702 | 2055-10-12 703 | 2055-11-02 704 | 2055-11-15 705 | 2055-11-20 706 | 2055-12-25 707 | 2056-01-01 708 | 2056-02-14 709 | 2056-02-15 710 | 2056-03-31 711 | 2056-04-21 712 | 2056-05-01 713 | 2056-06-01 714 | 2056-09-07 715 | 2056-10-12 716 | 2056-11-02 717 | 2056-11-15 718 | 2056-11-20 719 | 2056-12-25 720 | 2057-01-01 721 | 2057-03-05 722 | 2057-03-06 723 | 2057-04-20 724 | 2057-04-21 725 | 2057-05-01 726 | 2057-06-21 727 | 2057-09-07 728 | 2057-10-12 729 | 2057-11-02 730 | 2057-11-15 731 | 2057-11-20 732 | 2057-12-25 733 | 2058-01-01 734 | 2058-02-25 735 | 2058-02-26 736 | 2058-04-12 737 | 2058-04-21 738 | 2058-05-01 739 | 2058-06-13 740 | 2058-09-07 741 | 2058-10-12 742 | 2058-11-02 743 | 2058-11-15 744 | 2058-11-20 745 | 2058-12-25 746 | 2059-01-01 747 | 2059-02-10 748 | 2059-02-11 749 | 2059-03-28 750 | 2059-04-21 751 | 2059-05-01 752 | 2059-05-29 753 | 2059-09-07 754 | 2059-10-12 755 | 2059-11-02 756 | 2059-11-15 757 | 2059-11-20 758 | 2059-12-25 759 | 2060-01-01 760 | 2060-03-01 761 | 2060-03-02 762 | 2060-04-16 763 | 2060-04-21 764 | 2060-05-01 765 | 2060-06-17 766 | 2060-09-07 767 | 2060-10-12 768 | 2060-11-02 769 | 2060-11-15 770 | 2060-11-20 771 | 2060-12-25 772 | 2061-01-01 773 | 2061-02-21 774 | 2061-02-22 775 | 2061-04-08 776 | 2061-04-21 777 | 2061-05-01 778 | 2061-06-09 779 | 2061-09-07 780 | 2061-10-12 781 | 2061-11-02 782 | 2061-11-15 783 | 2061-11-20 784 | 2061-12-25 785 | 2062-01-01 786 | 2062-02-06 787 | 2062-02-07 788 | 2062-03-24 789 | 2062-04-21 790 | 2062-05-01 791 | 2062-05-25 792 | 2062-09-07 793 | 2062-10-12 794 | 2062-11-02 795 | 2062-11-15 796 | 2062-11-20 797 | 2062-12-25 798 | 2063-01-01 799 | 2063-02-26 800 | 2063-02-27 801 | 2063-04-13 802 | 2063-04-21 803 | 2063-05-01 804 | 2063-06-14 805 | 2063-09-07 806 | 2063-10-12 807 | 2063-11-02 808 | 2063-11-15 809 | 2063-11-20 810 | 2063-12-25 811 | 2064-01-01 812 | 2064-02-18 813 | 2064-02-19 814 | 2064-04-04 815 | 2064-04-21 816 | 2064-05-01 817 | 2064-06-05 818 | 2064-09-07 819 | 2064-10-12 820 | 2064-11-02 821 | 2064-11-15 822 | 2064-11-20 823 | 2064-12-25 824 | 2065-01-01 825 | 2065-02-09 826 | 2065-02-10 827 | 2065-03-27 828 | 2065-04-21 829 | 2065-05-01 830 | 2065-05-28 831 | 2065-09-07 832 | 2065-10-12 833 | 2065-11-02 834 | 2065-11-15 835 | 2065-11-20 836 | 2065-12-25 837 | 2066-01-01 838 | 2066-02-22 839 | 2066-02-23 840 | 2066-04-09 841 | 2066-04-21 842 | 2066-05-01 843 | 2066-06-10 844 | 2066-09-07 845 | 2066-10-12 846 | 2066-11-02 847 | 2066-11-15 848 | 2066-11-20 849 | 2066-12-25 850 | 2067-01-01 851 | 2067-02-14 852 | 2067-02-15 853 | 2067-04-01 854 | 2067-04-21 855 | 2067-05-01 856 | 2067-06-02 857 | 2067-09-07 858 | 2067-10-12 859 | 2067-11-02 860 | 2067-11-15 861 | 2067-11-20 862 | 2067-12-25 863 | 2068-01-01 864 | 2068-03-05 865 | 2068-03-06 866 | 2068-04-20 867 | 2068-04-21 868 | 2068-05-01 869 | 2068-06-21 870 | 2068-09-07 871 | 2068-10-12 872 | 2068-11-02 873 | 2068-11-15 874 | 2068-11-20 875 | 2068-12-25 876 | 2069-01-01 877 | 2069-02-25 878 | 2069-02-26 879 | 2069-04-12 880 | 2069-04-21 881 | 2069-05-01 882 | 2069-06-13 883 | 2069-09-07 884 | 2069-10-12 885 | 2069-11-02 886 | 2069-11-15 887 | 2069-11-20 888 | 2069-12-25 889 | 2070-01-01 890 | 2070-02-10 891 | 2070-02-11 892 | 2070-03-28 893 | 2070-04-21 894 | 2070-05-01 895 | 2070-05-29 896 | 2070-09-07 897 | 2070-10-12 898 | 2070-11-02 899 | 2070-11-15 900 | 2070-11-20 901 | 2070-12-25 902 | 2071-01-01 903 | 2071-03-02 904 | 2071-03-03 905 | 2071-04-17 906 | 2071-04-21 907 | 2071-05-01 908 | 2071-06-18 909 | 2071-09-07 910 | 2071-10-12 911 | 2071-11-02 912 | 2071-11-15 913 | 2071-11-20 914 | 2071-12-25 915 | 2072-01-01 916 | 2072-02-22 917 | 2072-02-23 918 | 2072-04-08 919 | 2072-04-21 920 | 2072-05-01 921 | 2072-06-09 922 | 2072-09-07 923 | 2072-10-12 924 | 2072-11-02 925 | 2072-11-15 926 | 2072-11-20 927 | 2072-12-25 928 | 2073-01-01 929 | 2073-02-06 930 | 2073-02-07 931 | 2073-03-24 932 | 2073-04-21 933 | 2073-05-01 934 | 2073-05-25 935 | 2073-09-07 936 | 2073-10-12 937 | 2073-11-02 938 | 2073-11-15 939 | 2073-11-20 940 | 2073-12-25 941 | 2074-01-01 942 | 2074-02-26 943 | 2074-02-27 944 | 2074-04-13 945 | 2074-04-21 946 | 2074-05-01 947 | 2074-06-14 948 | 2074-09-07 949 | 2074-10-12 950 | 2074-11-02 951 | 2074-11-15 952 | 2074-11-20 953 | 2074-12-25 954 | 2075-01-01 955 | 2075-02-18 956 | 2075-02-19 957 | 2075-04-05 958 | 2075-04-21 959 | 2075-05-01 960 | 2075-06-06 961 | 2075-09-07 962 | 2075-10-12 963 | 2075-11-02 964 | 2075-11-15 965 | 2075-11-20 966 | 2075-12-25 967 | 2076-01-01 968 | 2076-03-02 969 | 2076-03-03 970 | 2076-04-17 971 | 2076-04-21 972 | 2076-05-01 973 | 2076-06-18 974 | 2076-09-07 975 | 2076-10-12 976 | 2076-11-02 977 | 2076-11-15 978 | 2076-11-20 979 | 2076-12-25 980 | 2077-01-01 981 | 2077-02-22 982 | 2077-02-23 983 | 2077-04-09 984 | 2077-04-21 985 | 2077-05-01 986 | 2077-06-10 987 | 2077-09-07 988 | 2077-10-12 989 | 2077-11-02 990 | 2077-11-15 991 | 2077-11-20 992 | 2077-12-25 993 | 2078-01-01 994 | 2078-02-14 995 | 2078-02-15 996 | 2078-04-01 997 | 2078-04-21 998 | 2078-05-01 999 | 2078-06-02 1000 | 2078-09-07 1001 | 2078-10-12 1002 | 2078-11-02 1003 | 2078-11-15 1004 | 2078-11-20 1005 | 2078-12-25 1006 | 2079-01-01 1007 | 2079-03-06 1008 | 2079-03-07 1009 | 2079-04-21 1010 | 2079-04-21 1011 | 2079-05-01 1012 | 2079-06-22 1013 | 2079-09-07 1014 | 2079-10-12 1015 | 2079-11-02 1016 | 2079-11-15 1017 | 2079-11-20 1018 | 2079-12-25 1019 | 2080-01-01 1020 | 2080-02-19 1021 | 2080-02-20 1022 | 2080-04-05 1023 | 2080-04-21 1024 | 2080-05-01 1025 | 2080-06-06 1026 | 2080-09-07 1027 | 2080-10-12 1028 | 2080-11-02 1029 | 2080-11-15 1030 | 2080-11-20 1031 | 2080-12-25 1032 | 2081-01-01 1033 | 2081-02-10 1034 | 2081-02-11 1035 | 2081-03-28 1036 | 2081-04-21 1037 | 2081-05-01 1038 | 2081-05-29 1039 | 2081-09-07 1040 | 2081-10-12 1041 | 2081-11-02 1042 | 2081-11-15 1043 | 2081-11-20 1044 | 2081-12-25 1045 | 2082-01-01 1046 | 2082-03-02 1047 | 2082-03-03 1048 | 2082-04-17 1049 | 2082-04-21 1050 | 2082-05-01 1051 | 2082-06-18 1052 | 2082-09-07 1053 | 2082-10-12 1054 | 2082-11-02 1055 | 2082-11-15 1056 | 2082-11-20 1057 | 2082-12-25 1058 | 2083-01-01 1059 | 2083-02-15 1060 | 2083-02-16 1061 | 2083-04-02 1062 | 2083-04-21 1063 | 2083-05-01 1064 | 2083-06-03 1065 | 2083-09-07 1066 | 2083-10-12 1067 | 2083-11-02 1068 | 2083-11-15 1069 | 2083-11-20 1070 | 2083-12-25 1071 | 2084-01-01 1072 | 2084-02-07 1073 | 2084-02-08 1074 | 2084-03-24 1075 | 2084-04-21 1076 | 2084-05-01 1077 | 2084-05-25 1078 | 2084-09-07 1079 | 2084-10-12 1080 | 2084-11-02 1081 | 2084-11-15 1082 | 2084-11-20 1083 | 2084-12-25 1084 | 2085-01-01 1085 | 2085-02-26 1086 | 2085-02-27 1087 | 2085-04-13 1088 | 2085-04-21 1089 | 2085-05-01 1090 | 2085-06-14 1091 | 2085-09-07 1092 | 2085-10-12 1093 | 2085-11-02 1094 | 2085-11-15 1095 | 2085-11-20 1096 | 2085-12-25 1097 | 2086-01-01 1098 | 2086-02-11 1099 | 2086-02-12 1100 | 2086-03-29 1101 | 2086-04-21 1102 | 2086-05-01 1103 | 2086-05-30 1104 | 2086-09-07 1105 | 2086-10-12 1106 | 2086-11-02 1107 | 2086-11-15 1108 | 2086-11-20 1109 | 2086-12-25 1110 | 2087-01-01 1111 | 2087-03-03 1112 | 2087-03-04 1113 | 2087-04-18 1114 | 2087-04-21 1115 | 2087-05-01 1116 | 2087-06-19 1117 | 2087-09-07 1118 | 2087-10-12 1119 | 2087-11-02 1120 | 2087-11-15 1121 | 2087-11-20 1122 | 2087-12-25 1123 | 2088-01-01 1124 | 2088-02-23 1125 | 2088-02-24 1126 | 2088-04-09 1127 | 2088-04-21 1128 | 2088-05-01 1129 | 2088-06-10 1130 | 2088-09-07 1131 | 2088-10-12 1132 | 2088-11-02 1133 | 2088-11-15 1134 | 2088-11-20 1135 | 2088-12-25 1136 | 2089-01-01 1137 | 2089-02-14 1138 | 2089-02-15 1139 | 2089-04-01 1140 | 2089-04-21 1141 | 2089-05-01 1142 | 2089-06-02 1143 | 2089-09-07 1144 | 2089-10-12 1145 | 2089-11-02 1146 | 2089-11-15 1147 | 2089-11-20 1148 | 2089-12-25 1149 | 2090-01-01 1150 | 2090-02-27 1151 | 2090-02-28 1152 | 2090-04-14 1153 | 2090-04-21 1154 | 2090-05-01 1155 | 2090-06-15 1156 | 2090-09-07 1157 | 2090-10-12 1158 | 2090-11-02 1159 | 2090-11-15 1160 | 2090-11-20 1161 | 2090-12-25 1162 | 2091-01-01 1163 | 2091-02-19 1164 | 2091-02-20 1165 | 2091-04-06 1166 | 2091-04-21 1167 | 2091-05-01 1168 | 2091-06-07 1169 | 2091-09-07 1170 | 2091-10-12 1171 | 2091-11-02 1172 | 2091-11-15 1173 | 2091-11-20 1174 | 2091-12-25 1175 | 2092-01-01 1176 | 2092-02-11 1177 | 2092-02-12 1178 | 2092-03-28 1179 | 2092-04-21 1180 | 2092-05-01 1181 | 2092-05-29 1182 | 2092-09-07 1183 | 2092-10-12 1184 | 2092-11-02 1185 | 2092-11-15 1186 | 2092-11-20 1187 | 2092-12-25 1188 | 2093-01-01 1189 | 2093-02-23 1190 | 2093-02-24 1191 | 2093-04-10 1192 | 2093-04-21 1193 | 2093-05-01 1194 | 2093-06-11 1195 | 2093-09-07 1196 | 2093-10-12 1197 | 2093-11-02 1198 | 2093-11-15 1199 | 2093-11-20 1200 | 2093-12-25 1201 | 2094-01-01 1202 | 2094-02-15 1203 | 2094-02-16 1204 | 2094-04-02 1205 | 2094-04-21 1206 | 2094-05-01 1207 | 2094-06-03 1208 | 2094-09-07 1209 | 2094-10-12 1210 | 2094-11-02 1211 | 2094-11-15 1212 | 2094-11-20 1213 | 2094-12-25 1214 | 2095-01-01 1215 | 2095-03-07 1216 | 2095-03-08 1217 | 2095-04-21 1218 | 2095-04-22 1219 | 2095-05-01 1220 | 2095-06-23 1221 | 2095-09-07 1222 | 2095-10-12 1223 | 2095-11-02 1224 | 2095-11-15 1225 | 2095-11-20 1226 | 2095-12-25 1227 | 2096-01-01 1228 | 2096-02-27 1229 | 2096-02-28 1230 | 2096-04-13 1231 | 2096-04-21 1232 | 2096-05-01 1233 | 2096-06-14 1234 | 2096-09-07 1235 | 2096-10-12 1236 | 2096-11-02 1237 | 2096-11-15 1238 | 2096-11-20 1239 | 2096-12-25 1240 | 2097-01-01 1241 | 2097-02-11 1242 | 2097-02-12 1243 | 2097-03-29 1244 | 2097-04-21 1245 | 2097-05-01 1246 | 2097-05-30 1247 | 2097-09-07 1248 | 2097-10-12 1249 | 2097-11-02 1250 | 2097-11-15 1251 | 2097-11-20 1252 | 2097-12-25 1253 | 2098-01-01 1254 | 2098-03-03 1255 | 2098-03-04 1256 | 2098-04-18 1257 | 2098-04-21 1258 | 2098-05-01 1259 | 2098-06-19 1260 | 2098-09-07 1261 | 2098-10-12 1262 | 2098-11-02 1263 | 2098-11-15 1264 | 2098-11-20 1265 | 2098-12-25 1266 | 2099-01-01 1267 | 2099-02-23 1268 | 2099-02-24 1269 | 2099-04-10 1270 | 2099-04-21 1271 | 2099-05-01 1272 | 2099-06-11 1273 | 2099-09-07 1274 | 2099-10-12 1275 | 2099-11-02 1276 | 2099-11-15 1277 | 2099-11-20 1278 | 2099-12-25 1279 | 1280 | 1281 | -------------------------------------------------------------------------------- /pandas.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# pandas Integration\n", 8 | "\n", 9 | "`bizdays` has a very handy integration with pandas `DataFrame`." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "from bizdays import set_option, Calendar\n", 19 | "\n", 20 | "set_option('mode', 'pandas')" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "When the option `mode` is set to `pandas` the calendar methods will return\n", 28 | "\n", 29 | "- `Timestamp` instead of `datetime.date`\n", 30 | "- `DatetimeIndex` instead of lists of dates\n", 31 | "- `numpy.ndarray` instead of lists of integers (`bizdays`) and bools (`isbizday`)" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 2, 37 | "metadata": {}, 38 | "outputs": [ 39 | { 40 | "data": { 41 | "text/plain": [ 42 | "Calendar: ANBIMA\n", 43 | "Start: 2000-01-01\n", 44 | "End: 2078-12-25\n", 45 | "Weekdays: Saturday, Sunday\n", 46 | "Holidays: 948\n", 47 | "Financial: True" 48 | ] 49 | }, 50 | "execution_count": 2, 51 | "metadata": {}, 52 | "output_type": "execute_result" 53 | } 54 | ], 55 | "source": [ 56 | "cal = Calendar.load(filename='ANBIMA.cal')\n", 57 | "cal" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 3, 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "data": { 67 | "text/plain": [ 68 | "array([False, True])" 69 | ] 70 | }, 71 | "execution_count": 3, 72 | "metadata": {}, 73 | "output_type": "execute_result" 74 | } 75 | ], 76 | "source": [ 77 | "cal.isbizday(['2014-01-12', '2014-01-13'])" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 4, 83 | "metadata": {}, 84 | "outputs": [ 85 | { 86 | "data": { 87 | "text/plain": [ 88 | "array([253, 503, ], dtype=object)" 89 | ] 90 | }, 91 | "execution_count": 4, 92 | "metadata": {}, 93 | "output_type": "execute_result" 94 | } 95 | ], 96 | "source": [ 97 | "cal.bizdays('2014-01-13', ['2015-01-13', '2016-01-13', None])" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 5, 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "data": { 107 | "text/plain": [ 108 | "Timestamp('2015-12-28 00:00:00')" 109 | ] 110 | }, 111 | "execution_count": 5, 112 | "metadata": {}, 113 | "output_type": "execute_result" 114 | } 115 | ], 116 | "source": [ 117 | "cal.following('2015-12-25')" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 6, 123 | "metadata": {}, 124 | "outputs": [ 125 | { 126 | "data": { 127 | "text/plain": [ 128 | "DatetimeIndex(['2015-12-28', '2015-12-28'], dtype='datetime64[ns]', freq=None)" 129 | ] 130 | }, 131 | "execution_count": 6, 132 | "metadata": {}, 133 | "output_type": "execute_result" 134 | } 135 | ], 136 | "source": [ 137 | "cal.following(['2015-12-28', '2015-12-25'])" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 7, 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "text/plain": [ 148 | "DatetimeIndex(['2014-01-02', '2014-01-03', '2014-01-06', '2014-01-07'], dtype='datetime64[ns]', freq=None)" 149 | ] 150 | }, 151 | "execution_count": 7, 152 | "metadata": {}, 153 | "output_type": "execute_result" 154 | } 155 | ], 156 | "source": [ 157 | "cal.seq('2014-01-02', '2014-01-07')" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 8, 163 | "metadata": {}, 164 | "outputs": [ 165 | { 166 | "data": { 167 | "text/plain": [ 168 | "DatetimeIndex(['2014-01-02', '2014-01-03', '2014-01-06', '2014-01-07',\n", 169 | " '2014-01-08', '2014-01-09', '2014-01-10', '2014-01-13',\n", 170 | " '2014-01-14', '2014-01-15'],\n", 171 | " dtype='datetime64[ns]', freq=None)" 172 | ] 173 | }, 174 | "execution_count": 8, 175 | "metadata": {}, 176 | "output_type": "execute_result" 177 | } 178 | ], 179 | "source": [ 180 | "cal.offset('2014-01-02', range(10))" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 9, 186 | "metadata": {}, 187 | "outputs": [ 188 | { 189 | "data": { 190 | "text/plain": [ 191 | "Timestamp('2002-05-22 00:00:00')" 192 | ] 193 | }, 194 | "execution_count": 9, 195 | "metadata": {}, 196 | "output_type": "execute_result" 197 | } 198 | ], 199 | "source": [ 200 | "cal.getdate('15th bizday', 2002, 5)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 10, 206 | "metadata": {}, 207 | "outputs": [ 208 | { 209 | "data": { 210 | "text/plain": [ 211 | "DatetimeIndex(['2002-01-15', '2002-02-15', '2002-03-15', '2002-04-15',\n", 212 | " '2002-05-15', '2002-06-15', '2002-07-15', '2002-08-15',\n", 213 | " '2002-09-15', '2002-10-15', '2002-11-15', '2002-12-15'],\n", 214 | " dtype='datetime64[ns]', freq=None)" 215 | ] 216 | }, 217 | "execution_count": 10, 218 | "metadata": {}, 219 | "output_type": "execute_result" 220 | } 221 | ], 222 | "source": [ 223 | "cal.getdate('15th day', 2002, range(1, 13))" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 11, 229 | "metadata": {}, 230 | "outputs": [ 231 | { 232 | "data": { 233 | "text/plain": [ 234 | "array([20, 18, 23, 20, 21, 21, 22, 22, 21, 20, 20, 23])" 235 | ] 236 | }, 237 | "execution_count": 11, 238 | "metadata": {}, 239 | "output_type": "execute_result" 240 | } 241 | ], 242 | "source": [ 243 | "cal.getbizdays(2021, range(1, 13))" 244 | ] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "metadata": {}, 249 | "source": [ 250 | "## Example: series of payments\n", 251 | "\n", 252 | "Let's create a series of payment dates and compute the business days between each payment date.\n", 253 | "\n", 254 | "The payments are scheduled to occur in the 15th days of each month of 2021.\n", 255 | "\n", 256 | "Start creating a DataFrame with year and month columns." 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": 12, 262 | "metadata": {}, 263 | "outputs": [ 264 | { 265 | "data": { 266 | "text/html": [ 267 | "
\n", 268 | "\n", 281 | "\n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | "
yearmonth
020211
120212
220213
320214
420215
520216
620217
720218
820219
9202110
10202111
11202112
\n", 352 | "
" 353 | ], 354 | "text/plain": [ 355 | " year month\n", 356 | "0 2021 1\n", 357 | "1 2021 2\n", 358 | "2 2021 3\n", 359 | "3 2021 4\n", 360 | "4 2021 5\n", 361 | "5 2021 6\n", 362 | "6 2021 7\n", 363 | "7 2021 8\n", 364 | "8 2021 9\n", 365 | "9 2021 10\n", 366 | "10 2021 11\n", 367 | "11 2021 12" 368 | ] 369 | }, 370 | "execution_count": 12, 371 | "metadata": {}, 372 | "output_type": "execute_result" 373 | } 374 | ], 375 | "source": [ 376 | "import pandas as pd\n", 377 | "\n", 378 | "df = pd.DataFrame({\n", 379 | " 'year': 2021,\n", 380 | " 'month': range(1,13)\n", 381 | "})\n", 382 | "df" 383 | ] 384 | }, 385 | { 386 | "cell_type": "markdown", 387 | "metadata": {}, 388 | "source": [ 389 | "Use `getdate` to get the payment dates." 390 | ] 391 | }, 392 | { 393 | "cell_type": "code", 394 | "execution_count": 13, 395 | "metadata": {}, 396 | "outputs": [ 397 | { 398 | "data": { 399 | "text/html": [ 400 | "
\n", 401 | "\n", 414 | "\n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 419 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 424 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | "
yearmonthpayment_dates
0202112021-01-15
1202122021-02-15
2202132021-03-15
3202142021-04-15
4202152021-05-15
5202162021-06-15
6202172021-07-15
7202182021-08-15
8202192021-09-15
92021102021-10-15
102021112021-11-15
112021122021-12-15
\n", 498 | "
" 499 | ], 500 | "text/plain": [ 501 | " year month payment_dates\n", 502 | "0 2021 1 2021-01-15\n", 503 | "1 2021 2 2021-02-15\n", 504 | "2 2021 3 2021-03-15\n", 505 | "3 2021 4 2021-04-15\n", 506 | "4 2021 5 2021-05-15\n", 507 | "5 2021 6 2021-06-15\n", 508 | "6 2021 7 2021-07-15\n", 509 | "7 2021 8 2021-08-15\n", 510 | "8 2021 9 2021-09-15\n", 511 | "9 2021 10 2021-10-15\n", 512 | "10 2021 11 2021-11-15\n", 513 | "11 2021 12 2021-12-15" 514 | ] 515 | }, 516 | "execution_count": 13, 517 | "metadata": {}, 518 | "output_type": "execute_result" 519 | } 520 | ], 521 | "source": [ 522 | "df['payment_dates'] = cal.getdate('15th day', df['year'], df['month'])\n", 523 | "df" 524 | ] 525 | }, 526 | { 527 | "cell_type": "markdown", 528 | "metadata": {}, 529 | "source": [ 530 | "The payments happen in business days so the dates must be shifted to the next working date." 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 14, 536 | "metadata": {}, 537 | "outputs": [ 538 | { 539 | "data": { 540 | "text/html": [ 541 | "
\n", 542 | "\n", 555 | "\n", 556 | " \n", 557 | " \n", 558 | " \n", 559 | " \n", 560 | " \n", 561 | " \n", 562 | " \n", 563 | " \n", 564 | " \n", 565 | " \n", 566 | " \n", 567 | " \n", 568 | " \n", 569 | " \n", 570 | " \n", 571 | " \n", 572 | " \n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " \n", 584 | " \n", 585 | " \n", 586 | " \n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | " \n", 614 | " \n", 615 | " \n", 616 | " \n", 617 | " \n", 618 | " \n", 619 | " \n", 620 | " \n", 621 | " \n", 622 | " \n", 623 | " \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " \n", 628 | " \n", 629 | " \n", 630 | " \n", 631 | " \n", 632 | " \n", 633 | " \n", 634 | " \n", 635 | " \n", 636 | " \n", 637 | " \n", 638 | " \n", 639 | " \n", 640 | " \n", 641 | " \n", 642 | " \n", 643 | " \n", 644 | " \n", 645 | " \n", 646 | " \n", 647 | " \n", 648 | " \n", 649 | " \n", 650 | " \n", 651 | "
yearmonthpayment_datesfixing
0202112021-01-152021-01-15
1202122021-02-152021-02-17
2202132021-03-152021-03-15
3202142021-04-152021-04-15
4202152021-05-152021-05-17
5202162021-06-152021-06-15
6202172021-07-152021-07-15
7202182021-08-152021-08-16
8202192021-09-152021-09-15
92021102021-10-152021-10-15
102021112021-11-152021-11-16
112021122021-12-152021-12-15
\n", 652 | "
" 653 | ], 654 | "text/plain": [ 655 | " year month payment_dates fixing\n", 656 | "0 2021 1 2021-01-15 2021-01-15\n", 657 | "1 2021 2 2021-02-15 2021-02-17\n", 658 | "2 2021 3 2021-03-15 2021-03-15\n", 659 | "3 2021 4 2021-04-15 2021-04-15\n", 660 | "4 2021 5 2021-05-15 2021-05-17\n", 661 | "5 2021 6 2021-06-15 2021-06-15\n", 662 | "6 2021 7 2021-07-15 2021-07-15\n", 663 | "7 2021 8 2021-08-15 2021-08-16\n", 664 | "8 2021 9 2021-09-15 2021-09-15\n", 665 | "9 2021 10 2021-10-15 2021-10-15\n", 666 | "10 2021 11 2021-11-15 2021-11-16\n", 667 | "11 2021 12 2021-12-15 2021-12-15" 668 | ] 669 | }, 670 | "execution_count": 14, 671 | "metadata": {}, 672 | "output_type": "execute_result" 673 | } 674 | ], 675 | "source": [ 676 | "df['fixing'] = cal.following(df['payment_dates'])\n", 677 | "df" 678 | ] 679 | }, 680 | { 681 | "cell_type": "markdown", 682 | "metadata": {}, 683 | "source": [ 684 | "`bizdays` compute the amount of business days between each payment." 685 | ] 686 | }, 687 | { 688 | "cell_type": "code", 689 | "execution_count": 15, 690 | "metadata": {}, 691 | "outputs": [ 692 | { 693 | "data": { 694 | "text/html": [ 695 | "
\n", 696 | "\n", 709 | "\n", 710 | " \n", 711 | " \n", 712 | " \n", 713 | " \n", 714 | " \n", 715 | " \n", 716 | " \n", 717 | " \n", 718 | " \n", 719 | " \n", 720 | " \n", 721 | " \n", 722 | " \n", 723 | " \n", 724 | " \n", 725 | " \n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | " \n", 740 | " \n", 741 | " \n", 742 | " \n", 743 | " \n", 744 | " \n", 745 | " \n", 746 | " \n", 747 | " \n", 748 | " \n", 749 | " \n", 750 | " \n", 751 | " \n", 752 | " \n", 753 | " \n", 754 | " \n", 755 | " \n", 756 | " \n", 757 | " \n", 758 | " \n", 759 | " \n", 760 | " \n", 761 | " \n", 762 | " \n", 763 | " \n", 764 | " \n", 765 | " \n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | " \n", 812 | " \n", 813 | " \n", 814 | " \n", 815 | " \n", 816 | " \n", 817 | " \n", 818 | "
yearmonthpayment_datesfixingbusiness_days
0202112021-01-152021-01-15<NA>
1202122021-02-152021-02-1721
2202132021-03-152021-03-1518
3202142021-04-152021-04-1522
4202152021-05-152021-05-1721
5202162021-06-152021-06-1520
6202172021-07-152021-07-1522
7202182021-08-152021-08-1622
8202192021-09-152021-09-1521
92021102021-10-152021-10-1521
102021112021-11-152021-11-1620
112021122021-12-152021-12-1521
\n", 819 | "
" 820 | ], 821 | "text/plain": [ 822 | " year month payment_dates fixing business_days\n", 823 | "0 2021 1 2021-01-15 2021-01-15 \n", 824 | "1 2021 2 2021-02-15 2021-02-17 21\n", 825 | "2 2021 3 2021-03-15 2021-03-15 18\n", 826 | "3 2021 4 2021-04-15 2021-04-15 22\n", 827 | "4 2021 5 2021-05-15 2021-05-17 21\n", 828 | "5 2021 6 2021-06-15 2021-06-15 20\n", 829 | "6 2021 7 2021-07-15 2021-07-15 22\n", 830 | "7 2021 8 2021-08-15 2021-08-16 22\n", 831 | "8 2021 9 2021-09-15 2021-09-15 21\n", 832 | "9 2021 10 2021-10-15 2021-10-15 21\n", 833 | "10 2021 11 2021-11-15 2021-11-16 20\n", 834 | "11 2021 12 2021-12-15 2021-12-15 21" 835 | ] 836 | }, 837 | "execution_count": 15, 838 | "metadata": {}, 839 | "output_type": "execute_result" 840 | } 841 | ], 842 | "source": [ 843 | "df['business_days'] = cal.bizdays(df['fixing'].shift(), df['fixing'])\n", 844 | "df" 845 | ] 846 | } 847 | ], 848 | "metadata": { 849 | "interpreter": { 850 | "hash": "4e6da9785688dca07ac236f8ec5f7e99c2c01a8eecf30ede8ddadf573b26e527" 851 | }, 852 | "kernelspec": { 853 | "display_name": "Python 3", 854 | "language": "python", 855 | "name": "python3" 856 | }, 857 | "language_info": { 858 | "codemirror_mode": { 859 | "name": "ipython", 860 | "version": 3 861 | }, 862 | "file_extension": ".py", 863 | "mimetype": "text/x-python", 864 | "name": "python", 865 | "nbconvert_exporter": "python", 866 | "pygments_lexer": "ipython3", 867 | "version": "3.7.6" 868 | } 869 | }, 870 | "nbformat": 4, 871 | "nbformat_minor": 2 872 | } 873 | -------------------------------------------------------------------------------- /SITE.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import warnings\n", 10 | "warnings.filterwarnings('ignore')" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "**bizdays** computes business days between two dates based on the definition of nonworking days (usually holidays and weekends—nonworking weekdays).\n", 18 | "It also computes other collateral effects like adjust dates for the next or previous business day, check whether a date is a business day, create generators of business days sequences, and so forth.\n", 19 | "\n", 20 | "## Install\n", 21 | "\n", 22 | "**bizdays** is avalilable at PyPI, so it is pip instalable.\n", 23 | "\n", 24 | "\tpip install bizdays\n", 25 | "\n", 26 | "## Using\n", 27 | "\n", 28 | "Business days calculations are done defining a `Calendar` object." 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "data": { 38 | "text/plain": [ 39 | "Calendar: ANBIMA\n", 40 | "Start: 2000-01-01\n", 41 | "End: 2078-12-25\n", 42 | "Holidays: 948\n", 43 | "Financial: True" 44 | ] 45 | }, 46 | "execution_count": 2, 47 | "metadata": {}, 48 | "output_type": "execute_result" 49 | } 50 | ], 51 | "source": [ 52 | "from bizdays import Calendar\n", 53 | "cal = Calendar.load('ANBIMA')\n", 54 | "cal" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "where `holidays` is a sequence of dates which represents nonworking dates and the second argument, `weekdays`, is a sequence with nonworking weekdays.\n", 62 | "`holidays` must be a sequence of strings with ISO formatted dates or `datetime.date` objects and `weekdays` a sequence of weekdays in words.\n", 63 | "\n", 64 | "Once you have a `Calendar` you can" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 3, 70 | "metadata": {}, 71 | "outputs": [ 72 | { 73 | "data": { 74 | "text/plain": [ 75 | "False" 76 | ] 77 | }, 78 | "execution_count": 3, 79 | "metadata": {}, 80 | "output_type": "execute_result" 81 | } 82 | ], 83 | "source": [ 84 | "cal.isbizday('2014-01-12')" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 4, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "data": { 94 | "text/plain": [ 95 | "True" 96 | ] 97 | }, 98 | "execution_count": 4, 99 | "metadata": {}, 100 | "output_type": "execute_result" 101 | } 102 | ], 103 | "source": [ 104 | "cal.isbizday('2014-01-13')" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 5, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "data": { 114 | "text/plain": [ 115 | "253" 116 | ] 117 | }, 118 | "execution_count": 5, 119 | "metadata": {}, 120 | "output_type": "execute_result" 121 | } 122 | ], 123 | "source": [ 124 | "cal.bizdays('2014-01-13', '2015-01-13')" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 6, 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "data": { 134 | "text/plain": [ 135 | "datetime.date(2015, 12, 28)" 136 | ] 137 | }, 138 | "execution_count": 6, 139 | "metadata": {}, 140 | "output_type": "execute_result" 141 | } 142 | ], 143 | "source": [ 144 | "cal.following('2015-12-25')" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 7, 150 | "metadata": {}, 151 | "outputs": [ 152 | { 153 | "data": { 154 | "text/plain": [ 155 | "datetime.date(2015, 12, 28)" 156 | ] 157 | }, 158 | "execution_count": 7, 159 | "metadata": {}, 160 | "output_type": "execute_result" 161 | } 162 | ], 163 | "source": [ 164 | "cal.following('2015-12-28')" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 8, 170 | "metadata": {}, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/plain": [ 175 | "datetime.date(2013, 12, 31)" 176 | ] 177 | }, 178 | "execution_count": 8, 179 | "metadata": {}, 180 | "output_type": "execute_result" 181 | } 182 | ], 183 | "source": [ 184 | "cal.preceding('2014-01-01')" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": 9, 190 | "metadata": {}, 191 | "outputs": [ 192 | { 193 | "data": { 194 | "text/plain": [ 195 | "datetime.date(2014, 1, 2)" 196 | ] 197 | }, 198 | "execution_count": 9, 199 | "metadata": {}, 200 | "output_type": "execute_result" 201 | } 202 | ], 203 | "source": [ 204 | "cal.preceding('2014-01-02')" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 10, 210 | "metadata": {}, 211 | "outputs": [ 212 | { 213 | "data": { 214 | "text/plain": [ 215 | "[datetime.date(2014, 1, 2),\n", 216 | " datetime.date(2014, 1, 3),\n", 217 | " datetime.date(2014, 1, 6),\n", 218 | " datetime.date(2014, 1, 7)]" 219 | ] 220 | }, 221 | "execution_count": 10, 222 | "metadata": {}, 223 | "output_type": "execute_result" 224 | } 225 | ], 226 | "source": [ 227 | "cal.seq('2014-01-02', '2014-01-07')" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 11, 233 | "metadata": {}, 234 | "outputs": [ 235 | { 236 | "data": { 237 | "text/plain": [ 238 | "datetime.date(2014, 1, 9)" 239 | ] 240 | }, 241 | "execution_count": 11, 242 | "metadata": {}, 243 | "output_type": "execute_result" 244 | } 245 | ], 246 | "source": [ 247 | "cal.offset('2014-01-02', 5)" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 12, 253 | "metadata": {}, 254 | "outputs": [ 255 | { 256 | "data": { 257 | "text/plain": [ 258 | "datetime.date(2002, 5, 15)" 259 | ] 260 | }, 261 | "execution_count": 12, 262 | "metadata": {}, 263 | "output_type": "execute_result" 264 | } 265 | ], 266 | "source": [ 267 | "cal.getdate('15th day', 2002, 5)" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 13, 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "data": { 277 | "text/plain": [ 278 | "datetime.date(2002, 5, 22)" 279 | ] 280 | }, 281 | "execution_count": 13, 282 | "metadata": {}, 283 | "output_type": "execute_result" 284 | } 285 | ], 286 | "source": [ 287 | "cal.getdate('15th bizday', 2002, 5)" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": 14, 293 | "metadata": {}, 294 | "outputs": [ 295 | { 296 | "data": { 297 | "text/plain": [ 298 | "datetime.date(2002, 5, 29)" 299 | ] 300 | }, 301 | "execution_count": 14, 302 | "metadata": {}, 303 | "output_type": "execute_result" 304 | } 305 | ], 306 | "source": [ 307 | "cal.getdate('last wed', 2002, 5)" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": 15, 313 | "metadata": {}, 314 | "outputs": [ 315 | { 316 | "data": { 317 | "text/plain": [ 318 | "datetime.date(2002, 5, 24)" 319 | ] 320 | }, 321 | "execution_count": 15, 322 | "metadata": {}, 323 | "output_type": "execute_result" 324 | } 325 | ], 326 | "source": [ 327 | "cal.getdate('first fri before last day ', 2002, 5)" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": 16, 333 | "metadata": {}, 334 | "outputs": [ 335 | { 336 | "data": { 337 | "text/plain": [ 338 | "22" 339 | ] 340 | }, 341 | "execution_count": 16, 342 | "metadata": {}, 343 | "output_type": "execute_result" 344 | } 345 | ], 346 | "source": [ 347 | "cal.getbizdays(2001, 5)" 348 | ] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": {}, 353 | "source": [ 354 | "In this example I used the list of holidays released by [ANBIMA](http://www.anbima.com.br/feriados/feriados.asp).\n", 355 | "\n", 356 | "> **Important note on date arguments and returning dates**\n", 357 | "> \n", 358 | "> As you can see in the examples all date arguments are strings ISO formatted (`YYYY-mm-dd` or `%Y-%m-%d`), but they can also be passed as `datetime.date` objects.\n", 359 | "> All returning dates are `datetime.date` objects (or a sequence of it), unless you set `iso=True`, that will return an ISO formatted string.\n", 360 | "\n", 361 | "> The `startdate` and `enddate` of a `Calendar` are defined accordingly the first and last given holidays.\n", 362 | "\n", 363 | "### bizdays\n", 364 | "\n", 365 | "To compute the business days between two dates you call `bizdays` passing `from` and `to` dates as arguments.\n" 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "execution_count": 17, 371 | "metadata": {}, 372 | "outputs": [ 373 | { 374 | "data": { 375 | "text/plain": [ 376 | "2" 377 | ] 378 | }, 379 | "execution_count": 17, 380 | "metadata": {}, 381 | "output_type": "execute_result" 382 | } 383 | ], 384 | "source": [ 385 | "cal.bizdays('2012-12-31', '2013-01-03')" 386 | ] 387 | }, 388 | { 389 | "cell_type": "markdown", 390 | "metadata": {}, 391 | "source": [ 392 | "### getdate\n", 393 | "\n", 394 | "You specify dates by its position or related to other dates, for example:\n" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 18, 400 | "metadata": {}, 401 | "outputs": [ 402 | { 403 | "data": { 404 | "text/plain": [ 405 | "datetime.date(2002, 5, 15)" 406 | ] 407 | }, 408 | "execution_count": 18, 409 | "metadata": {}, 410 | "output_type": "execute_result" 411 | } 412 | ], 413 | "source": [ 414 | "cal.getdate('15th day', 2002, 5)" 415 | ] 416 | }, 417 | { 418 | "cell_type": "markdown", 419 | "metadata": {}, 420 | "source": [ 421 | "it returns the 15th day of 2002 may. You can also reffer to the whole year." 422 | ] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": 19, 427 | "metadata": {}, 428 | "outputs": [ 429 | { 430 | "data": { 431 | "text/plain": [ 432 | "datetime.date(2002, 5, 30)" 433 | ] 434 | }, 435 | "execution_count": 19, 436 | "metadata": {}, 437 | "output_type": "execute_result" 438 | } 439 | ], 440 | "source": [ 441 | "cal.getdate('150th day', 2002)" 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "metadata": {}, 447 | "source": [ 448 | "It accepts `day`, `bizday` and weekdays by: `sun`, `mon`, `tue`, `wed`, `thu`, `fri`, and `sat`." 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 20, 454 | "metadata": {}, 455 | "outputs": [ 456 | { 457 | "data": { 458 | "text/plain": [ 459 | "datetime.date(2006, 12, 31)" 460 | ] 461 | }, 462 | "execution_count": 20, 463 | "metadata": {}, 464 | "output_type": "execute_result" 465 | } 466 | ], 467 | "source": [ 468 | "cal.getdate('last day', 2006)" 469 | ] 470 | }, 471 | { 472 | "cell_type": "code", 473 | "execution_count": 21, 474 | "metadata": {}, 475 | "outputs": [ 476 | { 477 | "data": { 478 | "text/plain": [ 479 | "datetime.date(2006, 12, 29)" 480 | ] 481 | }, 482 | "execution_count": 21, 483 | "metadata": {}, 484 | "output_type": "execute_result" 485 | } 486 | ], 487 | "source": [ 488 | "cal.getdate('last bizday', 2006)" 489 | ] 490 | }, 491 | { 492 | "cell_type": "code", 493 | "execution_count": 22, 494 | "metadata": {}, 495 | "outputs": [ 496 | { 497 | "data": { 498 | "text/plain": [ 499 | "datetime.date(2006, 12, 25)" 500 | ] 501 | }, 502 | "execution_count": 22, 503 | "metadata": {}, 504 | "output_type": "execute_result" 505 | } 506 | ], 507 | "source": [ 508 | "cal.getdate('last mon', 2006)" 509 | ] 510 | }, 511 | { 512 | "cell_type": "markdown", 513 | "metadata": {}, 514 | "source": [ 515 | "For postion use: `first`, `second`, `third`, `1st`, `2nd`, `3rd`, `[n]th`, and `last`.\n", 516 | "\n", 517 | "#### Using date postions as a reference\n", 518 | "\n", 519 | "You can find before and after other date positions (using date positions as a reference).\n" 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "execution_count": 23, 525 | "metadata": {}, 526 | "outputs": [ 527 | { 528 | "data": { 529 | "text/plain": [ 530 | "datetime.date(2006, 7, 24)" 531 | ] 532 | }, 533 | "execution_count": 23, 534 | "metadata": {}, 535 | "output_type": "execute_result" 536 | } 537 | ], 538 | "source": [ 539 | "cal.getdate('last mon before 30th day', 2006, 7)" 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "execution_count": 24, 545 | "metadata": {}, 546 | "outputs": [ 547 | { 548 | "data": { 549 | "text/plain": [ 550 | "datetime.date(2006, 1, 17)" 551 | ] 552 | }, 553 | "execution_count": 24, 554 | "metadata": {}, 555 | "output_type": "execute_result" 556 | } 557 | ], 558 | "source": [ 559 | "cal.getdate('second bizday after 15th day', 2006)" 560 | ] 561 | }, 562 | { 563 | "cell_type": "markdown", 564 | "metadata": {}, 565 | "source": [ 566 | "### Business days for years or months\n", 567 | "\n", 568 | "You get the number of business days for a year or a month calling `getbizdays`." 569 | ] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "execution_count": 25, 574 | "metadata": {}, 575 | "outputs": [ 576 | { 577 | "data": { 578 | "text/plain": [ 579 | "251" 580 | ] 581 | }, 582 | "execution_count": 25, 583 | "metadata": {}, 584 | "output_type": "execute_result" 585 | } 586 | ], 587 | "source": [ 588 | "cal.getbizdays(2021)" 589 | ] 590 | }, 591 | { 592 | "cell_type": "code", 593 | "execution_count": 26, 594 | "metadata": {}, 595 | "outputs": [ 596 | { 597 | "data": { 598 | "text/plain": [ 599 | "23" 600 | ] 601 | }, 602 | "execution_count": 26, 603 | "metadata": {}, 604 | "output_type": "execute_result" 605 | } 606 | ], 607 | "source": [ 608 | "cal.getbizdays(2021, 12)" 609 | ] 610 | }, 611 | { 612 | "cell_type": "markdown", 613 | "metadata": {}, 614 | "source": [ 615 | "#### following and preceding\n", 616 | "\n", 617 | "Several contracts, by default, always expiry in the same day, for example, 1st Januray, which isn't a business day, so instead of carrying your code with awful checks you could call `following` which returns the given date\n", 618 | "whether it is a business day or the next business day." 619 | ] 620 | }, 621 | { 622 | "cell_type": "code", 623 | "execution_count": 27, 624 | "metadata": {}, 625 | "outputs": [ 626 | { 627 | "data": { 628 | "text/plain": [ 629 | "datetime.date(2013, 1, 2)" 630 | ] 631 | }, 632 | "execution_count": 27, 633 | "metadata": {}, 634 | "output_type": "execute_result" 635 | } 636 | ], 637 | "source": [ 638 | "cal.following('2013-01-01')" 639 | ] 640 | }, 641 | { 642 | "cell_type": "code", 643 | "execution_count": 28, 644 | "metadata": {}, 645 | "outputs": [ 646 | { 647 | "data": { 648 | "text/plain": [ 649 | "datetime.date(2013, 1, 2)" 650 | ] 651 | }, 652 | "execution_count": 28, 653 | "metadata": {}, 654 | "output_type": "execute_result" 655 | } 656 | ], 657 | "source": [ 658 | "cal.following('2013-01-02')" 659 | ] 660 | }, 661 | { 662 | "cell_type": "markdown", 663 | "metadata": {}, 664 | "source": [ 665 | "We also have `preceding`, although I suppose it is unusual, too.\n" 666 | ] 667 | }, 668 | { 669 | "cell_type": "code", 670 | "execution_count": 29, 671 | "metadata": {}, 672 | "outputs": [ 673 | { 674 | "data": { 675 | "text/plain": [ 676 | "datetime.date(2012, 12, 31)" 677 | ] 678 | }, 679 | "execution_count": 29, 680 | "metadata": {}, 681 | "output_type": "execute_result" 682 | } 683 | ], 684 | "source": [ 685 | "cal.preceding('2013-01-01')" 686 | ] 687 | }, 688 | { 689 | "cell_type": "markdown", 690 | "metadata": {}, 691 | "source": [ 692 | "\n", 693 | "#### modified_following and modified_preceding\n", 694 | "\n", 695 | "`modified_following` and `modified_preceding` are common functions used to specify maturity of contracts.\n", 696 | "They work the same way `following` and `preceding` but once the returning date is a different month it is adjusted to the `following` or `preceding` business day in the same month.\n" 697 | ] 698 | }, 699 | { 700 | "cell_type": "code", 701 | "execution_count": 30, 702 | "metadata": {}, 703 | "outputs": [ 704 | { 705 | "data": { 706 | "text/plain": [ 707 | "datetime.date(2002, 3, 31)" 708 | ] 709 | }, 710 | "execution_count": 30, 711 | "metadata": {}, 712 | "output_type": "execute_result" 713 | } 714 | ], 715 | "source": [ 716 | "dt = cal.getdate('last day', 2002, 3)\n", 717 | "dt" 718 | ] 719 | }, 720 | { 721 | "cell_type": "code", 722 | "execution_count": 31, 723 | "metadata": {}, 724 | "outputs": [ 725 | { 726 | "data": { 727 | "text/plain": [ 728 | "datetime.date(2002, 3, 28)" 729 | ] 730 | }, 731 | "execution_count": 31, 732 | "metadata": {}, 733 | "output_type": "execute_result" 734 | } 735 | ], 736 | "source": [ 737 | "cal.modified_following(dt)" 738 | ] 739 | }, 740 | { 741 | "cell_type": "code", 742 | "execution_count": 32, 743 | "metadata": {}, 744 | "outputs": [ 745 | { 746 | "data": { 747 | "text/plain": [ 748 | "False" 749 | ] 750 | }, 751 | "execution_count": 32, 752 | "metadata": {}, 753 | "output_type": "execute_result" 754 | } 755 | ], 756 | "source": [ 757 | "cal.isbizday('2002-03-29')" 758 | ] 759 | }, 760 | { 761 | "cell_type": "code", 762 | "execution_count": 33, 763 | "metadata": {}, 764 | "outputs": [ 765 | { 766 | "data": { 767 | "text/plain": [ 768 | "datetime.date(2002, 6, 1)" 769 | ] 770 | }, 771 | "execution_count": 33, 772 | "metadata": {}, 773 | "output_type": "execute_result" 774 | } 775 | ], 776 | "source": [ 777 | "dt = cal.getdate('first day', 2002, 6)\n", 778 | "dt" 779 | ] 780 | }, 781 | { 782 | "cell_type": "code", 783 | "execution_count": 34, 784 | "metadata": {}, 785 | "outputs": [ 786 | { 787 | "data": { 788 | "text/plain": [ 789 | "datetime.date(2002, 6, 3)" 790 | ] 791 | }, 792 | "execution_count": 34, 793 | "metadata": {}, 794 | "output_type": "execute_result" 795 | } 796 | ], 797 | "source": [ 798 | "cal.modified_preceding(dt)" 799 | ] 800 | }, 801 | { 802 | "cell_type": "markdown", 803 | "metadata": {}, 804 | "source": [ 805 | "### seq\n", 806 | "\n", 807 | "To execute calculations through sequential dates, sometimes you must consider only business days.\n", 808 | "For example, you want to compute the price of a bond from its issue date up to its maturity.\n", 809 | "You have to walk over business days in order to carry the contract up to maturity.\n", 810 | "To accomplish that you use the `seq` method (stolen from R) which returns a sequence generator of business days." 811 | ] 812 | }, 813 | { 814 | "cell_type": "code", 815 | "execution_count": 35, 816 | "metadata": {}, 817 | "outputs": [ 818 | { 819 | "data": { 820 | "text/plain": [ 821 | "[datetime.date(2012, 12, 31),\n", 822 | " datetime.date(2013, 1, 2),\n", 823 | " datetime.date(2013, 1, 3)]" 824 | ] 825 | }, 826 | "execution_count": 35, 827 | "metadata": {}, 828 | "output_type": "execute_result" 829 | } 830 | ], 831 | "source": [ 832 | "cal.seq('2012-12-31', '2013-01-03')" 833 | ] 834 | }, 835 | { 836 | "cell_type": "markdown", 837 | "metadata": {}, 838 | "source": [ 839 | "### offset\n", 840 | "\n", 841 | "This method offsets the given date by `n` days respecting the calendar, so it obligatorily returns a business day." 842 | ] 843 | }, 844 | { 845 | "cell_type": "code", 846 | "execution_count": 36, 847 | "metadata": {}, 848 | "outputs": [ 849 | { 850 | "data": { 851 | "text/plain": [ 852 | "datetime.date(2013, 1, 3)" 853 | ] 854 | }, 855 | "execution_count": 36, 856 | "metadata": {}, 857 | "output_type": "execute_result" 858 | } 859 | ], 860 | "source": [ 861 | "cal.offset('2013-01-02', 1)" 862 | ] 863 | }, 864 | { 865 | "cell_type": "code", 866 | "execution_count": 37, 867 | "metadata": {}, 868 | "outputs": [ 869 | { 870 | "data": { 871 | "text/plain": [ 872 | "datetime.date(2013, 1, 7)" 873 | ] 874 | }, 875 | "execution_count": 37, 876 | "metadata": {}, 877 | "output_type": "execute_result" 878 | } 879 | ], 880 | "source": [ 881 | "cal.offset('2013-01-02', 3)" 882 | ] 883 | }, 884 | { 885 | "cell_type": "code", 886 | "execution_count": 38, 887 | "metadata": {}, 888 | "outputs": [ 889 | { 890 | "data": { 891 | "text/plain": [ 892 | "datetime.date(2013, 1, 2)" 893 | ] 894 | }, 895 | "execution_count": 38, 896 | "metadata": {}, 897 | "output_type": "execute_result" 898 | } 899 | ], 900 | "source": [ 901 | "cal.offset('2013-01-02', 0)" 902 | ] 903 | }, 904 | { 905 | "cell_type": "markdown", 906 | "metadata": {}, 907 | "source": [ 908 | "Obviously, if you want to offset backwards you can use `-n`." 909 | ] 910 | }, 911 | { 912 | "cell_type": "code", 913 | "execution_count": 39, 914 | "metadata": {}, 915 | "outputs": [ 916 | { 917 | "data": { 918 | "text/plain": [ 919 | "datetime.date(2012, 12, 31)" 920 | ] 921 | }, 922 | "execution_count": 39, 923 | "metadata": {}, 924 | "output_type": "execute_result" 925 | } 926 | ], 927 | "source": [ 928 | "cal.offset('2013-01-02', -1)" 929 | ] 930 | }, 931 | { 932 | "cell_type": "code", 933 | "execution_count": 40, 934 | "metadata": {}, 935 | "outputs": [ 936 | { 937 | "data": { 938 | "text/plain": [ 939 | "datetime.date(2012, 12, 27)" 940 | ] 941 | }, 942 | "execution_count": 40, 943 | "metadata": {}, 944 | "output_type": "execute_result" 945 | } 946 | ], 947 | "source": [ 948 | "cal.offset('2013-01-02', -3)" 949 | ] 950 | }, 951 | { 952 | "cell_type": "markdown", 953 | "metadata": {}, 954 | "source": [ 955 | "Once the given date is a business day there is no problems, but if instead it isn't a working day the offset can lead to unexpected results. For example:\n" 956 | ] 957 | }, 958 | { 959 | "cell_type": "code", 960 | "execution_count": 41, 961 | "metadata": {}, 962 | "outputs": [ 963 | { 964 | "data": { 965 | "text/plain": [ 966 | "datetime.date(2013, 1, 2)" 967 | ] 968 | }, 969 | "execution_count": 41, 970 | "metadata": {}, 971 | "output_type": "execute_result" 972 | } 973 | ], 974 | "source": [ 975 | "cal.offset('2013-01-01', 1)" 976 | ] 977 | }, 978 | { 979 | "cell_type": "code", 980 | "execution_count": 42, 981 | "metadata": {}, 982 | "outputs": [ 983 | { 984 | "data": { 985 | "text/plain": [ 986 | "datetime.date(2013, 1, 1)" 987 | ] 988 | }, 989 | "execution_count": 42, 990 | "metadata": {}, 991 | "output_type": "execute_result" 992 | } 993 | ], 994 | "source": [ 995 | "cal.offset('2013-01-01', 0)" 996 | ] 997 | }, 998 | { 999 | "cell_type": "code", 1000 | "execution_count": 43, 1001 | "metadata": {}, 1002 | "outputs": [ 1003 | { 1004 | "data": { 1005 | "text/plain": [ 1006 | "datetime.date(2012, 12, 31)" 1007 | ] 1008 | }, 1009 | "execution_count": 43, 1010 | "metadata": {}, 1011 | "output_type": "execute_result" 1012 | } 1013 | ], 1014 | "source": [ 1015 | "cal.offset('2013-01-01', -1)" 1016 | ] 1017 | }, 1018 | { 1019 | "cell_type": "markdown", 1020 | "metadata": {}, 1021 | "source": [ 1022 | "## Actual Calendar\n", 1023 | "\n", 1024 | "The Actual Calendar can be defined as\n" 1025 | ] 1026 | }, 1027 | { 1028 | "cell_type": "code", 1029 | "execution_count": 44, 1030 | "metadata": {}, 1031 | "outputs": [ 1032 | { 1033 | "data": { 1034 | "text/plain": [ 1035 | "Calendar: actual\n", 1036 | "Start: 1970-01-01\n", 1037 | "End: 2071-01-01\n", 1038 | "Holidays: 0\n", 1039 | "Financial: True" 1040 | ] 1041 | }, 1042 | "execution_count": 44, 1043 | "metadata": {}, 1044 | "output_type": "execute_result" 1045 | } 1046 | ], 1047 | "source": [ 1048 | "cal = Calendar(name='actual')\n", 1049 | "cal" 1050 | ] 1051 | }, 1052 | { 1053 | "cell_type": "markdown", 1054 | "metadata": {}, 1055 | "source": [ 1056 | "The Actual Calendar doesn't consider holidays, nor nonworking weekdays for counting business days between 2 dates.\n", 1057 | "This is the same of subtracting 2 dates, and adjust methods will return the given argument.\n", 1058 | "But the idea of using the Actual Calendar is working with the same interface for any calendar you work with.\n", 1059 | "When you price financial instruments you don't have to check if it uses business days or not.\n", 1060 | "\n", 1061 | "> `startdate` and `enddate` defaults to `1970-01-01` and `2071-01-01`, but they can be set during Calendar's instanciation.\n", 1062 | "\n", 1063 | "## Vectorized operations\n", 1064 | "\n", 1065 | "The Calendar's methods: `isbizday`, `bizdays`, `adjust_previous`, `adjust_next`, and `offset`, have a vectorized counterparty, inside `Calendar.vec` attribute.\n", 1066 | "\n" 1067 | ] 1068 | }, 1069 | { 1070 | "cell_type": "code", 1071 | "execution_count": 45, 1072 | "metadata": {}, 1073 | "outputs": [ 1074 | { 1075 | "data": { 1076 | "text/plain": [ 1077 | "[datetime.date(2002, 1, 2),\n", 1078 | " datetime.date(2002, 1, 2),\n", 1079 | " datetime.date(2002, 1, 3)]" 1080 | ] 1081 | }, 1082 | "execution_count": 45, 1083 | "metadata": {}, 1084 | "output_type": "execute_result" 1085 | } 1086 | ], 1087 | "source": [ 1088 | "cal = Calendar.load('ANBIMA')\n", 1089 | "dates = ('2002-01-01', '2002-01-02', '2002-01-03')\n", 1090 | "cal.following(dates)" 1091 | ] 1092 | }, 1093 | { 1094 | "cell_type": "code", 1095 | "execution_count": 46, 1096 | "metadata": {}, 1097 | "outputs": [ 1098 | { 1099 | "data": { 1100 | "text/plain": [ 1101 | "[0, 1, 2]" 1102 | ] 1103 | }, 1104 | "execution_count": 46, 1105 | "metadata": {}, 1106 | "output_type": "execute_result" 1107 | } 1108 | ], 1109 | "source": [ 1110 | "cal.bizdays('2001-12-31', dates)" 1111 | ] 1112 | }, 1113 | { 1114 | "cell_type": "markdown", 1115 | "metadata": {}, 1116 | "source": [ 1117 | "These functions accept sequences and single values and return lists.\n", 1118 | "When `bizdays` was called, a date and a sequence have been passed, computing business days between the given date and all the others.\n", 1119 | "\n", 1120 | "### Recycle rule\n", 1121 | "\n", 1122 | "Once you pass 2 sequences, for methods that allow it, and those sequences doesn't have the same length, no problem.\n", 1123 | "The shorter collection is cycled to fit the longer's length.\n", 1124 | "This is the recycle rule." 1125 | ] 1126 | }, 1127 | { 1128 | "cell_type": "code", 1129 | "execution_count": 47, 1130 | "metadata": {}, 1131 | "outputs": [ 1132 | { 1133 | "data": { 1134 | "text/plain": [ 1135 | "[datetime.date(2002, 1, 2),\n", 1136 | " datetime.date(2002, 1, 4),\n", 1137 | " datetime.date(2002, 1, 8),\n", 1138 | " datetime.date(2002, 1, 7),\n", 1139 | " datetime.date(2002, 1, 8)]" 1140 | ] 1141 | }, 1142 | "execution_count": 47, 1143 | "metadata": {}, 1144 | "output_type": "execute_result" 1145 | } 1146 | ], 1147 | "source": [ 1148 | "dates = ('2002-01-01', '2002-01-02', '2002-01-03', '2002-01-04', '2002-01-05')\n", 1149 | "cal.offset(dates, (1, 2, 3))" 1150 | ] 1151 | }, 1152 | { 1153 | "cell_type": "markdown", 1154 | "metadata": {}, 1155 | "source": [ 1156 | "## Pandas integration\n", 1157 | "\n", 1158 | "`bizdays` has an integration with pandas.\n", 1159 | "To get this integration working the option `mode` must be set to `pandas`." 1160 | ] 1161 | }, 1162 | { 1163 | "cell_type": "code", 1164 | "execution_count": 48, 1165 | "metadata": {}, 1166 | "outputs": [], 1167 | "source": [ 1168 | "import bizdays\n", 1169 | "\n", 1170 | "bizdays.set_option('mode', 'pandas')" 1171 | ] 1172 | }, 1173 | { 1174 | "cell_type": "markdown", 1175 | "metadata": {}, 1176 | "source": [ 1177 | "Once `mode` is set to `pandas`, the Calendar's methods return `Timestamp` (for single dates), `DatetimeIndex` (for sequece of dates) and `numpy.ndarray` (for other sequences)." 1178 | ] 1179 | }, 1180 | { 1181 | "cell_type": "code", 1182 | "execution_count": 49, 1183 | "metadata": {}, 1184 | "outputs": [ 1185 | { 1186 | "data": { 1187 | "text/plain": [ 1188 | "DatetimeIndex(['2012-12-31', '2013-01-02', '2013-01-03'], dtype='datetime64[ns]', freq=None)" 1189 | ] 1190 | }, 1191 | "execution_count": 49, 1192 | "metadata": {}, 1193 | "output_type": "execute_result" 1194 | } 1195 | ], 1196 | "source": [ 1197 | "cal.seq('2012-12-31', '2013-01-03')" 1198 | ] 1199 | }, 1200 | { 1201 | "cell_type": "code", 1202 | "execution_count": 50, 1203 | "metadata": {}, 1204 | "outputs": [ 1205 | { 1206 | "data": { 1207 | "text/plain": [ 1208 | "DatetimeIndex(['2002-01-02', '2002-01-04', '2002-01-08', '2002-01-07',\n", 1209 | " '2002-01-08'],\n", 1210 | " dtype='datetime64[ns]', freq=None)" 1211 | ] 1212 | }, 1213 | "execution_count": 50, 1214 | "metadata": {}, 1215 | "output_type": "execute_result" 1216 | } 1217 | ], 1218 | "source": [ 1219 | "dates = ('2002-01-01', '2002-01-02', '2002-01-03', '2002-01-04', '2002-01-05')\n", 1220 | "cal.offset(dates, (1, 2, 3))" 1221 | ] 1222 | }, 1223 | { 1224 | "cell_type": "code", 1225 | "execution_count": 51, 1226 | "metadata": {}, 1227 | "outputs": [ 1228 | { 1229 | "data": { 1230 | "text/plain": [ 1231 | "DatetimeIndex(['2002-01-02', '2002-01-02', '2002-01-03'], dtype='datetime64[ns]', freq=None)" 1232 | ] 1233 | }, 1234 | "execution_count": 51, 1235 | "metadata": {}, 1236 | "output_type": "execute_result" 1237 | } 1238 | ], 1239 | "source": [ 1240 | "dates = ('2002-01-01', '2002-01-02', '2002-01-03')\n", 1241 | "cal.following(dates)" 1242 | ] 1243 | }, 1244 | { 1245 | "cell_type": "code", 1246 | "execution_count": 52, 1247 | "metadata": {}, 1248 | "outputs": [ 1249 | { 1250 | "data": { 1251 | "text/plain": [ 1252 | "array([0, 1, 2])" 1253 | ] 1254 | }, 1255 | "execution_count": 52, 1256 | "metadata": {}, 1257 | "output_type": "execute_result" 1258 | } 1259 | ], 1260 | "source": [ 1261 | "cal.bizdays('2001-12-31', dates)" 1262 | ] 1263 | }, 1264 | { 1265 | "cell_type": "code", 1266 | "execution_count": 53, 1267 | "metadata": {}, 1268 | "outputs": [ 1269 | { 1270 | "data": { 1271 | "text/plain": [ 1272 | "array([False, True, True])" 1273 | ] 1274 | }, 1275 | "execution_count": 53, 1276 | "metadata": {}, 1277 | "output_type": "execute_result" 1278 | } 1279 | ], 1280 | "source": [ 1281 | "cal.isbizday(dates)" 1282 | ] 1283 | }, 1284 | { 1285 | "cell_type": "code", 1286 | "execution_count": 55, 1287 | "metadata": {}, 1288 | "outputs": [ 1289 | { 1290 | "data": { 1291 | "text/plain": [ 1292 | "array([23, 21])" 1293 | ] 1294 | }, 1295 | "execution_count": 55, 1296 | "metadata": {}, 1297 | "output_type": "execute_result" 1298 | } 1299 | ], 1300 | "source": [ 1301 | "cal.getbizdays([2021, 2022], [12, 1])" 1302 | ] 1303 | }, 1304 | { 1305 | "cell_type": "code", 1306 | "execution_count": 56, 1307 | "metadata": {}, 1308 | "outputs": [ 1309 | { 1310 | "data": { 1311 | "text/plain": [ 1312 | "DatetimeIndex(['2021-12-31', '2022-01-28'], dtype='datetime64[ns]', freq=None)" 1313 | ] 1314 | }, 1315 | "execution_count": 56, 1316 | "metadata": {}, 1317 | "output_type": "execute_result" 1318 | } 1319 | ], 1320 | "source": [ 1321 | "cal.getdate('last fri', [2021, 2022], [12, 1])" 1322 | ] 1323 | }, 1324 | { 1325 | "cell_type": "code", 1326 | "execution_count": null, 1327 | "metadata": {}, 1328 | "outputs": [], 1329 | "source": [] 1330 | } 1331 | ], 1332 | "metadata": { 1333 | "interpreter": { 1334 | "hash": "4e6da9785688dca07ac236f8ec5f7e99c2c01a8eecf30ede8ddadf573b26e527" 1335 | }, 1336 | "kernelspec": { 1337 | "display_name": "Python 3", 1338 | "language": "python", 1339 | "name": "python3" 1340 | }, 1341 | "language_info": { 1342 | "codemirror_mode": { 1343 | "name": "ipython", 1344 | "version": 3 1345 | }, 1346 | "file_extension": ".py", 1347 | "mimetype": "text/x-python", 1348 | "name": "python", 1349 | "nbconvert_exporter": "python", 1350 | "pygments_lexer": "ipython3", 1351 | "version": "3.7.6" 1352 | } 1353 | }, 1354 | "nbformat": 4, 1355 | "nbformat_minor": 2 1356 | } 1357 | -------------------------------------------------------------------------------- /bizdays.py: -------------------------------------------------------------------------------- 1 | from io import StringIO 2 | import os 3 | import re 4 | from datetime import datetime, date, timedelta 5 | from itertools import cycle 6 | from typing import TextIO, Dict 7 | 8 | PANDAS_INSTALLED = False 9 | 10 | try: 11 | import pandas as pd 12 | import numpy as np 13 | 14 | PANDAS_INSTALLED = True 15 | 16 | def isnull(x): 17 | return pd.isna(x) 18 | 19 | def recseq(gen, typo="DatetimeIndex"): 20 | g = list(gen) 21 | if get_option("mode") == "pandas": 22 | if typo == "DatetimeIndex": 23 | return pd.DatetimeIndex(g) 24 | elif typo == "array": 25 | return np.array(g) 26 | else: 27 | return g 28 | 29 | def retdate(dt): 30 | if get_option("mode.datetype") == "datetime": 31 | return datetime(dt.year, dt.month, dt.day) 32 | elif get_option("mode.datetype") == "date": 33 | return dt 34 | elif get_option("mode.datetype") == "iso": 35 | return dt.isoformat() 36 | elif get_option("mode") == "pandas": 37 | return pd.to_datetime(dt) 38 | else: 39 | return dt 40 | 41 | def return_none(): 42 | if get_option("mode") == "pandas": 43 | return pd.NA 44 | else: 45 | return None 46 | 47 | except ImportError: 48 | 49 | def isnull(x): 50 | return x is None 51 | 52 | def recseq(gen, typo=None): 53 | return list(gen) 54 | 55 | def retdate(dt): 56 | if get_option("mode.datetype") == "datetime": 57 | return datetime(dt.year, dt.month, dt.day) 58 | elif get_option("mode.datetype") == "date": 59 | return dt 60 | elif get_option("mode.datetype") == "iso": 61 | return dt.isoformat() 62 | else: 63 | return dt 64 | 65 | def return_none(): 66 | return None 67 | 68 | 69 | __all__ = ["get_option", "set_option", "Calendar"] 70 | 71 | 72 | options = {"mode": "python"} 73 | 74 | 75 | def get_option(name): 76 | """gets option value 77 | 78 | Parameters 79 | ---------- 80 | name : str 81 | option name 82 | 83 | Returns 84 | ------- 85 | val : str 86 | option value 87 | """ 88 | return options.get(name) 89 | 90 | 91 | def set_option(name, val): 92 | """sets option value 93 | 94 | Parameters 95 | ---------- 96 | name : str 97 | option name 98 | val : str 99 | option value 100 | 101 | Returns 102 | ------- 103 | No return 104 | """ 105 | if name == "pandas" and not PANDAS_INSTALLED: 106 | raise Exception("Cannot set mode pandas: pandas not installed") 107 | options[name] = val 108 | 109 | 110 | D1 = timedelta(1) 111 | 112 | 113 | def isstr(d): 114 | return isinstance(d, str) 115 | 116 | 117 | def isseq(seq): 118 | if isstr(seq): 119 | return False 120 | try: 121 | iter(seq) 122 | except TypeError: 123 | return False 124 | else: 125 | return True 126 | 127 | 128 | class DateOutOfRange(Exception): 129 | pass 130 | 131 | 132 | def find_date_pos(col, dt): 133 | beg = 0 134 | end = len(col) 135 | while (end - beg) > 1: 136 | mid = int((end + beg) / 2) 137 | if dt > col[mid]: 138 | beg = mid 139 | elif dt < col[mid]: 140 | end = mid 141 | else: 142 | return mid 143 | return beg 144 | 145 | 146 | def __daterangecheck(obj, dt): 147 | dt = Date(dt).date 148 | if dt > obj.enddate or dt < obj.startdate: 149 | raise DateOutOfRange("Given date out of calendar range") 150 | return dt 151 | 152 | 153 | def daterangecheck(func): 154 | def handler(self, dt, *args): 155 | dt = __daterangecheck(self, dt) 156 | return func(self, dt, *args) 157 | 158 | return handler 159 | 160 | 161 | def daterangecheck2(func): 162 | def handler(self, dt1, dt2, *args): 163 | dt1 = __daterangecheck(self, dt1) 164 | dt2 = __daterangecheck(self, dt2) 165 | return func(self, dt1, dt2, *args) 166 | 167 | return handler 168 | 169 | 170 | def load_holidays(fname, format="%Y-%m-%d"): 171 | if not os.path.exists(fname): 172 | raise Exception( 173 | "Invalid calendar specification: \ 174 | file not found (%s)" 175 | % fname 176 | ) 177 | _holidays = [] 178 | with open(fname) as fcal: 179 | for cal_reg in fcal: 180 | cal_reg = cal_reg.strip() 181 | if cal_reg == "": 182 | continue 183 | _holidays.append(Date(cal_reg, format=format).date) 184 | return _holidays 185 | 186 | 187 | class DateIndex(object): 188 | WEEKDAYS = ("mon", "tue", "wed", "thu", "fri", "sat", "sun") 189 | 190 | def __init__(self, holidays, startdate, enddate, weekdays): 191 | self._index = {} 192 | self._bizdays = [] 193 | self._days = [] 194 | self._years = {} 195 | self._weekdays = {} 196 | self.startdate = Date(startdate).date 197 | self.enddate = Date(enddate).date 198 | self.weekdays = weekdays 199 | self.holidays = [Date(d).date for d in holidays] 200 | 201 | dts = [] 202 | dt = self.startdate 203 | while dt <= self.enddate: 204 | dts.append(dt) 205 | dt = dt + D1 206 | 207 | w = 0 208 | c = 1 209 | for dt in dts: 210 | is_hol = dt in self.holidays or dt.weekday() in weekdays 211 | if not is_hol: 212 | w += 1 213 | self._bizdays.append(dt) 214 | self._index[dt] = [w, c, is_hol, None] 215 | c += 1 216 | 217 | max_w = self._index[self.enddate][0] 218 | w = max_w + 1 219 | for dt in reversed(dts): 220 | is_hol = self._index[dt][2] 221 | if not is_hol: 222 | w -= 1 223 | self._index[dt][3] = w 224 | 225 | for dt in dts: 226 | # ---- 227 | ix = self._index[dt] 228 | col = self._years.get(dt.year, []) 229 | _ = (dt, dt.month, dt.weekday(), ix[2], ix[1], ix[0], ix[3]) 230 | col.append(_) 231 | # col.append((dt, dt.month, dt.weekday(), is_hol, c, w)) 232 | self._years[dt.year] = col 233 | col = self._weekdays.get(dt.weekday(), []) 234 | col.append(dt) 235 | self._weekdays[dt.weekday()] = col 236 | self._days.append(dt) 237 | # ---- 238 | 239 | @daterangecheck 240 | def offset(self, dt, n): 241 | if n > 0: 242 | pos = self._index[dt][0] - 1 + n 243 | elif n < 0: 244 | pos = self._index[dt][3] - 1 + n 245 | else: 246 | return dt 247 | return self._bizdays[pos] 248 | 249 | @daterangecheck 250 | def following(self, dt): 251 | if not self._index[dt][2]: 252 | return dt 253 | else: 254 | return self.following(dt + D1) 255 | 256 | @daterangecheck 257 | def modified_following(self, dt): 258 | dtx = self.following(dt) 259 | if dtx.month != dt.month: 260 | dtx = self.preceding(dt) 261 | return dtx 262 | 263 | @daterangecheck 264 | def preceding(self, dt): 265 | if not self._index[dt][2]: 266 | return dt 267 | else: 268 | return self.preceding(dt - D1) 269 | 270 | @daterangecheck 271 | def modified_preceding(self, dt): 272 | dtx = self.preceding(dt) 273 | if dtx.month != dt.month: 274 | dtx = self.following(dt) 275 | return dtx 276 | 277 | @daterangecheck2 278 | def seq(self, dt1, dt2): 279 | pos1 = max(self._index[dt1][0], self._index[dt1][3]) - 1 280 | pos2 = min(self._index[dt2][0], self._index[dt2][3]) 281 | return self._bizdays[pos1:pos2] 282 | 283 | @daterangecheck 284 | def get(self, dt): 285 | return self._index[dt] 286 | 287 | def getbizdays(self, year, month=None): 288 | if month: 289 | return sum(not d[3] for d in self._years[year] if d[1] == month) 290 | else: 291 | return sum(not d[3] for d in self._years[year]) 292 | 293 | def getdate(self, expr, year, month=None): 294 | tok = expr.split() 295 | if len(tok) == 2: 296 | n = self._getnth(tok[0]) 297 | if tok[1] == "day": 298 | return self._getnthday(n, year, month) 299 | elif tok[1] == "bizday": 300 | return self._getnthbizday(n, year, month) 301 | elif tok[1] in self.WEEKDAYS: 302 | return self._getnthweekday(n, tok[1], year, month) 303 | else: 304 | raise ValueError("Invalid day:", tok[1]) 305 | elif len(tok) == 5: 306 | n = self._getnth(tok[3]) 307 | if tok[4] == "day": 308 | pos = self._getnthdaypos(n, year, month) 309 | elif tok[4] == "bizday": 310 | pos = self._getnthbizdaypos(n, year, month) 311 | else: 312 | raise ValueError("Invalid reference day:", tok[4]) 313 | m = {"before": -1, "after": 1}.get(tok[2], 0) 314 | if not m: 315 | raise ValueError("Invalid operator:", tok[2]) 316 | n = self._getnthpos(tok[0]) * m 317 | if tok[1] == "day": 318 | return self._getnthday_beforeafter(n, pos) 319 | elif tok[1] == "bizday": 320 | return self._getnthbizday_beforeafter(n, pos) 321 | elif tok[1] in self.WEEKDAYS: 322 | return self._getnthweekday_beforeafter(n, tok[1], pos) 323 | else: 324 | raise ValueError("Invalid day:", tok[1]) 325 | 326 | def _getnthpos(self, nth): 327 | if nth == "first": 328 | return 1 329 | elif nth == "second": 330 | return 2 331 | elif nth == "third": 332 | return 3 333 | elif nth == "last": 334 | return 1 335 | elif nth[-2:] in ("th", "st", "nd", "rd"): 336 | return int(nth[:-2]) 337 | else: 338 | raise ValueError("invalid nth:", nth) 339 | 340 | def _getnth(self, nth): 341 | if nth == "first": 342 | return 1 343 | elif nth == "second": 344 | return 2 345 | elif nth == "third": 346 | return 3 347 | elif nth == "last": 348 | return -1 349 | elif nth[-2:] in ("th", "st", "nd", "rd"): 350 | return int(nth[:-2]) 351 | else: 352 | raise ValueError("invalid nth:", nth) 353 | 354 | def _getnthdaypos(self, n, year, month=None): 355 | n = n - 1 if n > 0 else n 356 | if month: 357 | pos = [ 358 | (d[4] - 1, d[5] - 1, d[0], d[6] - 1) 359 | for d in self._years[year] 360 | if d[1] == month 361 | ] 362 | return pos[n] 363 | else: 364 | return ( 365 | self._years[year][n][4] - 1, 366 | self._years[year][n][5] - 1, 367 | self._years[year][n][0], 368 | self._years[year][n][6], 369 | ) 370 | 371 | def _getnthbizdaypos(self, n, year, month=None): 372 | n = n - 1 if n > 0 else n 373 | if month: 374 | col = [ 375 | (d[4] - 1, d[5] - 1, d[0], d[6] - 1) 376 | for d in self._years[year] 377 | if not d[3] and d[1] == month 378 | ] 379 | else: 380 | col = [ 381 | (d[4] - 1, d[5] - 1, d[0], d[6] - 1) 382 | for d in self._years[year] 383 | if not d[3] 384 | ] 385 | return col[n] 386 | 387 | def _getnthweekdaypos(self, n, weekday, year, month=None): 388 | n = n - 1 if n > 0 else n 389 | if month: 390 | col = [ 391 | (d[4] - 1, d[5] - 1, d[0], d[6] - 1) 392 | for d in self._years[year] 393 | if self.WEEKDAYS[d[2]] == weekday and d[1] == month 394 | ] 395 | else: 396 | col = [ 397 | (d[4] - 1, d[5] - 1, d[0], d[6] - 1) 398 | for d in self._years[year] 399 | if self.WEEKDAYS[d[2]] == weekday 400 | ] 401 | return col[n] 402 | 403 | def _getnthday_beforeafter(self, n1, pos): 404 | pos = pos[0] + n1 405 | return self._days[pos] 406 | 407 | def _getnthbizday_beforeafter(self, n1, pos): 408 | if n1 > 0: 409 | pos = pos[1] + n1 410 | else: 411 | pos = pos[3] + n1 412 | return self._bizdays[pos] 413 | 414 | def _getnthweekday_beforeafter(self, n1, weekday, pos): 415 | dt = pos[2] 416 | wday = self.WEEKDAYS.index(weekday) 417 | pos = find_date_pos(self._weekdays[wday], dt) 418 | if dt.weekday() != wday: 419 | n1 = n1 + 1 if n1 < 0 else n1 420 | return self._weekdays[wday][pos + n1] 421 | 422 | def _getnthday(self, n, year, month=None): 423 | pos = self._getnthdaypos(n, year, month)[0] 424 | return self._days[pos] 425 | 426 | def _getnthbizday(self, n, year, month=None): 427 | pos = self._getnthbizdaypos(n, year, month)[1] 428 | return self._bizdays[pos] 429 | 430 | def _getnthweekday(self, n, weekday, year, month=None): 431 | n = n - 1 if n > 0 else n 432 | if month: 433 | col = [ 434 | d[0] 435 | for d in self._years[year] 436 | if self.WEEKDAYS[d[2]] == weekday and d[1] == month 437 | ] 438 | else: 439 | col = [d[0] for d in self._years[year] if self.WEEKDAYS[d[2]] == weekday] 440 | return col[n] 441 | 442 | def __getitem__(self, dt): 443 | return self.get(dt) 444 | 445 | 446 | class Date(object): 447 | def __init__(self, d=None, format="%Y-%m-%d"): 448 | # d = d if d else date.today() 449 | if isstr(d): 450 | d = datetime.strptime(d, format).date() 451 | elif isinstance(d, datetime): 452 | d = d.date() 453 | elif isinstance(d, Date): 454 | d = d.date 455 | elif isinstance(d, date): 456 | pass 457 | elif d is None: 458 | pass 459 | else: 460 | raise ValueError() 461 | self.date = d 462 | 463 | def format(self, fmts="%Y-%m-%d"): 464 | return datetime.strftime(self.date, fmts) 465 | 466 | def __gt__(self, other): 467 | return self.date > other.date 468 | 469 | def __ge__(self, other): 470 | return self.date >= other.date 471 | 472 | def __lt__(self, other): 473 | return self.date < other.date 474 | 475 | def __le__(self, other): 476 | return self.date <= other.date 477 | 478 | def __eq__(self, other): 479 | return self.date == other.date 480 | 481 | def __repr__(self): 482 | return self.format() 483 | 484 | __str__ = __repr__ 485 | 486 | 487 | class Calendar(object): 488 | """ 489 | Calendar class 490 | 491 | Calendar representation where holidays and nonworking weekdays are 492 | defined. 493 | 494 | Attributes 495 | ---------- 496 | 497 | name : str 498 | 499 | holidays : list of dates 500 | 501 | enddate : date 502 | 503 | startdate : date 504 | 505 | weekdays : list of str 506 | 507 | financial : bool 508 | 509 | 510 | Parameters 511 | ---------- 512 | holidays : list with dates 513 | Dates can be ISO formated string, datetime.date or datetime.datetime. 514 | 515 | weekdays : list 516 | A list with weekdays representing nonworking days. 517 | 518 | Accepts: 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 519 | 'Saturday', 'Sunday' 520 | 521 | startdate : str or datetime.date 522 | Calendar's start date 523 | 524 | enddate : str or datetime.date 525 | Calendar's end date 526 | 527 | name : str 528 | Calendar's name 529 | 530 | financial : bool 531 | Defines a financial calendar 532 | """ 533 | 534 | _weekdays = ( 535 | "Monday", 536 | "Tuesday", 537 | "Wednesday", 538 | "Thursday", 539 | "Friday", 540 | "Saturday", 541 | "Sunday", 542 | ) 543 | 544 | def __init__( 545 | self, 546 | holidays=[], 547 | weekdays=[], 548 | startdate=None, 549 | enddate=None, 550 | name=None, 551 | financial=True, 552 | ): 553 | self.financial = financial 554 | self.name = name 555 | self._holidays = [Date(d) for d in holidays] 556 | self._nonwork_weekdays = [ 557 | [w[:3].lower() for w in self._weekdays].index(wd[:3].lower()) 558 | for wd in weekdays 559 | ] 560 | if len(self._holidays): 561 | if startdate: 562 | self._startdate = Date(startdate) 563 | else: 564 | self._startdate = min(self._holidays) 565 | if enddate: 566 | self._enddate = Date(enddate) 567 | else: 568 | self._enddate = max(self._holidays) 569 | else: 570 | if startdate: 571 | self._startdate = Date(startdate) 572 | else: 573 | self._startdate = Date("1970-01-01") 574 | if enddate: 575 | self._enddate = Date(enddate) 576 | else: 577 | self._enddate = Date("2071-01-01") 578 | self._index = DateIndex( 579 | self._holidays, self._startdate, self._enddate, self._nonwork_weekdays 580 | ) 581 | self.vec = VectorizedOps(self) 582 | 583 | def __get_weekdays(self): 584 | return tuple(self._weekdays[nwd] for nwd in self._nonwork_weekdays) 585 | 586 | weekdays = property(__get_weekdays) 587 | 588 | def __get_startdate(self): 589 | return self._startdate.date 590 | 591 | startdate = property(__get_startdate) 592 | 593 | def __get_enddate(self): 594 | return self._enddate.date 595 | 596 | enddate = property(__get_enddate) 597 | 598 | def __get_holidays(self): 599 | return [d.date for d in self._holidays] 600 | 601 | holidays = property(__get_holidays) 602 | 603 | def bizdays(self, date_from, date_to): 604 | """ 605 | Calculate the amount of business days between two dates 606 | 607 | Parameters 608 | ---------- 609 | 610 | date_from : datetime.date, datetime.datetime, pandas.Timestamp, str 611 | Start date 612 | 613 | date_to : datetime.date, datetime.datetime, pandas.Timestamp, str 614 | End date 615 | 616 | Returns 617 | ------- 618 | int, list, numpy.ndarray 619 | The number of business days between date_from and date_to 620 | """ 621 | if isseq(date_from) or isseq(date_to): 622 | return recseq(self.vec.bizdays(date_from, date_to), "array") 623 | else: 624 | if isnull(date_from) or isnull(date_to): 625 | return return_none() 626 | date_from = Date(date_from).date 627 | date_to = Date(date_to).date 628 | if date_from > date_to: 629 | d1, d2 = date_to, date_from 630 | else: 631 | d1, d2 = date_from, date_to 632 | t1 = (self._index[d1][0], self._index[d1][3]) 633 | t2 = (self._index[d2][0], self._index[d2][3]) 634 | i1 = t2[0] - t1[0] 635 | i2 = t2[1] - t1[1] 636 | bdays = min(i1, i2) 637 | adj_vec = int(self._index[d1][2] and self._index[d2][2]) 638 | date_reverse = date_from > date_to 639 | if date_reverse: 640 | adj_vec = -adj_vec 641 | bdays = -bdays 642 | bdays -= adj_vec 643 | if self.financial: 644 | if self._index[d1][2] and self._index[d2][2] and abs(bdays) == 1: 645 | return 0 646 | else: 647 | return bdays 648 | else: 649 | if date_reverse: 650 | return bdays - 1 651 | else: 652 | return bdays + 1 653 | 654 | def isbizday(self, dt): 655 | """ 656 | Checks if the given dates are business days. 657 | 658 | Parameters 659 | ---------- 660 | 661 | dt : datetime.date, datetime.datetime, pandas.Timestamp, str 662 | Dates to be checked 663 | 664 | Returns 665 | ------- 666 | 667 | bool, list of bool, array of bool 668 | Returns True if the given date is a business day and False 669 | otherwise. 670 | """ 671 | if isseq(dt): 672 | return recseq(self.vec.isbizday(dt), "array") 673 | else: 674 | if isnull(dt): 675 | return dt 676 | else: 677 | return not self._index[dt][2] 678 | 679 | def __adjust_next(self, dt): 680 | return Date(self._index.following(dt)).date 681 | 682 | def adjust_next(self, dt): 683 | """ 684 | Adjusts the given dates to the next business day 685 | 686 | Rolls the given date to the next business day, 687 | unless it is a business day. 688 | 689 | Parameters 690 | ---------- 691 | 692 | dt : datetime.date, datetime.datetime, pandas.Timestamp, str 693 | Dates to be adjusted 694 | 695 | Returns 696 | ------- 697 | 698 | datetime.date, datetime.datetime, pandas.Timestamp, str 699 | return the next business day if the given date is 700 | not a business day. 701 | 702 | """ 703 | if isseq(dt): 704 | return recseq(self.vec.adjust_next(dt)) 705 | else: 706 | if isnull(dt): 707 | return dt 708 | return retdate(self.__adjust_next(dt)) 709 | 710 | following = adjust_next 711 | 712 | def modified_following(self, dt): 713 | """ 714 | Adjusts the given dates to the next business day with a small 715 | difference. 716 | 717 | Rolls the given date to the next business day, 718 | unless it happens in the next month, in this case 719 | it returns the previous business day. 720 | 721 | Parameters 722 | ---------- 723 | 724 | dt : datetime.date, datetime.datetime, pandas.Timestamp, str 725 | Dates to be adjusted 726 | 727 | Returns 728 | ------- 729 | 730 | datetime.date, datetime.datetime, pandas.Timestamp, str 731 | return the next business day if the given date is 732 | not a business day. 733 | 734 | """ 735 | if isseq(dt): 736 | return recseq(self.vec.modified_following(dt)) 737 | else: 738 | if isnull(dt): 739 | return dt 740 | dtx = self._index.modified_following(dt) 741 | return retdate(dtx) 742 | 743 | def __adjust_previous(self, dt): 744 | return Date(self._index.preceding(dt)).date 745 | 746 | def adjust_previous(self, dt): 747 | """ 748 | Adjusts the given dates to the previous business day 749 | 750 | Rolls the given date to the previous business day, 751 | unless it is a business day. 752 | 753 | Parameters 754 | ---------- 755 | 756 | dt : datetime.date, datetime.datetime, pandas.Timestamp, str 757 | Dates to be adjusted 758 | 759 | Returns 760 | ------- 761 | 762 | datetime.date, datetime.datetime, pandas.Timestamp, str 763 | return the previous business day if the given date is 764 | not a business day. 765 | 766 | """ 767 | if isseq(dt): 768 | return recseq(self.vec.adjust_previous(dt)) 769 | else: 770 | if isnull(dt): 771 | return dt 772 | dt = self.__adjust_previous(dt) 773 | return retdate(dt) 774 | 775 | preceding = adjust_previous 776 | 777 | def modified_preceding(self, dt): 778 | """ 779 | Adjusts the given dates to the previous business day with a small 780 | difference. 781 | 782 | Rolls the given date to the previous business day, 783 | unless it happens in the previous month, in this case 784 | it returns the previous business day. 785 | 786 | Parameters 787 | ---------- 788 | 789 | dt : datetime.date, datetime.datetime, pandas.Timestamp, str 790 | Dates to be adjusted 791 | 792 | Returns 793 | ------- 794 | 795 | datetime.date, datetime.datetime, pandas.Timestamp, str 796 | return the previous business day if the given date is 797 | not a business day. 798 | 799 | """ 800 | if isseq(dt): 801 | return recseq(self.vec.modified_preceding(dt)) 802 | else: 803 | if isnull(dt): 804 | return dt 805 | dtx = self._index.modified_preceding(dt) 806 | return retdate(dtx) 807 | 808 | def seq(self, date_from, date_to): 809 | """ 810 | Sequence of business days. 811 | 812 | Parameters 813 | ---------- 814 | 815 | date_from : datetime.date, datetime.datetime, pandas.Timestamp, str 816 | Start date 817 | 818 | date_to : datetime.date, datetime.datetime, pandas.Timestamp, str 819 | End date 820 | 821 | Returns 822 | ------- 823 | list of dates, pandas.DatetimeIndex 824 | Returns a sequence of dates with business days only. 825 | """ 826 | _from = Date(date_from).date 827 | _to = Date(date_to).date 828 | reverse = False 829 | if _from > _to: 830 | _from, _to = _to, _from 831 | reverse = True 832 | _seq = recseq(retdate(dt) for dt in self._index.seq(_from, _to)) 833 | if reverse: 834 | _seq.reverse() 835 | return _seq 836 | 837 | def offset(self, dt, n): 838 | """ 839 | Offsets the given dates by n business days. 840 | 841 | Parameters 842 | ---------- 843 | 844 | dt : datetime.date, datetime.datetime, pandas.Timestamp, str 845 | Dates to be offset 846 | 847 | n : int, list of int 848 | the amount of business days to offset 849 | 850 | Returns 851 | ------- 852 | date, list of dates, pandas.DatetimeIndex 853 | Returns the given dates offset by the given amount of n business 854 | days. 855 | 856 | """ 857 | if isseq(dt) or isseq(n): 858 | return recseq(self.vec.offset(dt, n)) 859 | else: 860 | if isnull(dt): 861 | return dt 862 | elif isnull(n): 863 | return n 864 | return retdate(self._index.offset(dt, n)) 865 | 866 | def diff(self, dts): 867 | """ 868 | Compute the number of business days between dates in a given vector 869 | of dates. 870 | 871 | Parameters 872 | ---------- 873 | 874 | dts : list of date 875 | Sequence containing the dates to be differenced. 876 | 877 | Returns 878 | ------- 879 | 880 | list of int 881 | The number of business days between given dates. 882 | """ 883 | if len(dts) <= 1: 884 | return recseq([], "array") 885 | return self.bizdays(dts[:-1], dts[1:]) 886 | 887 | def getdate(self, expr, year, month=None): 888 | """ 889 | Get dates using other dates (or month or year) as reference. 890 | 891 | Imagine you have one date and want the first or last day of this 892 | date's month. For example, you have the date 2018-02-01 and want 893 | the last day of its month. You have to check whether or not its year 894 | is a leap year, and this sounds a tough task. getdate helps with 895 | returning specific dates according to a reference than can be another 896 | date, a month or an year. 897 | 898 | Parameters 899 | ---------- 900 | 901 | expr : str, list of str 902 | String specifying the date to be returned. 903 | 904 | See :doc:`getdate` for more information. 905 | 906 | year : int, list of int 907 | Year 908 | 909 | month : int, list of int 910 | Month 911 | 912 | Returns 913 | ------- 914 | date, list of dates, pandas.DatetimeIndex 915 | Returns dates according to a reference that can be a month or an 916 | year. 917 | 918 | """ 919 | if any([isseq(expr), isseq(year), isseq(month)]): 920 | return recseq(self.vec.getdate(expr, year, month)) 921 | else: 922 | dt = self._index.getdate(expr, year, month) 923 | return retdate(Date(dt).date) 924 | 925 | def getbizdays(self, year, month=None): 926 | """ 927 | Business days in a specific year or month. 928 | 929 | Parameters 930 | ---------- 931 | 932 | year : int, list of int 933 | Year 934 | 935 | month : int, list of int 936 | Month 937 | 938 | Returns 939 | ------- 940 | int, list of int 941 | Returns the number of business days in the given time span. 942 | 943 | """ 944 | if any([isseq(year), isseq(month)]): 945 | return recseq(self.vec.getbizdays(year, month), "array") 946 | else: 947 | return self._index.getbizdays(year, month) 948 | 949 | @classmethod 950 | def load(cls, name=None, filename=None): 951 | """ 952 | Load calendars from a file. 953 | 954 | Parameters 955 | ---------- 956 | 957 | name : str 958 | Name of the calendar. 959 | The calendar is loaded from a file delivered with the package. 960 | The calendars: 961 | 962 | * B3 963 | * ANBIMA 964 | * Actual 965 | * calendars from pandas_market_calendars - use the prefix "PMC/" to name the calendar 966 | 967 | are delivered with the package. 968 | 969 | filename : str 970 | Text file with holidays and weekdays. 971 | 972 | Returns 973 | ------- 974 | Calendar 975 | A Calendar object. 976 | 977 | """ 978 | if filename: 979 | res = _checkfile(filename) 980 | return cls._load_calendar_from_file(res) 981 | elif name: 982 | if name.startswith("PMC/"): 983 | try: 984 | import pandas_market_calendars as mcal 985 | except ImportError: 986 | raise Exception("pandas_market_calendars must be installed to use PMC calendars") 987 | cal = mcal.get_calendar(name[4:]) 988 | hol = cal.holidays() 989 | return Calendar((d.item() for d in hol.holidays), weekdays=("Saturday", "Sunday"), name=name) 990 | else: 991 | res = _checklocalfile(name) 992 | return cls._load_calendar_from_file(res) 993 | 994 | @classmethod 995 | def _load_calendar_from_file(cls, res: Dict[str, TextIO]) -> "Calendar": 996 | w = "|".join(w.lower() for w in cls._weekdays) 997 | wre = "^%s$" % w 998 | _holidays = [] 999 | _nonwork_weekdays = [] 1000 | with res["iter"] as fcal: 1001 | for cal_reg in fcal: 1002 | cal_reg = cal_reg.strip() 1003 | if cal_reg == "": 1004 | continue 1005 | if re.match(wre, cal_reg.lower()): 1006 | _nonwork_weekdays.append(cal_reg) 1007 | elif re.match(r"^\d\d\d\d-\d\d-\d\d$", cal_reg): 1008 | _holidays.append(Date(cal_reg)) 1009 | return Calendar(_holidays, weekdays=_nonwork_weekdays, name=res["name"]) 1010 | 1011 | def __str__(self): 1012 | return """Calendar: {0} 1013 | Start: {1} 1014 | End: {2} 1015 | Weekdays: {5} 1016 | Holidays: {3} 1017 | Financial: {4}""".format( 1018 | self.name, 1019 | self.startdate, 1020 | self.enddate, 1021 | len(self._holidays), 1022 | self.financial, 1023 | ", ".join(self.weekdays) if self.weekdays else "", 1024 | ) 1025 | 1026 | __repr__ = __str__ 1027 | 1028 | 1029 | def _checkfile(fname: str) -> Dict[str, TextIO]: 1030 | if not os.path.exists(fname): 1031 | raise Exception(f"Invalid calendar: {fname}") 1032 | name = os.path.split(fname)[-1] 1033 | if name.endswith(".cal"): 1034 | name = name.replace(".cal", "") 1035 | else: 1036 | name = None 1037 | return {"name": name, "iter": open(fname)} 1038 | 1039 | 1040 | def _checklocalfile(name: str) -> Dict[str, TextIO]: 1041 | dir = os.path.dirname(__file__) 1042 | fname = f"{dir}/{name}.cal" 1043 | if not os.path.exists(fname): 1044 | raise Exception(f"Invalid calendar: {name}") 1045 | name = os.path.split(fname)[-1] 1046 | if name.endswith(".cal"): 1047 | name = name.replace(".cal", "") 1048 | else: 1049 | name = None 1050 | return {"name": name, "iter": open(fname)} 1051 | 1052 | 1053 | class VectorizedOps(object): 1054 | def __init__(self, calendar): 1055 | self.cal = calendar 1056 | 1057 | def isbizday(self, dates): 1058 | return (self.cal.isbizday(dt) for dt in dates) 1059 | 1060 | def bizdays(self, dates_from, dates_to): 1061 | if not isseq(dates_from): 1062 | dates_from = [dates_from] 1063 | if not isseq(dates_to): 1064 | dates_to = [dates_to] 1065 | lengths = [len(dates_from), len(dates_to)] 1066 | if max(lengths) % min(lengths) != 0: 1067 | raise Exception( 1068 | "from length must be multiple of to length and " "vice-versa" 1069 | ) 1070 | if len(dates_from) < len(dates_to): 1071 | dates_from = cycle(dates_from) 1072 | else: 1073 | dates_to = cycle(dates_to) 1074 | return ( 1075 | self.cal.bizdays(_from, _to) for _from, _to in zip(dates_from, dates_to) 1076 | ) 1077 | 1078 | def adjust_next(self, dates): 1079 | if not isseq(dates): 1080 | dates = [dates] 1081 | return (self.cal.adjust_next(dt) for dt in dates) 1082 | 1083 | def modified_following(self, dates): 1084 | if not isseq(dates): 1085 | dates = [dates] 1086 | return (self.cal.modified_following(dt) for dt in dates) 1087 | 1088 | def adjust_previous(self, dates): 1089 | if not isseq(dates): 1090 | dates = [dates] 1091 | return (self.cal.adjust_previous(dt) for dt in dates) 1092 | 1093 | def modified_preceding(self, dates): 1094 | if not isseq(dates): 1095 | dates = [dates] 1096 | return (self.cal.modified_preceding(dt) for dt in dates) 1097 | 1098 | def offset(self, dates, ns): 1099 | if not isseq(dates): 1100 | dates = [dates] 1101 | if not isseq(ns): 1102 | ns = [ns] 1103 | if len(dates) < len(ns): 1104 | dates = cycle(dates) 1105 | else: 1106 | ns = cycle(ns) 1107 | return (self.cal.offset(dt, n) for dt, n in zip(dates, ns)) 1108 | 1109 | def getdate(self, expr, year, month): 1110 | if not isseq(expr): 1111 | expr = [expr] 1112 | if not isseq(year): 1113 | year = [year] 1114 | if not isseq(month): 1115 | month = [month] 1116 | if len(expr) >= len(year) and len(expr) >= len(month): 1117 | year = cycle(year) 1118 | month = cycle(month) 1119 | elif len(year) >= len(expr) and len(year) >= len(month): 1120 | expr = cycle(expr) 1121 | month = cycle(month) 1122 | elif len(month) >= len(expr) and len(month) >= len(year): 1123 | expr = cycle(expr) 1124 | year = cycle(year) 1125 | return (self.cal.getdate(ex, ye, mo) for ex, ye, mo in zip(expr, year, month)) 1126 | 1127 | def getbizdays(self, year, month): 1128 | if not isseq(year): 1129 | year = [year] 1130 | if not isseq(month): 1131 | month = [month] 1132 | if len(year) > len(month): 1133 | month = cycle(month) 1134 | else: 1135 | year = cycle(year) 1136 | return (self.cal.getbizdays(ye, mo) for ye, mo in zip(year, month)) 1137 | --------------------------------------------------------------------------------