Repository: numpy/numpy
Pull request: BUG: Correct datetime64 missing type overload for datetime.date #18640
<!--         ----------------------------------------------------------------
                MAKE SURE YOUR PR GETS THE ATTENTION IT DESERVES!
                ----------------------------------------------------------------

*  FORMAT IT RIGHT:
      http://www.numpy.org/devdocs/dev/development_workflow.html#writing-the-commit-message

*  IF IT'S A NEW FEATURE OR API CHANGE, TEST THE WATERS:
      http://www.numpy.org/devdocs/dev/development_workflow.html#get-the-mailing-list-s-opinion

*  HIT ALL THE GUIDELINES:
      https://numpy.org/devdocs/dev/index.html#guidelines

*  WHAT TO DO IF WE HAVEN'T GOTTEN BACK TO YOU:
      http://www.numpy.org/devdocs/dev/development_workflow.html#getting-your-pr-reviewed
-->


Problem statement:
datetime64 missing type overload for datetime.date
### Typing code example:

```python
import datetime
import numpy as np
d = np.datetime64(datetime.date(2021, 3, 17))
```

### Run mypy on file

```
untitled.py:3: error: No overload variant of "datetime64" matches argument type "date"  [call-overload]
untitled.py:3: note: Possible overload variant:
untitled.py:3: note:     def datetime64(self, Union[None, datetime64, Union[str, bytes], datetime] = ..., Union[Union[str, bytes], Tuple[Union[str, bytes], Union[int, integer[Any]]]] = ...) -> datetime64
scripts\untitled0.py:3: note:     <1 more non-matching overload not shown>
Found 1 error in 1 file (checked 1 source file)
```

Running the code with a datetime.datetime works fine, so it appears that numpy is missing datetime.date in the Union of allowable types to pass to np.datetime64

### NumPy/Python version information:

1.20.1 3.9.2 (tags/v3.9.2:1a79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 bit (AMD64)]

Hints from issue discussion:
Since `datetime.date` is a superclass of `datetime.datetime`, fixing this issue would be as easy as replacing the later with the former.
Would you be willing to create a PR?
Correction: `np.datetime64` seems to accept any object with the `date`, `month` and `year` attributes: https://github.com/numpy/numpy/blob/bac1f7b06995dbed32d057c93c8c3f580bdedb07/numpy/core/src/multiarray/datetime.c#L2102-L2107

In light of this a protocol would be more suitable as a replacement:
``` python
class _DatetimeScalar(Protocol):
    @property
    def day(self) -> int: ...
    @property
    def month(self) -> int: ...
    @property
    def year(self) -> int: ...
```
Yes, I can take a shot at a PR for this.
I would like to contribute to this. Since I am new to this, could you guide me?


Are static typing issues candidates for the "good first issue" label? I would have thought static typing is a difficult subject for beginners to dive into.
> Are static typing issues candidates for the "good first issue" label?

As a general rule I'd say no. Every rule has its exception though, and this particular issue has a small scope and well-defined solution:
replace `dt.datetime` in the `np.datetime64` constructor with aforementioned protocol (https://github.com/numpy/numpy/issues/18640#issuecomment-802045607).
I am new and I was just going to follow along. But after reading up on protocols, mypy, static typing and such. I plugged in @BvB93's recommendations. 

mypy runs on the following
```
import datetime
import numpy as np
d = np.datetime64(datetime.date(2021, 3, 17))
```
with output
```
Success: no issues found in 1 source file
```


I looked over @oscar139 changes and they look good to me.  I had started working on this but never finished it.  One thing I would recommend is to also add a test case with the datetime.date input to np.datetime64.  In `numpy/tests/data/pass/scalars.py` around line 87, I would add `np.datetime64(dt.date(2000, 5, 3))` or something along those lines.  Do you want to put that in the same pull request?
Right I meant to ask about testing. I read in the contributing guide...
> write tests that fail before your change and pass afterward 

But as I understand it there isn't anything failing before to pass now aside from the checker (mypy). 

Also this might be picky but @BvB93 points out that... 
> np.datetime64 seems to accept any object with the date, month and year attributes: 

I understand that we need only check for those properties but we named the protocol _DatetimeScalar would _DateScalar be more consistent?

Commit message: Add test case for np.datetime64(dt.date(2000, 5, 3))

Patch to review:
diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi
index 68a14ced6865..e2a99bdbeb33 100644
--- a/numpy/__init__.pyi
+++ b/numpy/__init__.pyi
@@ -2537,11 +2537,24 @@ class object_(generic):
 
 object0 = object_
 
+#Added Protocol to typecheck for day, month, year property to be used in
+#datetime64 constructor per Issue #18640 and BvB93s recommendation
+class _DatetimeScalar(Protocol):
+    @property
+    def day(self) -> int: ...
+    @property
+    def month(self) -> int: ...
+    @property
+    def year(self) -> int: ...
+
+
 class datetime64(generic):
-    @overload
+    #Replaced dt.datetime with _DatetimeScalar per issue #18640 and
+    #BvB93s recommendation
+    @overload    
     def __init__(
         self,
-        __value: Union[None, datetime64, _CharLike_co, dt.datetime] = ...,
+        __value: Union[None, datetime64, _CharLike_co, _DatetimeScalar] = ...,
         __format: Union[_CharLike_co, Tuple[_CharLike_co, _IntLike_co]] = ...,
     ) -> None: ...
     @overload
diff --git a/numpy/typing/tests/data/pass/scalars.py b/numpy/typing/tests/data/pass/scalars.py
index c3f4ddbccd3f..815566b6889f 100644
--- a/numpy/typing/tests/data/pass/scalars.py
+++ b/numpy/typing/tests/data/pass/scalars.py
@@ -84,6 +84,7 @@ def __float__(self) -> float:
 np.datetime64("2019", "D")
 np.datetime64(np.datetime64())
 np.datetime64(dt.datetime(2000, 5, 3))
+np.datetime64(dt.date(2000, 5, 3))
 np.datetime64(None)
 np.datetime64(None, "D")
 


Code review comments:
diff_hunk: @@ -2537,11 +2537,24 @@ class object_(generic):
 
 object0 = object_
 
+#Added Protocol to typecheck for day, month, year property to be used in
+#datetime64 constructor per Issue #18640 and BvB93s recommendation
+class _DatetimeScalar(Protocol):
+    @property
+    def day(self) -> int: ...
+    @property
+    def month(self) -> int: ...
+    @property
+    def year(self) -> int: ...
+
+
 class datetime64(generic):
-    @overload
+    #Replaced dt.datetime with _DatetimeScalar per issue #18640 and
+    #BvB93s recommendation
path: numpy/__init__.pyi
text: @user1:
```suggestion
```
I'd actually suggest removing this. Currently it just sounds like a changelog, which doesn't really add any valuable information in my opinion.