Skip to content

API Reference

Scale

BusdayScale

BusdayScale(
    axis,
    bushours=(0, 24),
    weekmask=None,
    holidays=None,
    busdaycal=None,
)

Bases: mscale.ScaleBase

Matplotlib scale that compresses off-hours and non-business days.

Maps datetime values to business-day coordinates. Days and hours outside the defined schedule are collapsed so that every visible unit on the axis corresponds to active time.

Registered as the "busday" scale by busdayaxis.register_scale(). After registration, all parameters below except axis can be passed directly to ax.set_xscale("busday", ...). axis is injected automatically by Matplotlib and must not be passed explicitly.

PARAMETER DESCRIPTION
axis

Injected automatically by Matplotlib. Do not pass this via ax.set_xscale.

TYPE: matplotlib.axis.Axis

bushours
    | Sequence[tuple[HourValue, HourValue]]
    | Mapping[WeekdayKey, tuple[HourValue, HourValue]]
    , optional

Active hours per weekday.

HourValue is int | float | str | datetime.time. Strings must be valid ISO time strings (e.g. "09:30"). Numbers are hours since midnight (e.g. 9.5 = 09:30). WeekdayKey means either weekday index 0..6 or weekday name "Mon".."Sun".

Three accepted forms:

  • tuple[HourValue, HourValue] = (start, end): Same session for all days. The weekmask still applies, so off-days (Sat/Sun by default) are collapsed regardless. Default (0, 24).

    Example:

    bushours=(9, 17)                    # numeric hours
    bushours=("09:00", "17:00")         # ISO time strings
    bushours=(dt.time(9), dt.time(17))  # datetime.time objects
    

    To apply also on weekends, use:

    bushours=(9, 17), weekmask="1111111"  # Mon–Sun
    
  • Sequence[tuple[HourValue, HourValue]] with length 7: One tuple per weekday Monday–Sunday (index 0..6), fully explicit.

    Example:

    bushours=[
        (9, 17),  # Mon
        (9, 17),  # Tue
        (9, 17),  # Wed
        (9, 17),  # Thu
        (9, 17),  # Fri
        (0, 0),   # Sat
        (0, 0),   # Sun
    ]
    
  • Mapping[WeekdayKey, tuple[HourValue, HourValue]]: Per-day overrides; keys are WeekdayKey values (integers 0..6 or names "Mon""Sun"). Defaults for unspecified days:

    Day Key Default
    Mon 0 (0, 24)
    Tue 1 (0, 24)
    Wed 2 (0, 24)
    Thu 3 (0, 24)
    Fri 4 (0, 24)
    Sat 5 (0, 0)
    Sun 6 (0, 0)

    The weekmask is derived automatically: days with non-zero hours are treated as business days, so passing {"Sun": (10, 18)} will show Sundays without a separate weekmask override.

    Example:

    bushours={"Sun": (10, 18)}  # Sundays with custom hours, Weekdays 00-24
    

    FX Trading Hours:

    bushours={
        "Sun": (22, 24),
        # Monday - Thursday 00:00-24:00
        "Fri": (0, 22),
    }
    

TYPE: tuple[HourValue, HourValue] DEFAULT: (0, 24)

weekmask

Which weekdays are business days ("1" = on, "0" = off). Passed to :func:numpy.is_busday. When None (default):

  • For dict / list-of-7 bushours: derived automatically — days with non-zero hours become business days.
  • For uniform (start, end) bushours: "1111100" (Mon–Fri).

Use a string of 7 characters (Mon–Sun), a space-separated list of three-letter day names, or any format accepted by numpy.is_busday:

weekmask="1111100"              # Mon–Fri (default)
weekmask="Mon Tue Wed Thu Fri"  # equivalent
weekmask="Sun Mon Tue Wed Thu"  # Middle-Eastern work week
weekmask="1111111"              # every day is a business day

TYPE: str, array-like, or None DEFAULT: None

holidays

Extra non-business dates, regardless of weekmask. Dates on these days are collapsed to zero width on the axis, identical to weekends. Accepts any format understood by :func:numpy.is_busday (ISO strings, datetime.date, numpy.datetime64). Default None.

holidays=["2025-01-01", "2025-12-25"]

TYPE: array - like or None DEFAULT: None

busdaycal

Pre-built calendar combining a weekmask and holiday list. When set, weekmask and holidays are ignored. Useful when reusing the same calendar across multiple axes. Default None.

Note that busdaycal only controls which days are business days; it does not affect bushours. The two parameters are independent.

cal = np.busdaycalendar(weekmask="Mon Tue Wed Thu Fri",
                        holidays=["2025-01-01"])
ax.set_xscale("busday", busdaycal=cal)

TYPE: numpy.busdaycalendar or None DEFAULT: None

Notes

Timestamps outside bushours are clipped to the nearest session boundary during the forward transform. For example, with bushours=(9, 17), both 08:30 and 09:00 map to the same axis position (the session open). This means the transform is not perfectly invertible: the inverse transform always returns a time within business hours, even if the original value was outside.

Examples:

Compress weekends only (Mon–Fri default):

ax.set_xscale("busday")

Compress overnight gaps as well as weekends:

ax.set_xscale("busday", bushours=(9, 17))

Per-day hours — Friday early close at 16:00, weekends closed:

ax.set_xscale("busday", bushours={"Mon": (9, 17), "Fri": (9, 16)})

Show Sundays with custom hours; weekmask derived automatically (Sat excluded):

ax.set_xscale("busday", bushours={"Sun": (10, 18)})

FX-style session: Sunday open 22:00, Friday close 22:00, full days otherwise:

ax.set_xscale("busday", bushours={"Sun": (22, 24), "Fri": (0, 22)})

Middle-Eastern work week (Sun–Thu) with a holiday:

ax.set_xscale("busday", weekmask="Sun Mon Tue Wed Thu",
              holidays=["2025-01-01"])

Reuse a pre-built numpy.busdaycalendar:

cal = np.busdaycalendar(weekmask="1111100", holidays=["2025-12-25"])
ax.set_xscale("busday", busdaycal=cal)

Custom tick placement with BusdayLocator:

import matplotlib.dates as mdates
ax.set_xscale("busday", bushours=(9, 17))
ax.xaxis.set_major_locator(
    BusdayLocator(mdates.HourLocator(byhour=range(9, 18)))
)

register_scale

register_scale()

Register the "busday" scale with Matplotlib.

Call this once before any plotting code. After registration, ax.set_xscale("busday", ...) is available for the lifetime of the Python session. The keyword arguments bushours, weekmask, holidays, and busdaycal are forwarded directly to busdayaxis.BusdayScale; axis is injected by Matplotlib automatically and cannot be passed.

Examples:

import busdayaxis
import matplotlib.pyplot as plt
import pandas as pd

busdayaxis.register_scale()  # register once

dates = pd.date_range("2025-01-01", periods=10, freq="D")
values = range(10)

fig, ax = plt.subplots()
ax.plot(dates, values)
ax.set_xscale("busday", bushours=(9, 17))  # kwargs forwarded to BusdayScale
plt.show()

Locator

BusdayLocator

BusdayLocator(base_locator=None, keep_midnight_ticks=None)

Bases: mdates.DateLocator

Tick locator that filters out ticks outside business hours and business days.

Wraps any Matplotlib date locator and discards ticks that fall on non-business days or outside the active session defined by bushours.

The locator reads the business-hours and weekmask configuration from the axis automatically (set by BusdayScale), so it stays in sync with the scale without any extra configuration.

BusdayLocator is set automatically when you call ax.set_xscale("busday").

PARAMETER DESCRIPTION
base_locator

