Repository: edgedb/edgedb

Issue:
document date types
Currently the [documentation for date/time types](https://edgedb.com/docs/datamodel/scalars/datetime/) is missing:
* minimum and maximum values
  Are they different for pure dates and datetime types?
  The postgres documentation gives year ranges for its types, but doesn't state if this means the complete year or if they end in the middle of a year (because they ran out of bits). It's also not clear if edgedb types are identical to those postgres types.
* What calendar they use (proleptic gregorian like postgres?)

I think it'd be a good idea to limit all of them to the same range, so splitting/combining date and time to a datetime is infallible.


Discussion:
Experiments suggest:

**local_date**
* minimum: `0001-01-01`
* maximum `+294276-12-31`
* can't parse years larger than 9999

**local_datetime**
* minimum `-004713-11-24T00:00:00.000` (MJD=0 in proleptic Gregorian)
* maximum: somewhere around 272k (problems with crashes #1769)
* Can't parse negative years
* Can't parse years large than 9999

Yeah. Our datatime type is a relatively thin wrapper of Postgres' `timestamptz`. So whatever limitations that type has, `std::datetime` has them too. Would appreciate a PR.
The limits for `local_date` appear to be different from postgres, and I'm not sure if they make sense. I think all date-time types should have conistent limits (unlike postgres where dates have larger range than datetimes).
Colin please take a look as part of your current work on the docs.
@1st1 ideally you shouldn't just improve the documentation, but also review if the current behaviour is desired:

* I'd prefer a consistent range for all date/time types so splitting a datetime into date and time, or combining a date and time into a datetime are infallible operations
* Fix the parsers (the parser accepts a smaller range than the type can represent, unless that was changes since my initial post).

Resolving patch:
diff --git a/edb/pgsql/metaschema.py b/edb/pgsql/metaschema.py
--- a/edb/pgsql/metaschema.py
+++ b/edb/pgsql/metaschema.py
@@ -145,7 +145,7 @@ class TimestampTzDomain(dbops.Domain):
     """Timestamptz clamped to years 0001-9999.
 
     The default timestamp range of (4713 BC - 294276 AD) has problems:
-    Postgres isn't ISO compliant with years out of the 0-9999 range and
+    Postgres isn't ISO compliant with years out of the 1-9999 range and
     language compatibility is questionable.
     """
     def __init__(self) -> None:
@@ -165,7 +165,7 @@ class TimestampDomain(dbops.Domain):
     """Timestamp clamped to years 0001-9999.
 
     The default timestamp range of (4713 BC - 294276 AD) has problems:
-    Postgres isn't ISO compliant with years out of the 0-9999 range and
+    Postgres isn't ISO compliant with years out of the 1-9999 range and
     language compatibility is questionable.
     """
     def __init__(self) -> None:
@@ -185,7 +185,7 @@ class DateDomain(dbops.Domain):
     """Date clamped to years 0001-9999.
 
     The default timestamp range of (4713 BC - 294276 AD) has problems:
-    Postgres isn't ISO compliant with years out of the 0-9999 range and
+    Postgres isn't ISO compliant with years out of the 1-9999 range and
     language compatibility is questionable.
     """
     def __init__(self) -> None:


Test patch:
diff --git a/tests/test_edgeql_datatypes.py b/tests/test_edgeql_datatypes.py
--- a/tests/test_edgeql_datatypes.py
+++ b/tests/test_edgeql_datatypes.py
@@ -429,6 +429,77 @@ async def test_edgeql_dt_duration_09_local_date_range(self):
                 """
             )
 
+    async def test_edgeql_dt_duration_10_datetime_range(self):
+        async with self.assertRaisesRegexTx(
+            edgedb.InvalidValueError,
+            'value out of range',
+        ):
+            await self.con.execute(
+                """
+                SELECT <datetime>'9999-12-31T00:00:00Z' +
+                    <cal::relative_duration>'1 week'
+                """
+            )
+
+        async with self.assertRaisesRegexTx(
+            edgedb.InvalidValueError,
+            'value out of range',
+        ):
+            await self.con.execute(
+                """
+                SELECT <datetime>'0001-01-01T00:00:00Z' -
+                    <cal::relative_duration>'1 week'
+                """
+            )
+
+    async def test_edgeql_dt_duration_11_local_datetime_range(self):
+        async with self.assertRaisesRegexTx(
+            edgedb.InvalidValueError,
+            'value out of range',
+        ):
+            await self.con.execute(
+                """
+                SELECT <cal::local_datetime>'9999-12-31T00:00:00' +
+                    <cal::relative_duration>'1 week'
+                """
+            )
+
+        async with self.assertRaisesRegexTx(
+            edgedb.InvalidValueError,
+            'value out of range',
+        ):
+            await self.con.execute(
+                """
+                SELECT <cal::local_datetime>'0001-01-01T00:00:00' -
+                    <cal::relative_duration>'1 week'
+                """
+            )
+
+    async def test_edgeql_dt_duration_12_local_date_range(self):
+        async with self.assertRaisesRegexTx(
+            edgedb.InvalidValueError,
+            'value out of range',
+        ):
+            await self.con.execute(
+                """
+                SELECT
+                    <cal::local_date>'9999-12-31'
+                    + <cal::relative_duration>'30 hours'
+                """
+            )
+
+        async with self.assertRaisesRegexTx(
+            edgedb.InvalidValueError,
+            'value out of range',
+        ):
+            await self.con.execute(
+                """
+                SELECT
+                    <cal::local_date>'0001-01-01'
+                    - <cal::relative_duration>'30 hours'
+                """
+            )
+
     async def test_edgeql_dt_local_datetime_01(self):
         await self.assert_query_result(
             r'''
diff --git a/tests/test_edgeql_functions.py b/tests/test_edgeql_functions.py
--- a/tests/test_edgeql_functions.py
+++ b/tests/test_edgeql_functions.py
@@ -1814,15 +1814,6 @@ async def test_edgeql_functions_to_local_time_04(self):
                         cal::to_local_datetime('00:00:00 0715');
                 ''')
 
-    async def test_edgeql_functions_to_local_time_05(self):
-        async with self.assertRaisesRegexTx(
-            edgedb.InvalidValueError,
-            'value out of range',
-        ):
-            await self.con.query(r'''
-                SELECT cal::to_local_datetime(10000, 1, 1, 1, 1, 1);
-            ''')
-
     async def test_edgeql_functions_to_duration_01(self):
         await self.assert_query_result(
             r'''SELECT <str>to_duration(hours:=20);''',