Skip to main content

Year Calculator

A Python library for year calculations and calendar intelligence.

Installation

python -m pip install year-calculator

Calculate a resulting year

Use calculate_year() when you want a result that your own Python code can store, compare, or use in another calculation. It does not prompt or print.

from year_calculator import calculate_year

result = calculate_year(2007, 3287)
print(result)

Day counting follows the original whatyear() convention: day 0 is January 1. Leap years are handled automatically.

Original interactive function

whatyear() is retained for backward compatibility. It continues to prompt for missing values and print its result as before.

import year_calculator

year_calculator.whatyear(2007, 3287)

Working calendar

from year_calculator import WorkCalendar

calendar = WorkCalendar(
    year=2027,
    weekend_days=["Saturday", "Sunday"],
    holidays=["2027-01-26", "2027-08-15"],
)

print(calendar.working_days())
print(calendar.working_days(month=1))
print(calendar.monthly_report())

working_days() counts dates inclusively. Holidays are supplied by the user as datetime.date objects or YYYY-MM-DD strings and must belong to the years covered by the WorkCalendar. No country-specific holiday database is included.

Calendars that span several years

By default a WorkCalendar covers one year. Pass end_year to cover a longer period, so that date ranges, deadlines and leave plans can cross New Year.

from year_calculator import WorkCalendar

calendar = WorkCalendar(2027, end_year=2028, holidays=["2028-01-03"])

print(calendar.working_days("2027-12-27", "2028-01-07"))  # 9
print(calendar.working_days(month=1, year=2028))          # January 2028
print(calendar.next_working_day("2027-12-31"))            # 2028-01-04

month= uses the first year unless you also pass year=.

Deadlines in working days

calendar = WorkCalendar(2027, end_year=2028, holidays=["2028-01-03"])

print(calendar.add_working_days("2027-12-30", 3))   # 2028-01-05
print(calendar.add_working_days("2028-01-05", -3))  # 2027-12-30

Counting starts the day after the start date, so days=1 is the first working day after it. The start date itself may be a weekend or holiday. A negative number counts backwards, and 0 returns the start date unchanged. If the calendar ends before enough working days are found, a ValueError tells you to create the WorkCalendar with a later end_year.

Explain a date

info = calendar.explain("2028-01-03")
print(info)             # 2028-01-03 (Monday): not a working day: holiday
print(info.is_working)  # False
print(info.reasons)     # ('holiday',)

reasons can contain "weekend", "holiday" and "day off (no shifts scheduled)". For working days, info.shifts and info.hours show the scheduled shifts.

Find the next working shift

start, end = calendar.next_working_window("2027-12-31T18:00")

If the moment falls inside a shift, that shift is returned. Otherwise the next upcoming shift is returned. Date-times must be naive (no time zone). Shift ends are exclusive, so 17:00 on a 09:00-17:00 day returns the next day.

Plan leave for the longest breaks

plans = calendar.best_leave_plan(leave_days=5, top=3)

for plan in plans:
    print(plan.start, plan.end, plan.total_days_off)
    print("take leave on:", plan.leave_dates)

Weekends and holidays are free. Each working day inside a stretch costs one leave day. Plans are returned best first (longest stretch of days off) and never overlap. Restrict the search with start_date and end_date.

Configurable working schedules

By default, every non-weekend, non-holiday weekday has a 09:00-17:00 shift. You can override individual weekdays and configure multiple shifts to account for breaks. A weekday explicitly mapped to an empty list is treated as a day off. Weekend days and holidays always remain non-working days.

from year_calculator import WorkCalendar

calendar = WorkCalendar(
    year=2027,
    holidays=["2027-01-26"],
    working_schedule={
        "Monday": [("09:00", "12:00"), ("13:00", "17:00")],
        "Tuesday": [("09:00", "17:00")],
        "Friday": [("09:00", "13:00")],
    },
)

print(calendar.daily_schedule("2027-01-04"))
print(calendar.scheduled_hours("2027-01-01", "2027-01-31"))
print(calendar.scheduled_hours(month=1))

Schedule weekday keys can be full weekday names or integers (Monday=0 to Sunday=6). Times can be HH:MM strings or datetime.time objects. Shifts must have a positive duration and must not overlap. Overnight shifts and timezone-aware schedule times are not supported in this version.

working_hours(hours_per_day=8, ...) multiplies the number of working days by a fixed number of hours. Use scheduled_hours(...) to calculate hours from the configured weekday shifts.

A weekday mapped to an empty list is a day off everywhere: it is not counted by working_days() and has no hours.

Run tests

From the project root:

python -m unittest discover -s tests -v

License

Released under the MIT License. See the LICENSE file for details.

Metadata

Release files for year-calculator 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for year-calculator 0.2.0
File Size Uploaded Provenance Actions
year_calculator-0.2.0.tar.gz 16.8 kB Without provenance Details for year_calculator-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for year-calculator 0.2.0
File Interpreter ABI Platform Actions
year_calculator-0.2.0-py3-none-any.whl Python 3 none any Details for year_calculator-0.2.0-py3-none-any.whl

Total release size: 28.0 kB

Release files / year_calculator-0.2.0.tar.gz

Download URL year_calculator-0.2.0.tar.gz
Size 16.8 kB
Tags Source
SHA-256 checksum
How to use checksums
83a1e30f6bb81143c9ca943fd4e41c5e9d0b0490bb5158dbc8214b6301576521
BLAKE2b-256 checksum
How to use checksums
07260678c13fd8100f5ddb3d8c3833d12846d121dbcb000959da29cc2912dff9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Release files / year_calculator-0.2.0-py3-none-any.whl

Download URL year_calculator-0.2.0-py3-none-any.whl
Size 11.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f48ed12b26812a330f397fb4bdd8ba29c9447a678c08b6db9b2101892bd4365c
BLAKE2b-256 checksum
How to use checksums
37a3c91cecbe669d6a456993ea8c22d4f654c7e4e12ef5358479134cd2a47c29
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Security for year-calculator 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page