diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..e383edb2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM public.ecr.aws/d3j8x8q7/olympus-base-python:latest + +WORKDIR /app + +# Copy repository into the image before installing +COPY . . + +# Use pip to install in editable mode with test extras to avoid Poetry issues +RUN python -m pip install --upgrade pip +RUN python -m pip install -e ".[test]" + +CMD ["/bin/bash"] diff --git a/src/pendulum/datetime.py b/src/pendulum/datetime.py index da89b13d..3a003b99 100644 --- a/src/pendulum/datetime.py +++ b/src/pendulum/datetime.py @@ -554,6 +554,48 @@ def is_anniversary( # type: ignore[override] return (self.month, self.day) == (instance.month, instance.day) + # Rich ordering operations + def __lt__(self, other: datetime.datetime) -> bool: # type: ignore[override] + if not isinstance(other, datetime.datetime): + return NotImplemented + + # Normalize both operands to UTC stdlib datetimes using base implementation + self_utc = datetime.datetime.astimezone(self, UTC) + other_dt = other if isinstance(other, DateTime) else DateTime.instance(other) + other_utc = datetime.datetime.astimezone(other_dt, UTC) + + return datetime.datetime.__lt__(self_utc, other_utc) + + def __le__(self, other: datetime.datetime) -> bool: # type: ignore[override] + if not isinstance(other, datetime.datetime): + return NotImplemented + + self_utc = datetime.datetime.astimezone(self, UTC) + other_dt = other if isinstance(other, DateTime) else DateTime.instance(other) + other_utc = datetime.datetime.astimezone(other_dt, UTC) + + return datetime.datetime.__le__(self_utc, other_utc) + + def __gt__(self, other: datetime.datetime) -> bool: # type: ignore[override] + if not isinstance(other, datetime.datetime): + return NotImplemented + + self_utc = datetime.datetime.astimezone(self, UTC) + other_dt = other if isinstance(other, DateTime) else DateTime.instance(other) + other_utc = datetime.datetime.astimezone(other_dt, UTC) + + return datetime.datetime.__gt__(self_utc, other_utc) + + def __ge__(self, other: datetime.datetime) -> bool: # type: ignore[override] + if not isinstance(other, datetime.datetime): + return NotImplemented + + self_utc = datetime.datetime.astimezone(self, UTC) + other_dt = other if isinstance(other, DateTime) else DateTime.instance(other) + other_utc = datetime.datetime.astimezone(other_dt, UTC) + + return datetime.datetime.__ge__(self_utc, other_utc) + # ADDITIONS AND SUBSTRACTIONS def add( diff --git a/test.sh b/test.sh new file mode 100755 index 00000000..f2fdead4 --- /dev/null +++ b/test.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -eu + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 {base|new}" + exit 1 +fi + +case "$1" in +base) + # Run the comparison file but exclude the new regression test that + # doesn't exist in the base commit. + poetry run pytest -q tests/datetime/test_comparison.py -k "not less_than_with_fold" + ;; +new) + # Run the full file including the new regression test. + poetry run pytest -q tests/datetime/test_comparison.py + ;; +*) + echo "Usage: $0 {base|new}" + exit 1 + ;; +esac + diff --git a/tests/datetime/test_comparison.py b/tests/datetime/test_comparison.py index 4e25f0d6..05556ee6 100644 --- a/tests/datetime/test_comparison.py +++ b/tests/datetime/test_comparison.py @@ -392,3 +392,35 @@ def test_comparison_to_unsupported(): assert dt1 != "test" assert dt1 not in ["test"] + + +def test_less_than_with_fold(): + d1 = pendulum.datetime( + 2023, + 11, + 5, + 1, + 15, + tz="America/Los_Angeles", + fold=1, + ) + + d2 = pendulum.datetime( + 2023, + 11, + 5, + 1, + 25, + tz="America/Los_Angeles", + fold=0, + ) + + assert d1.timestamp() > d2.timestamp() + + # Verify rich ordering operators behave consistently when fold differs but + # the absolute instant (timestamp) shows d1 is later than d2. + assert not (d1 < d2) + assert d1 > d2 + assert d2 < d1 + assert d1 >= d2 + assert not (d1 <= d2)