FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Fix parse() crash on an ISO 8601 date + duration interval by vineethsaivs · Pull Request #982 · python-pendulum/pendulum · GitHub

Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (2) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
20 changes: 16 additions & 4 deletions src/pendulum/parser.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ def parse(text: str, **options: t.Any) -> Date | Time | DateTime | Duration:
return _parse(text, **options)


def _as_datetime(value: datetime.date) -> datetime.datetime:
# An interval endpoint parsed from a date-only ISO 8601 value (e.g. the
# "2021-01-01" in "2021-01-01/P1D") is a datetime.date. Promote it to a
# midnight datetime so the interval has DateTime endpoints and so
# DateTime.add/subtract accepts the duration's time components below
# (Date.add/subtract does not take hours/minutes/seconds/microseconds).
if isinstance(value, datetime.datetime):
return value

return datetime.datetime(value.year, value.month, value.day)


def _parse(
text: str, **options: t.Any
) -> Date | DateTime | Time | Duration | Interval[DateTime]:
Expand Down Expand Up @@ -75,7 +87,9 @@ def _parse(
duration = parsed.duration

if parsed.start is not None:
dt = pendulum.instance(parsed.start, tz=options.get("tz", UTC))
dt = pendulum.instance(
_as_datetime(parsed.start), tz=options.get("tz", UTC)
)

return pendulum.interval(
dt,
Expand All @@ -91,9 +105,7 @@ def _parse(
),
)

dt = pendulum.instance(
t.cast("datetime.datetime", parsed.end), tz=options.get("tz", UTC)
)
dt = pendulum.instance(_as_datetime(parsed.end), tz=options.get("tz", UTC))

return pendulum.interval(
dt.subtract(
Expand Down
18 changes: 18 additions & 0 deletions tests/test_parsing.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@ def test_parse_interval() -> None:
assert interval.end.offset == 0


def test_parse_interval_with_date_and_duration() -> None:
# The interval endpoint is a date-only value, so it parses to a
# datetime.date rather than a datetime.datetime. See
# https://github.com/python-pendulum/pendulum/issues/881
interval = pendulum.parse("2021-01-01/P1DT1H")

assert isinstance(interval, pendulum.Interval)
assert_datetime(interval.start, 2021, 1, 1, 0, 0, 0, 0)
assert_datetime(interval.end, 2021, 1, 2, 1, 0, 0, 0)

# The reverse form (duration then a date endpoint) goes through subtract().
interval = pendulum.parse("P1Y/2021-01-01")

assert isinstance(interval, pendulum.Interval)
assert_datetime(interval.start, 2020, 1, 1, 0, 0, 0, 0)
assert_datetime(interval.end, 2021, 1, 1, 0, 0, 0, 0)


def test_parse_now() -> None:
assert pendulum.parse("now").timezone_name == "UTC"
assert (
Expand Down

Back | FazBrowse Home | New Git URL