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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.