Repository: litestar-org/litestar

Issue:
Bug: StorageObject doesn't return < 0 when using expiry
### Description

When the stored value is expired, the returned interval is set to 86400 and will therefore not expire. 

### URL to code causing the issue

https://github.com/litestar-org/litestar/blob/main/litestar/stores/base.py#L122

### MCVE

```python
from pathlib import Path
from litestar.stores.file import FileStore

store = FileStore(path=Path("test.db"))

async def setstore() -> None:
  await store.set("test", "value", expires_in=5)
  return None

async def getstore() -> int:
  expiry = await store.expires_in("test")
  return expiry
```


### Steps to reproduce

_No response_

### Screenshots

```bash
"![expiry](https://cdn.discordapp.com/attachments/919193495690936353/1105483460677734501/image.png)"
```


### Logs

_No response_

### Litestar Version

`litestar==2.0.0a5`

### Platform

- [ ] Linux
- [X] Mac
- [ ] Windows
- [X] Other (Please specify in the description above)
StaticFilesConfig and virtual directories
I'm trying to write a ``FileSystemProtocol`` to load files from the package data using [importlib_resources](https://importlib-resources.readthedocs.io/en/latest/using.html#). But because ``directories`` is defined as ``DirectoryPath``, pydantic checks if the given directories exist in the local filesystem. 

This is not generally true, especially in any kind of virtual filesystem (e.g. a zipped package). I think this condition should be relaxed to support virtual filesystems.

https://github.com/starlite-api/starlite/blob/9bb6dcd57c10a591377cf8e3a537e9292566d5b9/starlite/config/static_files.py#L32


Discussion:

I agree. Do you want to submit a PR?

Resolving patch:
diff --git a/litestar/stores/base.py b/litestar/stores/base.py
--- a/litestar/stores/base.py
+++ b/litestar/stores/base.py
@@ -119,7 +119,7 @@ def expires_in(self) -> int:
         was set, return ``-1``.
         """
         if self.expires_at:
-            return (self.expires_at - datetime.now(tz=timezone.utc)).seconds
+            return int(self.expires_at.timestamp() - datetime.now(tz=timezone.utc).timestamp())
         return -1
 
     def to_bytes(self) -> bytes:


Test patch:
diff --git a/tests/test_stores.py b/tests/test_stores.py
--- a/tests/test_stores.py
+++ b/tests/test_stores.py
@@ -150,7 +150,11 @@ async def test_delete_all(store: Store) -> None:
     assert not any([await store.get(key) for key in keys])
 
 
-async def test_expires_in(store: Store) -> None:
+@pytest.mark.usefixtures("patch_storage_obj_frozen_datetime")
+async def test_expires_in(store: Store, frozen_datetime: FrozenDateTimeFactory) -> None:
+    if not isinstance(store, RedisStore):
+        pytest.xfail("bug in FileStore and MemoryStore")
+
     assert await store.expires_in("foo") is None
 
     await store.set("foo", "bar")
@@ -159,6 +163,9 @@ async def test_expires_in(store: Store) -> None:
     await store.set("foo", "bar", expires_in=10)
     assert math.ceil(await store.expires_in("foo") / 10) * 10 == 10  # type: ignore[operator]
 
+    frozen_datetime.tick(12)
+    assert await store.expires_in("foo") is None
+
 
 @patch("litestar.stores.redis.Redis")
 @patch("litestar.stores.redis.ConnectionPool.from_url")