Calendar-based time window filtering, age calculations, and business logic for dates and times.
Project description
Frist: Unified Age and Calendar Logic
Fristis a modern Python library designed to make working with time, dates, and intervals simple and expressive—whether you’re analyzing file ages, tracking events, or handling business calendars. Frist provides two core property-based APIs: Age and Cal. The Age object lets you answer “How old is this?” for any two datetimes (often defaulting to “now”), making it perfect for file aging, log analysis, or event tracking. The Cal object lets you ask “Is this date in a specific window?”—such as today, yesterday, this month, this quarter, or this fiscal year—using intuitive properties for calendar logic. Calendar ranges are always aligned to a calendar time scale, day, business day, month, year, quarter, hour.
You never need to do manual calendar math. Frist’s property-based API gives you direct answers to common time and calendar questions. For business and operational use cases, Frist’s policy object lets you define workdays, holidays, and business hours, so your calendar calculations match your real-world rules. Whether you need precise age calculations, flexible date windows, or custom business logic, Frist unifies these features in a clean, easy-to-use API built entirely around properties.
>>> from frist import Chrono
>>> import datetime as dt
>>> meeting_time = dt.datetime(2025, 4, 25, 15, 0) # Meeting 5 days ago
>>> today = dt.datetime(2025, 4, 30)
>>> meeting = Chrono(target_time=meeting_time, reference_time=today)
>>> f"Meeting age: {meeting.age.days:.2f} days"
'Meeting age: 5.00 days'
>>> meeting.cal.in_days(0)
False
>>> meeting.cal.in_days(-5)
True
>>> meeting.cal.in_months(0)
True
>>> meeting.cal.in_months(0, 2)
True
>>> meeting.cal.in_months(-2)
False
>>> other_day = dt.datetime(2025, 5, 1)
>>> meeting_other = Chrono(target_time=meeting_time, reference_time=other_day)
>>> meeting_other.cal.in_days(0)
False
>>> meeting_other.cal.in_months(0)
True
>>> meeting_other.cal.in_months(0, 2)
False
>>> meeting_other.cal.in_months(-2)
True
CalendarPolicy
The CalendarPolicy object lets you customize business logic for calendar calculations using half open intervals You can define:
- Workdays: Any combination of weekdays (e.g., Mon, Wed, Fri, Sun)
- Holidays: Any set of dates to exclude from working day calculations
- Business hours: Custom start/end times for each day
- Fiscal year start: Set the starting month for fiscal calculations
Default Policy:
If you do not provide a CalendarPolicy, Frist uses a default policy:
- Workdays: Monday–Friday (0–4)
- Work hours: 9AM–5PM
- Holidays: none
This is suitable for most standard business use cases. You only need to provide a custom CalendarPolicy if your calendar logic requires non-standard workweeks, holidays, or business hours.
Example (custom policy):
>>> from frist import CalendarPolicy
>>> import datetime as dt
>>> policy = CalendarPolicy(workdays={0,1,2,3,4}, holidays={"2025-1-1"}, work_hours=(9,17), fy_start_month=4)
>>> date = dt.datetime(2025, 5, 15)
>>> policy.get_fiscal_year(date)
2026
>>> policy.get_fiscal_quarter(date)
1
>>> policy.is_holiday(dt.datetime(year=2025,month=1,day=1))
True
API Reference
Age Object
>>> from frist import Age
>>> import datetime as dt
>>> start = dt.datetime(2000, 1, 1)
>>> end = dt.datetime(2025, 5, 1)
>>> age = Age(start_time=start, end_time=end)
>>> age.years
25.33
>>> age.days
9252
>>> age.months
303.98
>>> age.working_days
6573.0
Age(start_time: datetime, end_time: datetime = None, cal_policy: CalendarPolicy = None)
| Property | Description |
|---|---|
seconds |
Age in seconds |
minutes |
Age in minutes |
hours |
Age in hours |
days |
Age in days |
weeks |
Age in weeks |
months |
Age in months (approximate, 30.44 days) |
months_precise |
Age in months (precise, calendar-based) |
years |
Age in years (approximate, 365.25 days) |
years_precise |
Age in years (precise, calendar-based) |
working_days |
Fractional working days between start and end, per policy |
fiscal_year |
Fiscal year for start_time |
fiscal_quarter |
Fiscal quarter for start_time |
start_time |
Start datetime |
end_time |
End datetime |
cal_policy |
CalendarPolicy used for business logic |
| Method | Description |
|---|---|
set_times(start_time=None, end_time=None) |
Update start/end times |
parse(age_str) |
Parse age string to seconds |
The months_precise and years_precise properties calculate the exact number of calendar months or years between two dates, accounting for the actual length of each month and year. Unlike the approximate versions (which use averages like 30.44 days/month or 365.25 days/year), these properties provide results that match real-world calendar boundaries. They are more intuitively correct but may be slower to compute, especially for long time spans.
Cal Object
The Cal object provides a family of in_* methods (e.g., in_days, in_months, in_years etc) to check if the target date falls within a calendar window relative to the reference date. These methods use calendar units (not elapsed time) using half-open intervals. The start is inclusive, the end is exclusive. This makes it easy to check if a date is in a specific calendar range (e.g., last week, next month, fiscal quarter) using intuitive, unit-based logic.
>>> from frist import Cal
>>> import datetime as dt
>>> target = dt.datetime(2025, 4, 29)
>>> reference = dt.datetime(2025, 4, 30)
>>> cal = Cal(target_dt=target, ref_dt=reference)
>>> cal.in_days(-1)
True # Target is yesterday
>>> cal.in_days(0)
False # Target is not today
>>> cal.in_days(-1, 1)
True # Target is within ±1 day of reference
in_days(-1): Is the target date yesterday?in_days(-1, 1): Is the target date within ±1 calendar day of the reference?
Cal(target_dt: datetime, ref_dt: datetime, fy_start_month: int = 1, holidays: set[str] = None)
| Property | Description |
|---|---|
dt_val |
Target datetime |
base_time |
Reference datetime |
fiscal_year |
Fiscal year for dt_val |
fiscal_quarter |
Fiscal quarter for dt_val |
holiday |
True if dt_val is a holiday |
| Interval Method | Description |
|---|---|
in_minutes(start=0, end=None) |
Is target in minute window |
in_hours(start=0, end=None) |
Is target in hour window |
in_days(start=0, end=None) |
Is target in day window |
in_weeks(start=0, end=None, week_start="monday") |
Is target in week window |
in_months(start=0, end=None) |
Is target in month window |
in_quarters(start=0, end=None) |
Is target in quarter window |
in_years(start=0, end=None) |
Is target in year window |
in_fiscal_quarters(start=0, end=None) |
Is target in fiscal quarter window |
in_fiscal_years(start=0, end=None) |
Is target in fiscal year window |
Chrono Object
Chrono(target_time: datetime, reference_time: datetime = None, fy_start_month: int = 1, holidays: set[str] = None)
| Property | Description |
|---|---|
age |
Age object for span calculations (see Age above) |
cal |
Cal object for calendar window logic (see Cal above) |
fiscal_year |
Fiscal year for the target time |
fiscal_quarter |
Fiscal quarter for the target time |
holiday |
True if target time is a holiday (if holidays set provided) |
Status
Pytest
src\frist\__init__.py 7 0 0 0 100%
src\frist\_age.py 149 0 46 0 100%
src\frist\_cal.py 202 0 34 0 100%
src\frist\_cal_policy.py 79 0 38 0 100%
src\frist\_constants.py 15 0 0 0 100%
src\frist\_frist.py 66 0 18 0 100%
Tox
py310: OK (15.17=setup[12.99]+cmd[2.18] seconds)
py311: OK (10.57=setup[7.96]+cmd[2.61] seconds)
py312: OK (11.98=setup[9.45]+cmd[2.53] seconds)
py313: OK (10.74=setup[8.46]+cmd[2.29] seconds)
py314: OK (11.04=setup[8.61]+cmd[2.43] seconds)
congratulations :) (59.61 seconds)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file frist-0.11.0.tar.gz.
File metadata
- Download URL: frist-0.11.0.tar.gz
- Upload date:
- Size: 42.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2de3b0bbfe9dd9649ac4557b7b4a483a8fe881b2d5a1ce2bebe6ef95a019f246
|
|
| MD5 |
228e71762e7bc95c6c3644030d93cf00
|
|
| BLAKE2b-256 |
97f520cc41532dec7beeec07bf0d9e4ea96c75e3cbea17186aea89c862a120d3
|
File details
Details for the file frist-0.11.0-py3-none-any.whl.
File metadata
- Download URL: frist-0.11.0-py3-none-any.whl
- Upload date:
- Size: 19.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
006b8a047988c7554b235727812d76a11562dabd5bec6d057c0c4ffbff1de031
|
|
| MD5 |
0362600252ae7b29d162005043242f22
|
|
| BLAKE2b-256 |
4171ea6aadbfb4db237d3089ce8853a2146c6ec4762860573d82fc431bda73c8
|