The underlying datetime locator that proposes tick candidates. If None, falls back to AutoDateLocator.

TYPE: matplotlib.dates.DateLocator DEFAULT: None

keep_midnight_ticks

Controls whether ticks at midnight (00:00) on business days are kept even when they fall outside bushours. When None (default), this is determined automatically: midnight ticks are kept for daily-granularity locators (e.g. DayLocator) so that day labels remain properly aligned, and suppressed for finer locators (e.g. HourLocator) where they would appear outside the visible session.

TYPE: bool or None DEFAULT: None

Examples:

Hourly ticks during business hours (9–17)::

ax.set_xscale("busday", bushours=(9, 17))
ax.xaxis.set_major_locator(
    BusdayLocator(mdates.HourLocator())
)

Daily ticks on business days only::

ax.xaxis.set_major_locator(BusdayLocator(mdates.DayLocator()))

DayLocator

DayLocator(keep_midnight_ticks=None, **kwargs)

Bases: BusdayLocator

Business-day-aware wrapper around matplotlib.dates.DayLocator.

Places one tick per day (or every interval days), then discards any that fall on non-business days. Midnight ticks are always kept because daily ticks are placed at day boundaries.

All keyword arguments are forwarded to ~matplotlib.dates.DayLocator.

HourLocator

HourLocator(keep_midnight_ticks=None, **kwargs)

Bases: BusdayLocator

Business-day-aware wrapper around matplotlib.dates.HourLocator.

Places ticks at the specified hours, then discards any that fall outside business days or business hours. Midnight ticks are not kept by default because hourly ticks are sub-daily.

All keyword arguments are forwarded to ~matplotlib.dates.HourLocator.

MicrosecondLocator

MicrosecondLocator(keep_midnight_ticks=None, **kwargs)

Bases: BusdayLocator

Business-day-aware wrapper around matplotlib.dates.MicrosecondLocator.

Places ticks at the specified microseconds, then discards any that fall outside business days or business hours. Midnight ticks are not kept by default because microsecond-level ticks are sub-daily.

All keyword arguments are forwarded to ~matplotlib.dates.MicrosecondLocator.

MidBusdayLocator

Bases: mdates.DateLocator

Places one tick at the midpoint of the business hours for each business day.

Useful for centering day labels within each session, even when business hours vary by weekday or differ from the standard 9–17 window.

Examples:

Center day labels as minor ticks::

ax.set_xscale("busday", bushours=(9, 17))
ax.xaxis.set_minor_locator(busdayaxis.MidBusdayLocator())
ax.xaxis.set_minor_formatter(mdates.DateFormatter("%a"))

MinuteLocator

MinuteLocator(keep_midnight_ticks=None, **kwargs)

Bases: BusdayLocator

Business-day-aware wrapper around matplotlib.dates.MinuteLocator.

Places ticks at the specified minutes, then discards any that fall outside business days or business hours. Midnight ticks are not kept by default because minute-level ticks are sub-daily.

All keyword arguments are forwarded to ~matplotlib.dates.MinuteLocator.

SecondLocator

SecondLocator(keep_midnight_ticks=None, **kwargs)

Bases: BusdayLocator

Business-day-aware wrapper around matplotlib.dates.SecondLocator.

Places ticks at the specified seconds, then discards any that fall outside business days or business hours. Midnight ticks are not kept by default because second-level ticks are sub-daily.

All keyword arguments are forwarded to ~matplotlib.dates.SecondLocator.

WeekdayLocator

WeekdayLocator(keep_midnight_ticks=None, **kwargs)

Bases: BusdayLocator

Business-day-aware wrapper around matplotlib.dates.WeekdayLocator.

Places ticks on specified weekdays, then discards any that fall on non-business days (e.g. public holidays). Midnight ticks are always kept because weekday ticks are placed at day boundaries.

All keyword arguments are forwarded to ~matplotlib.dates.WeekdayLocator.