Repository: pydantic/pydantic

Issue:
timedelta_isoformat does not produce correct string in some cases
### Checks

* [x] I added a descriptive title to this issue
* [x] I have searched (google, github) for similar issues and couldn't find anything
* [x] I have read and followed [the docs](https://pydantic-docs.helpmanual.io/) and still think this is a bug

<!-- Sorry to sound so draconian, but every second saved replying to issues is time spend improving pydantic :-) -->

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
   pydantic version: 1.9.0
            pydantic compiled: True
                 install path: C:\Program Files\Python310\Lib\site-packages\pydantic
               python version: 3.10.2 (tags/v3.10.2:a58ebcc, Jan 17 2022, 14:12:15) [MSC v.1929 64 bit (AMD64)]
                     platform: Windows-10-10.0.19044-SP0
     optional deps. installed: ['dotenv', 'typing-extensions']
```
# Code Demonstration bug
```py
from pydantic.json import timedelta_isoformat
from datetime import datetime

timedelta = (datetime.fromisoformat("2022-03-15 08:00:57.152735+00:00") -
             datetime.fromisoformat("2022-03-15 08:00:57.264000+00:00"))

incorrect_iso_string = timedelta_isoformat(timedelta)
# this produce "P-1DT23H59M59.888735S"  and that is incorrect according to documentation ([±]P[DD]DT[HH]H[MM]M[SS]S)
...
```


i have verified this on two diferent computers with two diferents setups (conda/regular python so it should not be problem of my enviroment)
timedelta_isoformat does not produce correct string in some cases
### Checks

* [x] I added a descriptive title to this issue
* [x] I have searched (google, github) for similar issues and couldn't find anything
* [x] I have read and followed [the docs](https://pydantic-docs.helpmanual.io/) and still think this is a bug

<!-- Sorry to sound so draconian, but every second saved replying to issues is time spend improving pydantic :-) -->

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
   pydantic version: 1.9.0
            pydantic compiled: True
                 install path: C:\Program Files\Python310\Lib\site-packages\pydantic
               python version: 3.10.2 (tags/v3.10.2:a58ebcc, Jan 17 2022, 14:12:15) [MSC v.1929 64 bit (AMD64)]
                     platform: Windows-10-10.0.19044-SP0
     optional deps. installed: ['dotenv', 'typing-extensions']
```
# Code Demonstration bug
```py
from pydantic.json import timedelta_isoformat
from datetime import datetime

timedelta = (datetime.fromisoformat("2022-03-15 08:00:57.152735+00:00") -
             datetime.fromisoformat("2022-03-15 08:00:57.264000+00:00"))

incorrect_iso_string = timedelta_isoformat(timedelta)
# this produce "P-1DT23H59M59.888735S"  and that is incorrect according to documentation ([±]P[DD]DT[HH]H[MM]M[SS]S)
...
```


i have verified this on two diferent computers with two diferents setups (conda/regular python so it should not be problem of my enviroment)


Comments:

Patch:
diff --git a/pydantic/json.py b/pydantic/json.py
--- a/pydantic/json.py
+++ b/pydantic/json.py
@@ -105,8 +105,8 @@ def custom_pydantic_encoder(type_encoders: Dict[Any, Callable[[Type[Any]], Any]]
 
 def timedelta_isoformat(td: datetime.timedelta) -> str:
     """
-    ISO 8601 encoding for timedeltas.
+    ISO 8601 encoding for Python timedelta object.
     """
     minutes, seconds = divmod(td.seconds, 60)
     hours, minutes = divmod(minutes, 60)
-    return f'P{td.days}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S'
+    return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S'


Test patch:
diff --git a/tests/test_json.py b/tests/test_json.py
--- a/tests/test_json.py
+++ b/tests/test_json.py
@@ -46,6 +46,7 @@ class MyEnum(Enum):
         (datetime.datetime(2032, 1, 1), '"2032-01-01T00:00:00"'),
         (datetime.time(12, 34, 56), '"12:34:56"'),
         (datetime.timedelta(days=12, seconds=34, microseconds=56), '1036834.000056'),
+        (datetime.timedelta(seconds=-1), '-1.0'),
         ({1, 2, 3}, '[1, 2, 3]'),
         (frozenset([1, 2, 3]), '[1, 2, 3]'),
         ((v for v in range(4)), '[0, 1, 2, 3]'),
@@ -142,6 +143,8 @@ class Foo:
     [
         (datetime.timedelta(days=12, seconds=34, microseconds=56), 'P12DT0H0M34.000056S'),
         (datetime.timedelta(days=1001, hours=1, minutes=2, seconds=3, microseconds=654_321), 'P1001DT1H2M3.654321S'),
+        (datetime.timedelta(seconds=-1), '-P1DT23H59M59.000000S'),
+        (datetime.timedelta(), 'P0DT0H0M0.000000S'),
     ],
 )
 def test_iso_timedelta(input, output):