Skip to content

Conversation

@fivetran-felixhuang
Copy link
Collaborator

@fivetran-felixhuang fivetran-felixhuang commented Jan 13, 2026

https://docs.snowflake.com/en/sql-reference/functions/try_to_date

We have the same problem as transpiling TO_DATE (https://docs.snowflake.com/en/sql-reference/functions/to_date). Thus if the input is an integer expression like '31536000000', this implementation can't handle it.

For inputs of string types (formatted date strings), we can use a combination of TRY_CAST and TRY_STRPTIME through setting the 'safe' argument.

Testing

source query:

SELECT
      TRY_TO_DATE('2024.01.31', 'YYYY.MM.DD') AS dot_format,
      TRY_TO_DATE('2024-01-31') AS default_format,
      TRY_TO_DATE('01-31-2024', 'MM-DD-YYYY') AS us_dash_format,
      TRY_TO_DATE('01-31-2024vsad', 'MM-DD-YYYY') AS invalid_us_dash_format,
      TRY_TO_DATE('2024-05-10') AS valid_date,
      TRY_TO_DATE('2024-05-10', 'YYYY-MM-DD') AS valid_formated,
      TRY_TO_DATE('2024-05-10 16:00:00', 'YYYY-MM-DD HH24:MI:SS') AS formatted_valid_date,
      TRY_TO_DATE('2024.01.31', 'YYYY-MM-DD') AS invalid_formated,
      TRY_TO_DATE('01-312322024') invalid;

transpiled query:

SELECT 
CAST(CAST(TRY_STRPTIME('2024.01.31', '%Y.%m.%d') AS TIMESTAMP) AS DATE) AS "DOT_FORMAT", 
TRY_CAST('2024-01-31' AS DATE) AS "DEFAULT_FORMAT", 
CAST(CAST(TRY_STRPTIME('01-31-2024', '%m-%d-%Y') AS TIMESTAMP) AS DATE) AS "US_DASH_FORMAT", 
CAST(CAST(TRY_STRPTIME('01-31-2024vsad', '%m-%d-%Y') AS TIMESTAMP) AS DATE) AS "INVALID_US_DASH_FORMAT", 
TRY_CAST('2024-05-10' AS DATE) AS "VALID_DATE", 
TRY_CAST('2024-05-10' AS DATE) AS "VALID_FORMATED", 
TRY_CAST('2024-05-10 16:00:00' AS DATE) AS "FORMATTED_VALID_DATE", 
TRY_CAST('2024.01.31' AS DATE) AS "INVALID_FORMATED", 
TRY_CAST('01-312322024' AS DATE) AS "INVALID"

both queries produce the same results.

Discrepancy found

source query:
TRY_TO_DATE('2024-05-10', 'YYYY-MM-DD HH24:MI:SS')

transpiled query:
TRY_CAST('2024-05-10' AS DATE)

The source query returns NULL, while the transpiled query returns 2024-05-10 . Since the format is the default snowflake format (Snowflake.TIME_FORMAT), in tsordstodate_sql it won't be handled by the branch "if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT)", and instead will go straight to casting without checking whether the input string matches the format string (in this case the HH24:MI:SS part is missing in the input).

@github-actions
Copy link
Contributor

github-actions bot commented Jan 13, 2026

SQLGlot Integration Test Results

Comparing:

  • this branch (sqlglot:transpile_try_to_date_snowflake_duckdb, sqlglot version: transpile_try_to_date_snowflake_duckdb)
  • baseline (main, sqlglot version: 28.6.1.dev44)

⚠️ Limited to dialects: snowflake, duckdb

By Dialect

dialect main sqlglot:transpile_try_to_date_snowflake_duckdb difference links
duckdb -> duckdb 4003/4003 passed (100.0%) 4003/4003 passed (100.0%) No change full result / delta
snowflake -> duckdb 615/847 passed (72.6%) 615/847 passed (72.6%) No change full result / delta
snowflake -> snowflake 847/847 passed (100.0%) 847/847 passed (100.0%) No change full result / delta

Overall

main: 5697 total, 5465 passed (pass rate: 95.9%), sqlglot version: 28.6.1.dev44

sqlglot:transpile_try_to_date_snowflake_duckdb: 5697 total, 5465 passed (pass rate: 95.9%), sqlglot version: transpile_try_to_date_snowflake_duckdb

Difference: No change

@georgesittas
Copy link
Collaborator

@fivetran-felixhuang the example you mentioned isn't fixed in this branch:

(sqlglot) ➜  sqlglot git:(transpile_try_to_date_snowflake_duckdb) sqlglot --read snowflake --write duckdb "TRY_TO_DATE('2024-05-10', 'YYYY-MM-DD HH24:MI:SS')"
TRY_CAST('2024-05-10' AS DATE)

@fivetran-felixhuang
Copy link
Collaborator Author

@fivetran-felixhuang the example you mentioned isn't fixed in this branch:

(sqlglot) ➜  sqlglot git:(transpile_try_to_date_snowflake_duckdb) sqlglot --read snowflake --write duckdb "TRY_TO_DATE('2024-05-10', 'YYYY-MM-DD HH24:MI:SS')"
TRY_CAST('2024-05-10' AS DATE)

@georgesittas yeah I am not sure how to fix it...somehow we would have to validate that the input conform to the format

@georgesittas
Copy link
Collaborator

I'm a little confused, could you clarify what this PR is trying to solve? Is it supporting the transpilation of TRY_TO_DATE from Snowflake to DuckDB, with just this example not covered?

@fivetran-felixhuang
Copy link
Collaborator Author

fivetran-felixhuang commented Jan 14, 2026

if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT)

