Skip to main content

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

Python Coverage Pytest Ruff Tox

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

frist-0.11.0.tar.gz (42.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

frist-0.11.0-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

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

Hashes for frist-0.11.0.tar.gz
Algorithm Hash digest
SHA256 2de3b0bbfe9dd9649ac4557b7b4a483a8fe881b2d5a1ce2bebe6ef95a019f246
MD5 228e71762e7bc95c6c3644030d93cf00
BLAKE2b-256 97f520cc41532dec7beeec07bf0d9e4ea96c75e3cbea17186aea89c862a120d3

See more details on using hashes here.

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

Hashes for frist-0.11.0-py3-none-any.whl
Algorithm Hash digest
SHA256 006b8a047988c7554b235727812d76a11562dabd5bec6d057c0c4ffbff1de031
MD5 0362600252ae7b29d162005043242f22
BLAKE2b-256 4171ea6aadbfb4db237d3089ce8853a2146c6ec4762860573d82fc431bda73c8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page