Repository: litestar-org/litestar

Issue:
Bug: test failures where local date different from UTC
### Description

Tests for the attrs signature modelling fail if local date is different to UTC.

The tests compare `now = datetime.now(tz=timezone.utc)` against `today = date.today()` which is local time.

Making `today` UTC is the obvious solution, however that creates one further issue, where `now.timestamp()` is structured using `date.fromtimestamp()` which returns local time, and then compared to the UTC `today`. This is a behavioral difference between pydantic and cattrs in this case, where pydantic would return the UTC date given a timestamp, while the cattrs converter returns the local date.

Create `now` as UTC, and demonstrate that local date is forward a day:

```python-console
>>> now = datetime.now(tz=timezone.utc)
>>> now
datetime.datetime(2023, 4, 13, 23, 19, 57, 285302, tzinfo=datetime.timezone.utc)
>>> datetime.now()
datetime.datetime(2023, 4, 14, 9, 21, 5, 809068)
```

Parse now as timestamp using the cattrs converter:

```python-console
>>> from litestar._signature.models.attrs_signature_model import _converter
>>> _converter.structure(now.timestamp(), date)
datetime.date(2023, 4, 14)
```

Parse now as timestamp using pydantic:

```python-console
>>> from pydantic import parse_obj_as
>>> parse_obj_as(date, now.timestamp())
datetime.date(2023, 4, 13)
```

Output from failing test:

```
___________________________________________________________ test_cattrs_converter_structure_date[2023-04-13T23:08:58.999269+00:00-date-2023-04-14] ___________________________________________________________

value = '2023-04-13T23:08:58.999269+00:00', cls = <class 'datetime.date'>, expected = '2023-04-14'

    @pytest.mark.parametrize(
        "value,cls,expected",
        (
            (now, date, today.isoformat()),
            (now.isoformat(), date, today.isoformat()),
            (now.timestamp(), date, today.isoformat()),
            (today, date, today.isoformat()),
            (today.isoformat(), date, today.isoformat()),
        ),
    )
    def test_cattrs_converter_structure_date(value: Any, cls: Any, expected: Any) -> None:
        result = _converter.structure(value, cls).isoformat()
>       assert result == expected
E       AssertionError: assert '2023-04-13' == '2023-04-14'
E         - 2023-04-14
E         ?          ^
E         + 2023-04-13
E         ?          ^
```

### URL to code causing the issue

_No response_

### MCVE

```python
# Your MCVE code here
```


### Steps to reproduce

```bash
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
```


### Screenshots

```bash
"![SCREENSHOT_DESCRIPTION](SCREENSHOT_LINK.png)"
```


### Logs

_No response_

### Litestar Version

main

### Platform

- [ ] Linux
- [ ] Mac
- [ ] Windows
- [ ] Other (Please specify in the description above)


Discussion:

Resolving patch:
diff --git a/litestar/_signature/models/attrs_signature_model.py b/litestar/_signature/models/attrs_signature_model.py
--- a/litestar/_signature/models/attrs_signature_model.py
+++ b/litestar/_signature/models/attrs_signature_model.py
@@ -85,7 +85,7 @@ def _structure_date(value: Any, cls: type[date]) -> date:
         return value
 
     if isinstance(value, (float, int, Decimal)):
-        return cls.fromtimestamp(float(value))
+        return datetime.fromtimestamp(float(value), tz=timezone.utc).date()
 
     dt = _structure_datetime(value=value, cls=datetime)
     return cls(year=dt.year, month=dt.month, day=dt.day)


Test patch:
diff --git a/tests/signature/test_attrs_signature_modelling.py b/tests/signature/test_attrs_signature_modelling.py
--- a/tests/signature/test_attrs_signature_modelling.py
+++ b/tests/signature/test_attrs_signature_modelling.py
@@ -7,7 +7,7 @@
 from tests import Person, PersonFactory
 
 now = datetime.now(tz=timezone.utc)
-today = date.today()
+today = now.date()
 time_now = time(hour=now.hour, minute=now.minute, second=now.second, microsecond=now.microsecond)
 one_minute = timedelta(minutes=1)
 person = PersonFactory.build()