Business Day Tools
count(start, end)
Count business days between start (inclusive) and end (exclusive) with
Brazilian holiday adjustment and per-row holiday regime selection.
ORDER PRESERVATION (critical): The output order ALWAYS matches the element-wise
order of the original inputs. No sorting, deduplication, alignment or reshaping is
performed. If you pass arrays, the i-th result corresponds to the i-th pair of
(start, end) after broadcasting. This guarantees safe assignment back to the
originating DataFrame.
Holiday regime: For each start value, the holiday list (old vs. new) is chosen
based on the transition date 2023-12-26 (TRANSITION_DATE). Starts before the
transition use the old list for that row's count; starts on/after use the new list.
Null propagation: If any scalar argument is null, returns None. Nulls inside
array inputs yield nulls in corresponding result positions.
Return type: If both inputs are scalars (non-null) an int is returned; otherwise
a polars.Series of int counts (name: 'bday_count').
If a null scalar short-circuits, None is returned.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
None | DateLike | ArrayLike
|
Single date or collection (inclusive boundary). |
required |
end
|
None | DateLike | ArrayLike
|
Single date or collection (exclusive boundary). |
required |
Returns:
| Type | Description |
|---|---|
None | int | Series
|
int | pl.Series | None: Returns an integer or None if |
Notes
- This function is a wrapper around
polars.business_day_count. - The holiday list is determined per-row based on the
startdate.
Examples:
Total business days in January and February since the start of the year
>>> bday.count(start="01-01-2024", end=["01-02-2024", "01-03-2024"])
shape: (2,)
Series: 'bday_count' [i64]
[
22
41
]
The remaining business days from January/February until the end of the year
>>> bday.count(["01-01-2024", "01-02-2024"], "01-01-2025")
shape: (2,)
Series: 'bday_count' [i64]
[
253
231
]
The total business days in January and February of 2024
>>> bday.count(["01-01-2024", "01-02-2024"], ["01-02-2024", "01-03-2024"])
shape: (2,)
Series: 'bday_count' [i64]
[
22
19
]
Null values are propagated
>>> bday.count("01-01-2024", ["01-02-2024", None]) # None in end array
shape: (2,)
Series: 'bday_count' [i64]
[
22
null
]
>>> start_dates = ["01-01-2024", "01-02-2024", "01-03-2024"]
>>> bday.count(start_dates, "01-01-2025")
shape: (3,)
Series: 'bday_count' [i64]
[
253
231
212
]
Source code in pyield/bday/core.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
generate(start=None, end=None, inclusive='both', holiday_option='new')
Generates a Series of business days between a start and end date, considering
the list of Brazilian holidays. It supports customization of holiday lists and
inclusion options for start and end dates. It wraps pandas.bdate_range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
DateLike | None
|
The start date for generating the dates. If None, the current date is used. Defaults to None. |
None
|
end
|
DateLike | None
|
The end date for generating business days. If None, the current date is used. Defaults to None. |
None
|
inclusive
|
Literal['both', 'neither', 'left', 'right']
|
Determines which of the start and end dates are included in the result. Valid options are 'both', 'neither', 'left', 'right'. Defaults to 'both'. |
'both'
|
holiday_option
|
Literal['old', 'new', 'infer']
|
Specifies the list of holidays to consider. Defaults to "new".
- 'old': Uses the holiday list effective before the transition date
of 2023-12-26.
- 'new': Uses the holiday list effective on and after the transition
date of 2023-12-26.
- 'infer': Automatically selects the holiday list ('old' or 'new') based
on the |
'new'
|
Returns:
| Type | Description |
|---|---|
Series
|
pl.Series: A Series representing a range of business days between the specified start and end dates, considering the specified holidays. |
Examples:
>>> from pyield import bday
>>> bday.generate(start="22-12-2023", end="02-01-2024")
shape: (6,)
Series: 'bday' [date]
[
2023-12-22
2023-12-26
2023-12-27
2023-12-28
2023-12-29
2024-01-02
]
Note
For detailed information on parameters and error handling, refer to
pandas.bdate_range documentation:
https://pandas.pydata.org/docs/reference/api/pandas.bdate_range.html.
Source code in pyield/bday/core.py
is_business_day(dates)
Determine whether date(s) are Brazilian business days with per-element holiday regime selection.
PER-ROW HOLIDAY REGIME: For EACH input date the appropriate holiday list
("old" vs. "new") is selected by comparing to the transition date
2023-12-26 (TRANSITION_DATE). Dates strictly before the transition
use the old list; dates on or after it use the new list. This mirrors the
behavior of count and offset which apply regime logic element-wise.
ORDER & SHAPE PRESERVATION: The output preserves the original element order. No sorting, deduplication, reshaping or alignment is performed; the i-th result corresponds to the i-th provided date after broadcasting (if any broadcasting occurred from a scalar input elsewhere in the call chain).
NULL PROPAGATION: A null scalar argument short-circuits to None. Null
values inside array-like inputs produce nulls at the corresponding output
positions.
RETURN TYPE: If the (non-null) input resolves to a single element a Python
bool is returned. If that lone element is null, None is returned.
Otherwise a polars.Series of booleans named 'is_bday' is produced.
WEEKENDS: Saturdays and Sundays are never business days regardless of the holiday regime.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dates
|
None | DateLike | ArrayLike
|
Single date or collection (list/tuple/ndarray/Polars/Pandas
Series). May include nulls which propagate. Null scalar input
returns |
required |
Returns:
| Type | Description |
|---|---|
None | bool | Series
|
bool | pl.Series | None: |
None | bool | Series
|
for scalar input; |
None | bool | Series
|
Series (name: |
Examples:
>>> from pyield import bday
>>> bday.is_business_day("25-12-2023") # Christmas (old calendar)
False
>>> bday.is_business_day("20-11-2024") # National Zumbi Day (new holiday)
False
>>> bday.is_business_day(["22-12-2023", "26-12-2023"]) # Mixed periods
shape: (2,)
Series: 'is_bday' [bool]
[
true
true
]
Notes
- Transition date defined in
TRANSITION_DATE. - Mirrors per-row logic used in
countandoffset. - Weekends always evaluate to
False. - Null elements propagate.
Source code in pyield/bday/core.py
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 | |
last_business_day()
Returns the last business day in Brazil. If the current date is a business day, it returns the current date. If it is a weekend or holiday, it returns the last business day before the current date.
Returns:
| Type | Description |
|---|---|
date
|
dt.date: The last business day in Brazil. |
Notes
- The determination of the last business day considers the correct Brazilian holiday list (before or after the 2023-12-26 transition) applicable to the current date.
Source code in pyield/bday/core.py
offset(dates, offset, roll='forward')
Offset date(s) by a number of business days with per-row Brazilian holiday
regime selection. The operation is performed in two steps per element:
1) ROLL: If the original date falls on a weekend or holiday, move it according
to roll ("forward" -> next business day; "backward" -> previous).
2) ADD: Apply the signed business-day offset (positive forward, negative
backward, zero = stay on the rolled date).
ORDER PRESERVATION (critical): Output ordering strictly matches the element-wise
pairing after broadcasting between dates and offset. No sorting,
deduplication or shape changes occur. The i-th result corresponds to the i-th
(date, offset) pair, enabling safe assignment back into the originating DataFrame.
Holiday regime: For EACH date the appropriate holiday list (old vs. new) is
chosen based on the transition date 2023-12-26 (TRANSITION_DATE). Dates
before the transition use the old list; dates on/after use the new list.
Roll semantics: roll only acts when the original date is not already a
business day under its regime. After rolling, the subsequent business-day
addition is applied from that rolled anchor. An offset of 0 therefore
returns either the original date (if already a business day) or the rolled
business day.
Null propagation: If any scalar argument is null, the function short-circuits
to None. Nulls inside array inputs propagate to their corresponding output
positions.
Broadcasting: dates and offset may be scalars or array-like. Standard
Polars broadcasting rules apply when constructing the per-row pairs.
Return type: If both inputs are non-null scalars a datetime.date is returned.
Otherwise a polars.Series of dates named 'adjusted_date' is produced.
Null scalar inputs yield None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dates
|
DateLike | ArrayLike | None
|
Single date or collection of dates to be rolled (if needed) and then offset. Each date independently selects the holiday regime. |
required |
offset
|
int | ArrayLike | None
|
Signed count of business days to apply after rolling. Positive moves forward, negative backward, zero keeps the rolled anchor. |
required |
roll
|
Literal['forward', 'backward']
|
Direction to roll a non-business starting date ("forward" or "backward"). Defaults to "forward". |
'forward'
|
Returns:
| Type | Description |
|---|---|
date | Series | None
|
dt.date | pl.Series | None: A Python |
date | Series | None
|
Series of dates for any array input, or |
date | Series | None
|
was provided. |
Notes
- Wrapper around
polars.Expr.dt.add_business_daysapplied conditionally. - Holiday regime is decided per element by comparing to
TRANSITION_DATE. - Weekends are always treated as non-business days.
Examples:
Offset Saturday before Christmas to the next b. day (Tuesday after Christmas)
Offset Friday before Christmas (no offset because it's a business day)
Offset to the previous business day if not a bday (offset=0 and roll="backward")
No offset because it's a business day
Offset to the first business day before "23-12-2023"
Jump to the next business day (1 offset and roll="forward")
Offset Friday to the next business day (Friday is jumped -> Monday)
Offset Saturday to the next business day (Monday is jumped -> Tuesday)
Jump to the previous business day (-1 offset and roll="backward")
Offset Friday to the previous business day (Friday is jumped -> Thursday)
Offset Saturday to the previous business day (Friday is jumped -> Thursday)
List of dates and offsets
>>> bday.offset(["19-09-2024", "20-09-2024"], 1)
shape: (2,)
Series: 'adjusted_date' [date]
[
2024-09-20
2024-09-23
]
>>> bday.offset("19-09-2024", [1, 2]) # a list of offsets
shape: (2,)
Series: 'adjusted_date' [date]
[
2024-09-20
2024-09-23
]
Scalar nulls propagate to None
Scalar null propagates inside arrays
Nulls inside arrays are preserved
>>> bday.offset(["19-09-2024", None], 1)
shape: (2,)
Series: 'adjusted_date' [date]
[
2024-09-20
null
]
>>> dates = ["19-09-2024", "20-09-2024", "21-09-2024"]
>>> bday.offset(dates, 1)
shape: (3,)
Series: 'adjusted_date' [date]
[
2024-09-20
2024-09-23
2024-09-24
]
Note
This function uses polars.Expr.dt.add_business_days under the hood. For
detailed information, refer to the Polars documentation.
Source code in pyield/bday/core.py
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | |