Skip to main content

Composable business calendars for Django with holidays, intraday schedules, and timezone-aware operations.

Project description

django-bizcal

PyPI version Python versions CI License: MIT

django-bizcal is a production-oriented Python library for Django projects that need composable business calendars with official holidays, custom holidays, intraday schedules, timezone-aware arithmetic, and reusable service integration.

It is designed for SLA clocks, operational workflows, due dates, approvals, support desks, tenant-specific calendars, and country-specific business hours.

Highlights

  • Pure domain core with no ORM coupling.
  • Official holidays via holidays.
  • Custom organization or tenant holidays in memory.
  • Intraday schedules with multiple windows per weekday.
  • Calendar composition with union, intersection, difference, and override.
  • Explicit timezone support based on zoneinfo.
  • Reusable Django app with namespaced settings and service helpers.
  • Modern packaging with pyproject.toml, wheel/sdist builds, pytest, and GitHub Actions.

Installation

pip install django-bizcal

For local development:

pip install -e ".[dev]"

Quickstart

from datetime import datetime
from zoneinfo import ZoneInfo

from django_bizcal import UnionCalendar, WorkingCalendar

cl = WorkingCalendar.from_country(
    country="CL",
    years=[2026, 2027],
    tz="America/Santiago",
    weekly_schedule={
        0: [("09:00", "13:00"), ("14:00", "18:00")],
        1: [("09:00", "13:00"), ("14:00", "18:00")],
        2: [("09:00", "13:00"), ("14:00", "18:00")],
        3: [("09:00", "13:00"), ("14:00", "18:00")],
        4: [("09:00", "13:00"), ("14:00", "17:00")],
    },
    extra_holidays=["2026-12-24", "2026-12-31"],
)

mx = WorkingCalendar.from_country(
    country="MX",
    years=[2026, 2027],
    tz="America/Mexico_City",
    weekly_schedule={
        0: [("09:00", "18:00")],
        1: [("09:00", "18:00")],
        2: [("09:00", "18:00")],
        3: [("09:00", "18:00")],
        4: [("09:00", "18:00")],
    },
)

regional = UnionCalendar([cl, mx], tz="UTC")

start = datetime(2026, 1, 5, 15, 0, tzinfo=ZoneInfo("UTC"))
deadline = regional.add_business_hours(start, 10)
elapsed = regional.business_minutes_between(start, deadline)

Django integration

Add the reusable app:

INSTALLED_APPS = [
    ...,
    "django_bizcal",
]

Configure a default calendar:

BIZCAL_DEFAULT_TIMEZONE = "America/Santiago"
BIZCAL_DEFAULT_COUNTRY = "CL"
BIZCAL_PRELOAD_YEARS = [2026, 2027]
BIZCAL_DEFAULT_CALENDAR = {
    "type": "working",
    "tz": "America/Santiago",
    "country": "CL",
    "years": [2026, 2027],
    "weekly_schedule": {
        "0": [["09:00", "13:00"], ["14:00", "18:00"]],
        "1": [["09:00", "13:00"], ["14:00", "18:00"]],
        "2": [["09:00", "13:00"], ["14:00", "18:00"]],
        "3": [["09:00", "13:00"], ["14:00", "18:00"]],
        "4": [["09:00", "13:00"], ["14:00", "17:00"]],
    },
    "extra_holidays": ["2026-12-24", "2026-12-31"],
}

Consume it from application code:

from django_bizcal.services import get_default_calendar

calendar = get_default_calendar()
deadline = calendar.add_business_hours(ticket.created_at, 8)

Calendar builder

from django_bizcal import CalendarBuilder

calendar = CalendarBuilder.from_dict(
    {
        "type": "union",
        "tz": "UTC",
        "children": [
            {
                "type": "working",
                "country": "CL",
                "years": [2026, 2027],
                "tz": "America/Santiago",
                "weekly_schedule": {
                    "0": [["09:00", "18:00"]],
                    "1": [["09:00", "18:00"]],
                    "2": [["09:00", "18:00"]],
                    "3": [["09:00", "18:00"]],
                    "4": [["09:00", "18:00"]],
                },
            },
            {
                "type": "working",
                "country": "MX",
                "years": [2026, 2027],
                "tz": "America/Mexico_City",
                "weekly_schedule": {
                    "0": [["09:00", "18:00"]],
                    "1": [["09:00", "18:00"]],
                    "2": [["09:00", "18:00"]],
                    "3": [["09:00", "18:00"]],
                    "4": [["09:00", "18:00"]],
                },
            },
        ],
    }
)

Architecture

  • The domain core lives in src/django_bizcal and stays framework-light.
  • WorkingCalendar handles business schedules and holiday lookup.
  • Composition calendars project child windows into a reference timezone.
  • The Django layer only wraps settings, AppConfig, and service helpers.
  • V1 intentionally ships without database models to keep the public core stable and reusable.

See the full documentation in:

Compatibility

  • Python 3.11+
  • Django 4.2, 5.0, 5.1

Limitations

  • Official holiday lookup requires the relevant years to be preloaded.
  • Wall-clock times are interpreted with zoneinfo; DST transitions affect real elapsed durations.
  • V1 does not persist calendar definitions in the database.

Release

python -m build
pytest

Publishing guidance is documented in docs/release.md.

Support

If django-bizcal helps your team, consider sponsoring ongoing maintenance, documentation, and new features through GitHub Sponsors or by reaching out for support and implementation work.

Project details


Download files

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

Source Distribution

django_bizcal-0.1.1.tar.gz (21.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_bizcal-0.1.1-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file django_bizcal-0.1.1.tar.gz.

File metadata

  • Download URL: django_bizcal-0.1.1.tar.gz
  • Upload date:
  • Size: 21.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for django_bizcal-0.1.1.tar.gz
Algorithm Hash digest
SHA256 0d92d970ad9d62c949ef78ff2e134aad6e4120733a7d9622642d873bd83fd51f
MD5 32ddf36a653f60e2e396e9bba7e1f6e2
BLAKE2b-256 21c31b80d3cc83c2fa807a7cc98df88866579b2ba4cbb2ba9dc671e5d82fd494

See more details on using hashes here.

File details

Details for the file django_bizcal-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: django_bizcal-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for django_bizcal-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0e8b2f626867672fab10ef2cf6fcb4ea3f53b167f29d168c1b80b59a77462a00
MD5 b279b1dd1246e7a352514d2d9f28ae2b
BLAKE2b-256 d3ab07236c46a794f4f78dff660e913eb30c036777fc6fcc6896d2687278e30f

See more details on using hashes here.

Supported by

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