Repository: stravalib/stravalib (python)

Issue:
Replace `pydantic.datetime_parse()` with `dateutil.parser.parse()`
To accelerate a Pydantic 2 migration, we should be able to drop-in replace the use of `pydantic.datetime_parse()` with `dateutil.parser.parse()`.

I can put together a PR in the next week or so if you would like.

Pull request description:
Add `naive_datetime()` test to the `pydantic-v2` branch.
closes #521

_The text above will ensure the issue will be closed when this PR is merged_

<!--- * If this pr does not address an open issue  in the repository, please be sure
to explain what this pull request fixes or does. We prefer to discuss changes in issues first, to just ensure we are all on the same page about the change being proposed.

* If this is a technical code change, please be sure that you have [read our contributing guide.](https://stravalib.readthedocs.io/en/latest/contributing/how-to-contribute.html)
-->

## Description

<!--- Please describe changes made in this pull request in detail -->

Add @lwasser's test from #491 plus minor changes, namely checking if an input string is an integer.

## Type of change

Select the statement best describes this pull request.

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] This is a documentation update
- [ ] This is a infrastructure update (docs, ci, etc)
- [ ] Other (please describe)

## Does your PR include tests

If you are fixing a bug or adding a feature, we appreciate (but do not require)
tests to support whatever fix of feature you're implementing.

- [x] Yes
- [ ] No, i'd like some help with tests
- [ ] This change doesn't require tests

## Did you include your contribution to the change log?

- [x] Yes, `changelog.md` is up-to-date.

_If you are having a hard time getting the tests to run correctly feel free to
ping one of the maintainers here!_

<!---
If you are a stravalib maintainer submitting a PR in preparation for a new release
you can use the pull request release template:

[https://github.com/stravalib/stravalib/compare/main...branch-name-here?template=release-pull-request-template.md](https://github.com/stravalib/stravalib/compare/main...branch-name-here?template=release-pull-request-template.md). Be sure to modify
the text "branch-name-here" in the above url to apply the release template.
-->


Patch:
diff --git a/changelog.md b/changelog.md
index f64ce20..3e15d31 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,6 +1,7 @@
 # Change Log
 
 ## Unreleased
+- Fix: Add `naive_datetime()` test to the `pydantic-v2 branch`. (@bmeares, #522)
 
 ## v1.7
 
diff --git a/src/stravalib/model.py b/src/stravalib/model.py
index 62ec182..698089c 100644
--- a/src/stravalib/model.py
+++ b/src/stravalib/model.py
@@ -69,7 +69,7 @@ U = TypeVar("U", bound="BoundClientEntity")
 def naive_datetime(value: Optional[AllDateTypes]) -> Optional[datetime]:
     """Utility helper that parses a datetime value provided in
     JSON, string, int or other formats and returns a datetime.datetime
-    object
+    object.
 
     Parameters
     ----------
@@ -81,15 +81,30 @@ def naive_datetime(value: Optional[AllDateTypes]) -> Optional[datetime]:
     -------
     datetime.datetime
         A datetime object representing the datetime input value.
+
+    Notes
+    -----
+    Valid str, following formats work (from pydantic docs):
+
+    YYYY-MM-DD[T]HH:MM[:SS[.ffffff]][Z or [±]HH[:]MM]
     """
-    if value:
-        # Use dateutil.parser.parse for parsing date strings
-        dt = parser.parse(str(value))
-        # Remove timezone information, if any
-        return dt.replace(tzinfo=None)
-    else:
+    if not value:
         return None
 
+    # Allow for timestamps to be passed as strings, e.g '1714305600'.
+    try:
+        value_is_int = isinstance(value, int) or float(value).is_integer()
+    except (ValueError, TypeError):
+        value_is_int = False
+
+    dt = (
+        parser.parse(str(value))
+        if not value_is_int
+        else datetime.fromtimestamp(int(value))
+    )
+    # Remove timezone information, if any
+    return dt.replace(tzinfo=None)
+
 
 def lazy_property(fn: Callable[[U], T]) -> Optional[T]:
     """


Test patch:
diff --git a/src/stravalib/tests/unit/test_model.py b/src/stravalib/tests/unit/test_model.py
index d3331c5..1095758 100644
--- a/src/stravalib/tests/unit/test_model.py
+++ b/src/stravalib/tests/unit/test_model.py
@@ -1,6 +1,7 @@
-from datetime import datetime
+from datetime import datetime, timedelta, timezone
 
 import pytest
+from dateutil.parser import ParserError
 
 from stravalib import model
 from stravalib.model import (
@@ -23,6 +24,7 @@ from stravalib.model import (
     Split,
     SubscriptionCallback,
     SummarySegmentEffort,
+    naive_datetime,
 )
 from stravalib.tests import TestBase
 
@@ -294,3 +296,51 @@ class ModelTest(TestBase):
             "2011-02-09 21:22:21",
             subupd.event_time.strftime("%Y-%m-%d %H:%M:%S"),
         )
+
+
+# Test cases for the naive_datetime function
+@pytest.mark.parametrize(
+    "input_value, expected_output, exception",
+    [
+        (
+            "2024-04-28T12:00:00Z",
+            datetime(2024, 4, 28, 12, 0),
+            None
+        ),
+        (
+            int(datetime(2022, 4, 28, 12, 0).timestamp()),
+            datetime(2022, 4, 28, 12, 0),
+            None,
+        ),
+        (
+            str(int(datetime(2022, 4, 28, 12, 0).timestamp())),
+            datetime(2022, 4, 28, 12, 0),
+            None,
+        ),
+        (
+            datetime(2024, 4, 28, 12, 0, tzinfo=timezone.utc),
+            datetime(2024, 4, 28, 12, 0),
+            None,
+        ),
+        (
+            "April 28, 2024 12:00 PM UTC",
+            datetime(2024, 4, 28, 12, 0),
+            None,
+        ),
+        ("Foo", None, ParserError),
+    ],
+)
+def test_naive_datetime(input_value, expected_output, exception):
+    """Make sure our datetime parses properly reformats dates
+    in various formats. This test is important given the pydantic
+    `parse_datetime` method was removed in 2.x
+    # https://docs.pydantic.dev/1.10/usage/types/#datetime-types
+    Values
+    1. date time as a formatted string
+    2. datetime as a UNIX or POSIX format
+    """
+    if exception:
+        with pytest.raises(exception):
+            naive_datetime(input_value)
+    else:
+        assert naive_datetime(input_value) == expected_output