Skip to content

Commit

Permalink
Merge pull request #7 from kraken-tech/implementation
Browse files Browse the repository at this point in the history
Implementation
  • Loading branch information
samueljsb authored Jul 10, 2024
2 parents 6d7a53f + 8a12745 commit b915ea9
Show file tree
Hide file tree
Showing 10 changed files with 1,809 additions and 3 deletions.
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.1
hooks:
- id: ruff-format
- id: ruff
args: [--fix, --show-fixes]
- id: ruff-format
- repo: https://github.com/samueljsb/sort-lines
rev: v0.3.0
hooks:
Expand All @@ -29,3 +29,4 @@ repos:
rev: v1.10.1
hooks:
- id: mypy
additional_dependencies: [pytest, types-python-dateutil]
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ description = "Tools for working with timezone-aware datetimes."
license.file = "LICENSE"
readme = "README.md"
requires-python = ">=3.10"
dependencies = []
dependencies = [
"python-dateutil",
"typing_extensions",
]
classifiers = [ # pragma: alphabetize
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
Expand All @@ -45,6 +48,7 @@ dev = [ # pragma: alphabetize
"coverage",
"pre-commit",
"pytest",
"time-machine",
"tox",
"tox-uv",
"uv",
Expand Down
10 changes: 10 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,24 @@ pyproject-api==1.7.1
# via tox
pytest==8.2.2
# via datetime-tools (pyproject.toml)
python-dateutil==2.9.0.post0
# via
# datetime-tools (pyproject.toml)
# time-machine
pyyaml==6.0.1
# via pre-commit
six==1.16.0
# via python-dateutil
time-machine==2.14.2
# via datetime-tools (pyproject.toml)
tox==4.16.0
# via
# datetime-tools (pyproject.toml)
# tox-uv
tox-uv==1.9.1
# via datetime-tools (pyproject.toml)
typing-extensions==4.12.2
# via datetime-tools (pyproject.toml)
uv==0.2.22
# via
# datetime-tools (pyproject.toml)
Expand Down
24 changes: 23 additions & 1 deletion src/datetime_tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
"""
TODO Add a description of the package here.
Tools for working with timezone-aware datetimes.
"""

from ._clock import Clock
from ._converter import TimezoneConverter
from ._dates import (
DateNotFound,
closest_upcoming_match,
get_contiguous_periods,
is_last_day_of_month,
iter_dates,
latest_date_for_day,
)

__all__ = (
"Clock",
"DateNotFound",
"TimezoneConverter",
"closest_upcoming_match",
"get_contiguous_periods",
"is_last_day_of_month",
"iter_dates",
"latest_date_for_day",
)
69 changes: 69 additions & 0 deletions src/datetime_tools/_clock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import datetime
import zoneinfo

from dateutil import relativedelta


class Clock:
"""Get the current date/time in a specific timezone."""

def __init__(self, timezone: str) -> None:
self.tzinfo = zoneinfo.ZoneInfo(timezone)

# Current time/date

def now(self) -> datetime.datetime:
return datetime.datetime.now(tz=self.tzinfo)

def today(self) -> datetime.date:
return self.now().date()

# Relative times/dates

def yesterday(self) -> datetime.date:
return self.days_in_the_past(1)

def tomorrow(self) -> datetime.date:
return self.days_in_the_future(1)

def days_in_the_past(self, days: int) -> datetime.date:
return self.today() - datetime.timedelta(days=days)

def days_in_the_future(self, days: int) -> datetime.date:
return self.today() + datetime.timedelta(days=days)

def months_in_the_past(self, months: int) -> datetime.date:
"""Get the date some number of months ago.
If the target month does not have enough days, the closest day will be
returned (e.g. 3 months before July 31st is April 30th, not April
31st).
"""
return self.today() - relativedelta.relativedelta(months=months)

def months_in_the_future(self, months: int) -> datetime.date:
"""Get the date some number of months in the future.
If the target month does not have enough days, the closest day will be
returned (e.g. 4 months after July 31st is November 30th, not November
31st).
"""
return self.today() + relativedelta.relativedelta(months=months)

def is_in_the_past(
self, candidate: datetime.datetime | datetime.date
) -> bool:
"""Check whether a date/time is before the current date/time."""
if isinstance(candidate, datetime.datetime):
return candidate < self.now()
else:
return candidate < self.today()

def is_in_the_future(
self, candidate: datetime.datetime | datetime.date
) -> bool:
"""Check whether a date/time is after the current date/time."""
if isinstance(candidate, datetime.datetime):
return self.now() < candidate
else:
return self.today() < candidate
Loading

0 comments on commit b915ea9

Please sign in to comment.