Repository: pandas-dev/pandas-stubs (language: python)

Issue:
BDay typing
I was using pylance to check types and the `date + BDay` stubs seems to be incorrect
All 3 (`__rsub__`, `__add__`, `__radd__`) should return `pd.Timestamp`, but instead they are inferred as `datetime.date`:
```python
datetime.date(2023, 1, 1) + BDay(2)
datetime.date(2023, 1, 1) - BDay(2)
BDay(2) + datetime.date(2023, 1, 1)
```

https://github.com/microsoft/pylance-release/issues/4631


Discussion / hints:
We need to know the version of the stubs that you have installed and a more complete example that runs.  

To find the version of the stubs within VS Code, use this code
```python
from pandas._version import _stub_version
```
Then in VS Code, put your mouse over `_stub_version` and it will show the version.

> We need to know the version of the stubs that you have installed and a more complete example that runs.

Can reproduce on main:

```py
import datetime
from pandas.tseries.offsets import BDay

datetime.date(2023, 1, 1) + BDay(2)
datetime.date(2023, 1, 1) - BDay(2)
reveal_type(BDay(2) + datetime.date(2023, 1, 1)) # datetime.date but pd.Timestamp at runtime
```

> I was using pylance to check types and the `date + BDay` stubs seems to be incorrect

Technically it is correct because `pd.Timestamp` is a sub-class `issubclass(pd.Timestamp, datetime.date)` but it could be more precise :) Returning the correct sub-class may require many overloads, as there are many combinations (date/Timestamp/Tick/Day/BDay/...).
Thanks for confirming!
Doesn't it always return `pd.Timestamp` or an `offset` though? (and `offset` is not subclass of `datetime.date`)
Quickly cross-checking the examples you've mentioned I don't see any new cases where it could result in different, python-native types.
Your are welcome to open a PR, the relevant file is https://github.com/pandas-dev/pandas-stubs/blob/main/pandas-stubs/_libs/tslibs/offsets.pyi It is best to make changes and run the CI (`poe test`) to see whether the runtime and typing behavior agree :)

I think the line `def __add__(self, other: _DatetimeT) -> _DatetimeT: ...` is definitely wrong, an improvement may be `def __add__(self, other: datetime.date) -> pd.Timestamp: ...` even if that is not always the case.

Fix patch:
diff --git a/pandas-stubs/_libs/tslibs/offsets.pyi b/pandas-stubs/_libs/tslibs/offsets.pyi
index 1acdadf7..4b6a6123 100644
--- a/pandas-stubs/_libs/tslibs/offsets.pyi
+++ b/pandas-stubs/_libs/tslibs/offsets.pyi
@@ -14,6 +14,7 @@ from typing import (
 
 from dateutil.relativedelta import weekday as WeekdayClass
 import numpy as np
+from pandas import Timestamp
 from pandas.core.indexes.datetimes import DatetimeIndex
 from typing_extensions import Self
 
@@ -22,7 +23,7 @@ from pandas._typing import npt
 
 from pandas.tseries.holiday import AbstractHolidayCalendar
 
-_DatetimeT = TypeVar("_DatetimeT", bound=date)
+_DatetimeT = TypeVar("_DatetimeT", bound=datetime)
 _TimedeltaT = TypeVar("_TimedeltaT", bound=timedelta)
 
 prefix_mapping: dict[str, type]
@@ -42,26 +43,32 @@ class BaseOffset:
     @overload
     def __add__(self, other: npt.NDArray[np.object_]) -> npt.NDArray[np.object_]: ...
     @overload
-    def __add__(self, other: BaseOffset) -> Self: ...
+    def __add__(self, other: _DatetimeT) -> _DatetimeT: ...  # type: ignore[overload-overlap]  # pyright: ignore[reportOverlappingOverload]
+    @overload
+    def __add__(self, other: date) -> Timestamp: ...
     @overload
-    def __add__(self, other: _DatetimeT) -> _DatetimeT: ...
+    def __add__(self, other: BaseOffset) -> Self: ...
     @overload
     def __add__(self, other: _TimedeltaT) -> _TimedeltaT: ...
     @overload
     def __radd__(self, other: npt.NDArray[np.object_]) -> npt.NDArray[np.object_]: ...
     @overload
-    def __radd__(self, other: BaseOffset) -> Self: ...
+    def __radd__(self, other: _DatetimeT) -> _DatetimeT: ...  # type: ignore[overload-overlap]  # pyright: ignore[reportOverlappingOverload]
     @overload
-    def __radd__(self, other: _DatetimeT) -> _DatetimeT: ...
+    def __radd__(self, other: date) -> Timestamp: ...
+    @overload
+    def __radd__(self, other: BaseOffset) -> Self: ...
     @overload
     def __radd__(self, other: _TimedeltaT) -> _TimedeltaT: ...
     def __sub__(self, other: BaseOffset) -> Self: ...
     @overload
     def __rsub__(self, other: npt.NDArray[np.object_]) -> npt.NDArray[np.object_]: ...
     @overload
-    def __rsub__(self, other: BaseOffset) -> Self: ...
+    def __rsub__(self, other: _DatetimeT) -> _DatetimeT: ...  # type: ignore[overload-overlap]  # pyright: ignore[reportOverlappingOverload]
     @overload
-    def __rsub__(self, other: _DatetimeT) -> _DatetimeT: ...
+    def __rsub__(self, other: date) -> Timestamp: ...
+    @overload
+    def __rsub__(self, other: BaseOffset) -> Self: ...
     @overload
     def __rsub__(self, other: _TimedeltaT) -> _TimedeltaT: ...
     def __call__(self, other): ...


Test patch:
diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py
index ed6cf910..9722c314 100644
--- a/tests/test_timefuncs.py
+++ b/tests/test_timefuncs.py
@@ -664,8 +664,13 @@ def test_some_offsets() -> None:
         ),
         pd.DatetimeIndex,
     )
-    # GH 224
-    check(assert_type(dt.date.today() - Day(), dt.date), dt.date)
+    # GH 755
+    check(assert_type(dt.date.today() - Day(), pd.Timestamp), pd.Timestamp)
+    check(assert_type(dt.date.today() + Day(), pd.Timestamp), pd.Timestamp)
+    check(assert_type(Day() + dt.date.today(), pd.Timestamp), pd.Timestamp)
+    check(assert_type(dt.datetime.now() - Day(), dt.datetime), dt.datetime)
+    check(assert_type(dt.datetime.now() + Day(), dt.datetime), dt.datetime)
+    check(assert_type(Day() + dt.datetime.now(), dt.datetime), dt.datetime)
     # GH 235
     check(
         assert_type(