Repository: tobymao/sqlglot

Issue:
DATETRUNC not supported for dialect="tsql"
**Description**
The issue exists in the latest version 26.0.0. DATETRUNC is an allowed function in T-SQL. Please see link below. However, it is not supported in sqlglot. Please see snippet below.

**Fully reproducible code snippet**
```python
parse_one("DATETRUNC(month, closed_date)", dialect="tsql")
Anonymous(
  this=DATETRUNC,
  expressions=[
    Column(
      this=Identifier(this=month, quoted=False)),
    Column(
      this=Identifier(this=closed_date, quoted=False))])`
``` 
**Expected output**
A parse tree similar to what we get for other dialects like redshift.
```python
parse_one("DATE_TRUNC(month, closed_date)", dialect="redshift")
TimestampTrunc(
  this=Column(
    this=Identifier(this=closed_date, quoted=False)),
  unit=Var(this=MONTH))
``` 
**Official Documentation**
[DATETRUNC in T-SQL](https://learn.microsoft.com/en-us/sql/t-sql/functions/datetrunc-transact-sql?view=sql-server-ver16)

Hints from issue comments:
@geooo109: Thank you @rajat-wisdom for pointing it out.
I am working on it. 

Solution patch:
diff --git a/sqlglot/dialects/tsql.py b/sqlglot/dialects/tsql.py
index 1acc5ca8a7..7aa7a0e22b 100644
--- a/sqlglot/dialects/tsql.py
+++ b/sqlglot/dialects/tsql.py
@@ -370,6 +370,16 @@ def _timestrtotime_sql(self: TSQL.Generator, expression: exp.TimeStrToTime):
     return sql
 
 
+def _build_datetrunc(args: t.List) -> exp.TimestampTrunc:
+    unit = seq_get(args, 0)
+    this = seq_get(args, 1)
+
+    if this and this.is_string:
+        this = exp.cast(this, exp.DataType.Type.DATETIME2)
+
+    return exp.TimestampTrunc(this=this, unit=unit)
+
+
 class TSQL(Dialect):
     SUPPORTS_SEMI_ANTI_JOIN = False
     LOG_BASE_FIRST = False
@@ -570,6 +580,7 @@ class Parser(parser.Parser):
             "SUSER_SNAME": exp.CurrentUser.from_arg_list,
             "SYSTEM_USER": exp.CurrentUser.from_arg_list,
             "TIMEFROMPARTS": _build_timefromparts,
+            "DATETRUNC": _build_datetrunc,
         }
 
         JOIN_HINTS = {"LOOP", "HASH", "MERGE", "REMOTE"}
@@ -936,6 +947,7 @@ class Generator(generator.Generator):
             exp.Trim: trim_sql,
             exp.TsOrDsAdd: date_delta_sql("DATEADD", cast=True),
             exp.TsOrDsDiff: date_delta_sql("DATEDIFF"),
+            exp.TimestampTrunc: lambda self, e: self.func("DATETRUNC", e.unit, e.this),
         }
 
         TRANSFORMS.pop(exp.ReturnsProperty)


Test patch:
diff --git a/tests/dialects/test_tsql.py b/tests/dialects/test_tsql.py
index e8cd69648b..4c61780d76 100644
--- a/tests/dialects/test_tsql.py
+++ b/tests/dialects/test_tsql.py
@@ -2090,3 +2090,27 @@ def test_next_value_for(self):
                 "oracle": "SELECT NEXT VALUE FOR db.schema.sequence_name",
             },
         )
+
+    # string literals in the DATETRUNC are casted as DATETIME2
+    def test_datetrunc(self):
+        self.validate_all(
+            "SELECT DATETRUNC(month, 'foo')",
+            write={
+                "duckdb": "SELECT DATE_TRUNC('MONTH', CAST('foo' AS TIMESTAMP))",
+                "tsql": "SELECT DATETRUNC(MONTH, CAST('foo' AS DATETIME2))",
+            },
+        )
+        self.validate_all(
+            "SELECT DATETRUNC(month, foo)",
+            write={
+                "duckdb": "SELECT DATE_TRUNC('MONTH', foo)",
+                "tsql": "SELECT DATETRUNC(MONTH, foo)",
+            },
+        )
+        self.validate_all(
+            "SELECT DATETRUNC(year, CAST('foo1' AS date))",
+            write={
+                "duckdb": "SELECT DATE_TRUNC('YEAR', CAST('foo1' AS DATE))",
+                "tsql": "SELECT DATETRUNC(YEAR, CAST('foo1' AS DATE))",
+            },
+        )