Repository: edgedb/edgedb

Issue:
Converting `cal::local_time` from string can result into `24:00:00`
- EdgeDB Version: 3.0-dev.7166+local

Steps to Reproduce:
```
edgedb> SELECT cal::to_local_time('23:59:59.999999500');
edgedb error: ProtocolEncodingError: ProtocolEncodingError: datetime is out of range
```


Discussion:

Resolving patch:
diff --git a/edb/pgsql/metaschema.py b/edb/pgsql/metaschema.py
--- a/edb/pgsql/metaschema.py
+++ b/edb/pgsql/metaschema.py
@@ -2633,29 +2633,43 @@ class LocalTimeInFunction(dbops.Function):
     """Cast text into time using ISO8601 spec."""
     text = r'''
         SELECT
-            CASE WHEN val !~ (
-                    '^\s*(' ||
-                        '(\d{2}(:\d{2}(:\d{2}(\.\d+)?)?)?|\d{2,6}(\.\d+)?)' ||
-                    ')\s*$'
-                )
+            CASE WHEN date_part('hour', x.t) = 24
             THEN
                 edgedb.raise(
                     NULL::time,
                     'invalid_datetime_format',
                     msg => (
-                        'invalid input syntax for type time: '
+                        'cal::local_time field value out of range: '
                         || quote_literal(val)
-                    ),
-                    detail => (
-                        '{"hint":"Please use ISO8601 format. Examples: '
-                        || '18:43:27 or 18:43 Alternatively '
-                        || '\"to_local_time\" function provides custom '
-                        || 'formatting options."}'
                     )
                 )
             ELSE
-                val::time
-            END;
+                x.t
+            END
+        FROM (
+            SELECT
+                CASE WHEN val !~ ('^\s*(' ||
+                        '(\d{2}(:\d{2}(:\d{2}(\.\d+)?)?)?|\d{2,6}(\.\d+)?)' ||
+                    ')\s*$')
+                THEN
+                    edgedb.raise(
+                        NULL::time,
+                        'invalid_datetime_format',
+                        msg => (
+                            'invalid input syntax for type time: '
+                            || quote_literal(val)
+                        ),
+                        detail => (
+                            '{"hint":"Please use ISO8601 format. Examples: '
+                            || '18:43:27 or 18:43 Alternatively '
+                            || '\"to_local_time\" function provides custom '
+                            || 'formatting options."}'
+                        )
+                    )
+                ELSE
+                    val::time
+                END as t
+        ) as x;
     '''
 
     def __init__(self) -> None:


Test patch:
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
@@ -2565,6 +2565,54 @@ 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):
+        with self.assertRaisesRegex(
+            edgedb.InvalidValueError,
+            'cal::local_time field value out of range'
+        ):
+            async with self.con.transaction():
+                # including time zone
+                await self.con.query(r'''
+                    SELECT
+                        cal::to_local_time('24:00:00');
+                ''')
+
+    async def test_edgeql_functions_to_local_time_06(self):
+        with self.assertRaisesRegex(
+            edgedb.InvalidValueError,
+            'cal::local_time field value out of range'
+        ):
+            async with self.con.transaction():
+                # including time zone
+                await self.con.query(r'''
+                    SELECT
+                        cal::to_local_time(23, 59, 60);
+                ''')
+
+    async def test_edgeql_functions_to_local_time_07(self):
+        with self.assertRaisesRegex(
+            edgedb.InvalidValueError,
+            'cal::local_time field value out of range'
+        ):
+            async with self.con.transaction():
+                # including time zone
+                await self.con.query(r'''
+                    SELECT
+                        <cal::local_time>'23:59:59.999999999999';
+                ''')
+
+    async def test_edgeql_functions_to_local_time_08(self):
+        with self.assertRaisesRegex(
+            edgedb.InvalidValueError,
+            'cal::local_time field value out of range'
+        ):
+            async with self.con.transaction():
+                # including time zone
+                await self.con.query(r'''
+                    SELECT
+                        <cal::local_time><json>'24:00:00';
+                ''')
+
     async def test_edgeql_functions_to_duration_01(self):
         await self.assert_query_result(
             r'''SELECT <str>to_duration(hours:=20);''',