@georgesittas

My goal was to support transpilation of TRY_TO_DATE for the string inputs (integer inputs are quite tricky). But when I was testing, I found the example of discrepancy I mentioned above (not validating input strings against default formats). I tried to check if there are ways to validate string inputs if using the default format.

But I found more issues with TRY_TO_DATE/TO_DATE...It seems that Snowflake supports many date/time formats by default (https://docs.snowflake.com/en/sql-reference/date-time-input-output#label-date-time-input-output-supported-formats-for-auto-detection)

For example, TRY_TO_DATE('17-DEC-1980'), TRY_TO_DATE('12/17/1980') and TRY_TO_DATE('Thu, 21 Dec 2000 16:01:07 +0200') all return date values in Snowflake, while TRY_CAST('17-DEC-1980' AS DATE), TRY_CAST('12/17/1980' AS DATE) and TRY_CAST('Thu, 21 Dec 2000 16:01:07 +0200' AS DATE) return NULL, since DuckDB only supports the YYYY-MM-DD format

I am not sure what the next step is...if we want to validate the input strings against default formats by using duckDB's STRPTIME, we will need to support all of them

@fivetran-felixhuang
Copy link
Collaborator Author

It should also be noted that there are some formats that snowflake accepts but DuckDB doesn't..

TRY_TO_DATE('Mon Jul 08 18:09:51 +0000 2013', 'DY MON DD HH24:MI:SS TZHTZM YYYY') AS default_format_5,
      
TRY_TO_DATE('2013-04-28T20:57:01.123456789+07:00', '%Y-%m-%d"T"%H:%M:%S.FFTZH:TZM') AS default_format_iso,

TRY_TO_DATE('2013-04-28T20:57:01.123456', 'YYYY-MM-DD"T"HH24:MI:SS.FF') AS default_format_iso_2,
      
TRY_TO_DATE('Thu, 21 Dec 2000 16:01:07.123456789 +0200', 'DY, DD MON YYYY HH24:MI:SS.FF TZHTZM') AS default_format_rfc,

All of these return dates in snowflake, while their transpiled versions return NULL, as DuckDB doesn't recognize them

CAST(CAST(TRY_STRPTIME('Mon Jul 08 18:09:51 +0000 2013', '%a %b %d %H:%M:%S TZHTZM %Y') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_5", 

CAST(CAST(TRY_STRPTIME('2013-04-28T20:57:01.123456789+07:00', '%Y-%m-%d"T"%H:%M:%S.FFTZH:TZM') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_ISO", 

CAST(CAST(TRY_STRPTIME('2013-04-28T20:57:01.123456', '%Y-%m-%d"T"%H:%M:%S.FF') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_ISO_2", 

CAST(CAST(TRY_STRPTIME('Thu, 21 Dec 2000 16:01:07.123456789 +0200', '%a, %d %b %Y %H:%M:%S.FF TZHTZM') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_RFC",

@georgesittas
Copy link
Collaborator

georgesittas commented Jan 15, 2026

Is there a strftime counterpart for these format model elements in Snowflake? Seems like the unsupported ones are:

  • TZHTZM
  • FFTZH:TZM

We can ignore these for the time being, if not.

@fivetran-felixhuang
Copy link
Collaborator Author

Is there a strftime counterpart for these format model elements in Snowflake? Seems like the unsupported ones are:

  • TZHTZM
  • FFTZH:TZM

We can ignore these for the time being, if not.

perhaps Snowflake's TO_TIMESTAMP does something similar to DuckDB's strftime

@fivetran-felixhuang
Copy link
Collaborator Author

source query:

select 
TRY_TO_DATE('Mon Jul 08 18:09:51 +0000 2013', 'DY MON DD HH24:MI:SS TZHTZM YYYY') AS default_format_5,
      
TRY_TO_DATE('2013-04-28T20:57:01.123456789+07:00', 'YYYY-MM-DD"T"HH24:MI:SS.FFTZH:TZM') AS default_format_iso,

TRY_TO_DATE('2013-04-28T20:57:01.123456789+07', 'YYYY-MM-DD"T"HH24:MI:SS.FFTZH') AS default_format_iso_2,

TRY_TO_DATE('2013-04-28T20:57:01.123456', 'YYYY-MM-DD"T"HH24:MI:SS.FF') AS default_format_iso_2,
TRY_TO_DATE('Thu, 21 Dec 2000 16:01:07.123456789 +0200', 'DY, DD MON YYYY HH24:MI:SS.FF TZHTZM') AS default_format_rfc,
TRY_TO_DATE('2013-04-28T20:57:01.123456789+07:00', 'YYYY-MM-DD"T"HH24:MI:SS.FFTZH:TZM') AS format_tzh_tzm,
TRY_TO_DATE('2013-04-28 20:57 +0700', 'YYYY-MM-DD HH24:MI TZHTZM') AS format_tzh_tzm_2,
TRY_TO_DATE('2013-04-28 20:57 +07', 'YYYY-MM-DD HH24:MI TZH') AS format_tzh
      

transpiled query:

SELECT 
CAST(CAST(TRY_STRPTIME('Mon Jul 08 18:09:51 +0000 2013', '%a %b %d %H:%M:%S %z %Y') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_5", 
CAST(CAST(TRY_STRPTIME('2013-04-28T20:57:01.123456789+07:00', '%Y-%m-%dT%H:%M:%S.%n%z') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_ISO", 
CAST(CAST(TRY_STRPTIME('2013-04-28T20:57:01.123456789+07', '%Y-%m-%dT%H:%M:%S.%n%z') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_ISO_2", 
CAST(CAST(TRY_STRPTIME('2013-04-28T20:57:01.123456', '%Y-%m-%dT%H:%M:%S.%n') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_ISO_2", 
CAST(CAST(TRY_STRPTIME('Thu, 21 Dec 2000 16:01:07.123456789 +0200', '%a, %d %b %Y %H:%M:%S.%n %z') AS TIMESTAMP) AS DATE) AS "DEFAULT_FORMAT_RFC", 
CAST(CAST(TRY_STRPTIME('2013-04-28T20:57:01.123456789+07:00', '%Y-%m-%dT%H:%M:%S.%n%z') AS TIMESTAMP) AS DATE) AS "FORMAT_TZH_TZM", 
CAST(CAST(TRY_STRPTIME('2013-04-28 20:57 +0700', '%Y-%m-%d %H:%M %z') AS TIMESTAMP) AS DATE) AS "FORMAT_TZH_TZM_2", 
CAST(CAST(TRY_STRPTIME('2013-04-28 20:57 +07', '%Y-%m-%d %H:%M %z') AS TIMESTAMP) AS DATE) AS "FORMAT_TZH"

both return the same results

@tobymao tobymao force-pushed the transpile_try_to_date_snowflake_duckdb branch from 2d6ce49 to 70f27ce Compare January 19, 2026 03:38
Comment on lines +1060 to +1062
"%:z": "%z", # In DuckDB %z can represent ±HH:MM, ±HHMM, or ±HH.
"%-z": "%z",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have tests that demonstrate DuckDB's %z is preserved when roundtripping?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added test

Comment on lines +672 to +676
INVERSE_TIME_MAPPING = {
"T": "T",
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look right, why are we mapping T to itself? This should happen automatically given how the trie works when working with time formats, right? What trigerred the need to add this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that TRY_TO_DATE('2024-01-31', 'AUTO') will be mapped to TRY_TO_DATE('2024-01-31', 'AU"T"O') without this

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's add a test for this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added test

Comment on lines 699 to 720
"FF0": "%n", # %n is the internal representation of nanoseconds
"ff0": "%n",
"FF1": "%n",
"ff1": "%n",
"FF2": "%n",
"ff2": "%n",
"FF3": "%n",
"ff3": "%n",
"FF4": "%n",
"ff4": "%n",
"FF5": "%n",
"ff5": "%n",
"FF6": "%n",
"ff6": "%n",
"FF7": "%n",
"ff7": "%n",
"FF8": "%n",
"ff8": "%n",
"FF9": "%n",
"ff9": "%n",
"FF": "%n",
"ff": "%n",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't look correct either. Doesn't the digit after FF designate precision? For example, isn't FF3 meant to be used for milliseconds?

If you map these to %n, then you break roundtrip because, e.g. FF3 maps back to ff which is the last key to be mapped to %n.

Were these tested?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ff works for ff[0-9] in Snowflake, should we preserve the precision digits (i.e FF1, FF5)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we should preserve digits, because Snowflake's docs only indicate equivalency between FF and FF9:

Fractional seconds with precision 0 (seconds) to 9 (nanoseconds), e.g. FF, FF0, FF3, FF9. Specifying FF is equivalent to FF9 (nanoseconds).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment on lines 613 to 608
self.validate_identity(
"""SELECT TO_TIMESTAMP('2025-01-16T14:45:30.123+0500', 'yyyy-mm-DD"T"hh24:mi:ss.ff3TZHTZM')"""
"""SELECT TO_TIMESTAMP('2025-01-16T14:45:30.123+0500', 'yyyy-mm-DDThh24:mi:ss.fftzhtzm')"""
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this test change?

"": "SELECT STR_TO_TIME('2025-01-16 14:45:30.123', '%Y-%m-%d %H:%M:%S.%f')",
"snowflake": "SELECT TO_TIMESTAMP('2025-01-16 14:45:30.123', 'yyyy-mm-DD hh24:mi:ss.ff6')",
"": "SELECT STR_TO_TIME('2025-01-16 14:45:30.123', '%Y-%m-%d %H:%M:%S.%n')",
"snowflake": "SELECT TO_TIMESTAMP('2025-01-16 14:45:30.123', 'yyyy-mm-DD hh24:mi:ss.ff')",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's double-check whether FFN can be safely converted into ff in all cases.

},
)
self.validate_all(
"TRY_TO_DATE('01-01-2000', 'MM-DD-YYYY')",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this test change?

)

self.validate_all(
"""TRY_TO_DATE('2013-04-28 20:57 +0700', 'YYYY-MM-DD HH24:MI TZHTZM')""",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: let's not use triple quotes for these, it's unnecessary. Simple " quotes suffice for this and other tests.

@fivetran-felixhuang fivetran-felixhuang force-pushed the transpile_try_to_date_snowflake_duckdb branch from 70f27ce to 14d84eb Compare January 19, 2026 21:38
@fivetran-felixhuang
Copy link
Collaborator Author

#6806

@fivetran-felixhuang fivetran-felixhuang deleted the transpile_try_to_date_snowflake_duckdb branch January 22, 2026 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants