Skip to content

Commit

Permalink
feat(datetime): implement peprock.datetime.Period
Browse files Browse the repository at this point in the history
  • Loading branch information
jakob-keller committed Dec 24, 2023
1 parent 6829040 commit 2b1dbe7
Show file tree
Hide file tree
Showing 3 changed files with 603 additions and 3 deletions.
10 changes: 7 additions & 3 deletions peprock/datetime/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Date/time and related helpers and constants.
"""Date/time and related models, helpers and constants.
Complements the datetime package from the standard library
(https://docs.python.org/3/library/datetime.html), adding timezone awareness helpers and
timedelta constants.
(https://docs.python.org/3/library/datetime.html), adding datetime period models,
timezone awareness helpers and timedelta constants.
"""

# noinspection PyProtectedMember
Expand All @@ -18,6 +18,9 @@
ONE_SECOND,
ONE_WEEK,
)
from .period import (
Period,
)

__all__ = [
"__version__",
Expand All @@ -32,4 +35,5 @@
"is_aware",
"EnsureAwareError",
"ensure_aware",
"Period",
]
88 changes: 88 additions & 0 deletions peprock/datetime/period.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Datetime period model.
Examples
--------
>>> period = Period(
... start=datetime.datetime(2022, 1, 1, 12, 0),
... end=datetime.datetime(2022, 1, 2, 12, 0),
... )
>>> period.duration
datetime.timedelta(days=1)
>>> period.midpoint
datetime.datetime(2022, 1, 2, 0, 0)
>>> datetime.datetime(2022, 1, 1) in period
False
>>> period.start in period
True
>>> period.midpoint in period
True
>>> period.end in period
True
>>> datetime.datetime(2022, 1, 3) in period
False
>>> period in period
True
>>> Period(
... start=datetime.datetime(2022, 1, 1),
... end=datetime.datetime(2022, 1, 3),
... ) in period
False
"""
import collections.abc
import dataclasses
import datetime
import functools
import sys

if sys.version_info >= (3, 11):
from typing import Self
else:
from typing_extensions import Self


@dataclasses.dataclass(frozen=True)
class Period(
collections.abc.Container,
):
"""Datetime period supporting arithmetic operations."""

start: datetime.datetime
end: datetime.datetime

def _validate(self: Self) -> None:
if self.end < self.start:
msg = "end must be greater than or equal to start"
raise ValueError(msg)

def __post_init__(self: Self) -> None:
"""Validate period."""
self._validate()

@functools.cached_property
def duration(self: Self) -> datetime.timedelta:
"""Return duration of period."""
return self.end - self.start

@functools.cached_property
def midpoint(self: Self) -> datetime.datetime:
"""Return midpoint of period."""
return self.start + self.duration / 2

def __contains__(self: Self, item: object) -> bool:
"""Return True if item is in period."""
match item:
case Period():
# noinspection PyUnresolvedReferences
return self.start <= item.start and item.end <= self.end
case datetime.datetime():
# noinspection PyTypeChecker
return self.start <= item <= self.end

msg: str = f"expected peprock.datetime.Period | datetime.datetime, got {item!r}"
raise TypeError(msg)


__all__ = [
"Period",
]
Loading

0 comments on commit 2b1dbe7

Please sign in to comment.