Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add first_day and last_day #14

Merged
merged 3 commits into from Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -3,6 +3,11 @@ Changelog

All notable changes to this project will be documented in this file.

v0.5.0
------

- Added ``YearMonth.first_day`` and ``YearMonth.last_day`` properties to get the first and last day of the month.

v0.4.0
------

Expand Down
15 changes: 11 additions & 4 deletions mp_yearmonth/yearmonth.py
Expand Up @@ -138,6 +138,16 @@ def numdays(self) -> int:
_, days = calendar.monthrange(self.year, self.month)
return days

@property
def first_day(self) -> datetime.date:
"""Return the first day of the month."""
return datetime.date(self.year, self.month, 1)

@property
def last_day(self) -> datetime.date:
"""Return the last day of the month."""
return datetime.date(self.year, self.month, self.numdays)

@classmethod
def current(cls, tz: Optional[datetime.tzinfo] = None) -> "YearMonth":
"""Return the current year and month.
Expand Down Expand Up @@ -172,10 +182,7 @@ def prev(self) -> "YearMonth":

def bounds(self) -> Tuple[datetime.date, datetime.date]:
"""Return the first and last day of the month."""
return (
datetime.date(self.year, self.month, 1),
datetime.date(self.year, self.month, self.numdays),
)
return (self.first_day, self.last_day)

def applying_delta(self, months: int) -> "YearMonth":
"""Return the YearMonth that is `months` away from this one."""
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mp-yearmonth"
version = "0.4.0"
version = "0.5.0"
description = "A year-month datatype for Python."
keywords = ["year", "month", "date", "calendar"]
authors = ["Ramon Torres <ramon@macropoetry.com>"]
Expand Down
40 changes: 34 additions & 6 deletions tests/test_yearmonth.py
@@ -1,5 +1,6 @@
import pytest
import datetime
from typing import Tuple
from freezegun import freeze_time
from mp_yearmonth import YearMonth

Expand Down Expand Up @@ -103,6 +104,26 @@ def test_numdays():
assert YearMonth(2020, 2).numdays == 29


def test_first_day():
ym1 = YearMonth(2021, 1)
assert ym1.first_day == datetime.date(2021, 1, 1)

ym2 = YearMonth(2021, 2)
assert ym2.first_day == datetime.date(2021, 2, 1)


@pytest.mark.parametrize(
("year_month", "expected_last_day"),
[
(YearMonth(2021, 1), datetime.date(2021, 1, 31)),
(YearMonth(2021, 2), datetime.date(2021, 2, 28)),
(YearMonth(2020, 2), datetime.date(2020, 2, 29)), # Leap year
],
)
def test_last_day(year_month: YearMonth, expected_last_day: datetime.date):
assert year_month.last_day == expected_last_day


@freeze_time("2021-01-01") # UTC
def test_current():
assert YearMonth.current() == YearMonth(2021, 1)
Expand Down Expand Up @@ -225,9 +246,16 @@ def test_contains_handles_unsupported_types():
assert "test" not in YearMonth(2021, 1)


def test_bounds():
ym1 = YearMonth(2021, 1)
assert ym1.bounds() == (datetime.date(2021, 1, 1), datetime.date(2021, 1, 31))

ym2 = YearMonth(2021, 2)
assert ym2.bounds() == (datetime.date(2021, 2, 1), datetime.date(2021, 2, 28))
@pytest.mark.parametrize(
("year_month", "expected"),
[
(YearMonth(2021, 1), (datetime.date(2021, 1, 1), datetime.date(2021, 1, 31))),
(YearMonth(2021, 2), (datetime.date(2021, 2, 1), datetime.date(2021, 2, 28))),
(
YearMonth(2020, 2),
(datetime.date(2020, 2, 1), datetime.date(2020, 2, 29)),
), # Leap year
],
)
def test_bounds(year_month: YearMonth, expected: Tuple[datetime.date, datetime.date]):
assert year_month.bounds() == expected