@@ -2676,56 +2676,47 @@ def test_timestamps(self):
26762676 },
26772677 )
26782678 self .validate_all (
2679- "DATE_TRUNC('YEAR ', CAST('2026-01-01 00:00:00' AS TIMESTAMP))" ,
2679+ "DATE_TRUNC('HOUR ', CAST('2026-01-01 00:00:00' AS TIMESTAMP))" ,
26802680 write = {
2681- "snowflake" : "DATE_TRUNC('YEAR ', CAST('2026-01-01 00:00:00' AS TIMESTAMP))" ,
2682- "duckdb" : "DATE_TRUNC('YEAR ', CAST('2026-01-01 00:00:00' AS TIMESTAMP))" ,
2681+ "snowflake" : "DATE_TRUNC('HOUR ', CAST('2026-01-01 00:00:00' AS TIMESTAMP))" ,
2682+ "duckdb" : "DATE_TRUNC('HOUR ', CAST('2026-01-01 00:00:00' AS TIMESTAMP))" ,
26832683 },
26842684 )
2685+ # Snowflake's DATE_TRUNC return type matches type of the expresison
2686+ # DuckDB's DATE_TRUNC return type matches type of granularity part.
2687+ # In Snowflake --> DuckDB, DATE_TRUNC(date_part, timestamp) should be cast to timestamp to preserve Snowflake behavior.
26852688 self .validate_all (
2686- "DATE_TRUNC('HOUR', CAST( '2026-01-01' AS DATE) )" ,
2689+ "DATE_TRUNC(YEAR, TIMESTAMP '2026-01-01 00:00:00' )" ,
26872690 write = {
2688- "snowflake" : "DATE_TRUNC('HOUR ', CAST('2026-01-01' AS DATE ))" ,
2689- "duckdb" : "DATE_TRUNC('HOUR ', CAST('2026-01-01' AS DATE) )" ,
2691+ "snowflake" : "DATE_TRUNC('YEAR ', CAST('2026-01-01 00:00:00 ' AS TIMESTAMP ))" ,
2692+ "duckdb" : "CAST( DATE_TRUNC('YEAR ', CAST('2026-01-01 00:00:00 ' AS TIMESTAMP)) AS TIMESTAMP )" ,
26902693 },
26912694 )
26922695 self .validate_all (
2693- "DATE_TRUNC('HOUR' , CAST('2026-01-01 00:00:00 ' AS TIMESTAMP ))" ,
2696+ "DATE_TRUNC(MONTH , CAST('2024-06-15 14:23:45 ' AS TIMESTAMPTZ ))" ,
26942697 write = {
2695- "snowflake" : "DATE_TRUNC('HOUR ', CAST('2026-01-01 00:00:00 ' AS TIMESTAMP ))" ,
2696- "duckdb" : "DATE_TRUNC('HOUR ', CAST('2026-01-01 00:00:00 ' AS TIMESTAMP) )" ,
2698+ "snowflake" : "DATE_TRUNC('MONTH ', CAST('2024-06-15 14:23:45 ' AS TIMESTAMPTZ ))" ,
2699+ "duckdb" : "CAST( DATE_TRUNC('MONTH ', CAST('2024-06-15 14:23:45 ' AS TIMESTAMPTZ)) AS TIMESTAMPTZ )" ,
26972700 },
26982701 )
2699- # Snowflake's DATE_TRUNC return type matches type of the expresison
2700- # DuckDB's DATE_TRUNC return type matches type of granularity part.
2701- # In Snowflake --> DuckDB, DATE_TRUNC(date_part, timestamp) should be cast to timestamp to preserve Snowflake behavior.
2702- expr = self .parse_one ("DATE_TRUNC(YEAR, TIMESTAMP '2026-01-01 00:00:00')" )
2703- annotated = annotate_types (expr , dialect = "snowflake" )
2704- self .assertEqual (
2705- annotated .sql ("duckdb" ),
2706- "CAST(DATE_TRUNC('YEAR', CAST('2026-01-01 00:00:00' AS TIMESTAMP)) AS TIMESTAMP)" ,
2707- )
27082702
2709- expr = self .parse_one ("DATE_TRUNC(MONTH, CAST('2024-06-15 14:23:45' AS TIMESTAMPTZ))" )
2710- annotated = annotate_types (expr , dialect = "snowflake" )
2711- self .assertEqual (
2712- annotated .sql ("duckdb" ),
2713- "CAST(DATE_TRUNC('MONTH', CAST('2024-06-15 14:23:45' AS TIMESTAMPTZ)) AS TIMESTAMPTZ)" ,
2714- )
2715- # Time unit and timestamp should not trigger extra type casting
2716- expr = self .parse_one ("DATE_TRUNC(HOUR, TIMESTAMP '2024-06-15 14:23:45')" )
2717- annotated = annotate_types (expr , dialect = "snowflake" )
2718- self .assertEqual (
2719- annotated .sql ("duckdb" ),
2720- "DATE_TRUNC('HOUR', CAST('2024-06-15 14:23:45' AS TIMESTAMP))" ,
2703+ # In Snowflake --> DuckDB, DATE_TRUNC(time_part, date) should be cast to date to preserve Snowflake behavior.
2704+ self .validate_all (
2705+ "DATE_TRUNC('HOUR', CAST('2026-01-01' AS DATE))" ,
2706+ write = {
2707+ "snowflake" : "DATE_TRUNC('HOUR', CAST('2026-01-01' AS DATE))" ,
2708+ "duckdb" : "CAST(DATE_TRUNC('HOUR', CAST('2026-01-01' AS DATE)) AS DATE)" ,
2709+ },
27212710 )
27222711
2723- # In Snowflake --> DuckDB, DATE_TRUNC(time_part, date) should be cast to date to preserve Snowflake behavior.
2724- expr = self .parse_one ("DATE_TRUNC('HOUR', DATE '2026-01-01')" )
2725- annotated = annotate_types (expr , dialect = "snowflake" )
2726- self .assertEqual (
2727- annotated .sql ("duckdb" ),
2728- "CAST(DATE_TRUNC('HOUR', CAST('2026-01-01' AS DATE)) AS DATE)" ,
2712+ # DuckDB does not support DATE_TRUNC(time_part, time), so we add a dummy date to generate DATE_TRUNC(time_part, date) --> DATE in DuckDB
2713+ # Then it is casted to a time (HH:MM:SS) to match Snowflake.
2714+ self .validate_all (
2715+ "DATE_TRUNC('HOUR', CAST('14:23:45.123456' AS TIME))" ,
2716+ write = {
2717+ "snowflake" : "DATE_TRUNC('HOUR', CAST('14:23:45.123456' AS TIME))" ,
2718+ "duckdb" : "CAST(DATE_TRUNC('HOUR', CAST('1970-01-01' AS DATE) + CAST('14:23:45.123456' AS TIME)) AS TIME)" ,
2719+ },
27292720 )
27302721 self .validate_identity ("TO_DATE('12345')" ).assert_is (exp .Anonymous )
27312722
0 commit comments