From d76e0e459a9e9293df1d10728a2b3c4b79d4a2cb Mon Sep 17 00:00:00 2001
From: mlx93 <mylesethan93@gmail.com>
Date: Mon, 24 Nov 2025 20:47:43 -0600
Subject: [PATCH] feat(mssql_compat): Add DATEDIFF extension for SQL Server
 compatibility

Implements datediff(datepart, start_date, end_date) function per PRD1a/PRD1b specs:

- Supports day, week, month, quarter, year dateparts with aliases
- Hybrid calculation model: full calendar units + contextual fractions
- Returns NUMERIC with 3 decimal precision (banker's rounding)
- Handles DATE, TIMESTAMP, and TIMESTAMPTZ input types
- Calendar alignment detection for whole-number returns
- Negative span handling (start > end returns negative)
- Case-insensitive datepart parsing
- IMMUTABLE STRICT PARALLEL SAFE function attributes

Files added:
- contrib/mssql_compat/mssql_compat.c - C implementation (~750 lines)
- contrib/mssql_compat/mssql_compat--1.0.sql - SQL function declarations
- contrib/mssql_compat/mssql_compat.control - Extension metadata
- contrib/mssql_compat/Makefile - PGXS build config
- contrib/mssql_compat/meson.build - Meson build config
- contrib/mssql_compat/sql/mssql_compat.sql - Regression tests
- contrib/mssql_compat/expected/mssql_compat.out - Expected output
- contrib/mssql_compat/sql/datediff_comprehensive_tests.sql - 50+ test cases

All regression tests pass.
---
 contrib/meson.build                           |   1 +
 contrib/mssql_compat/Makefile                 |  21 +
 .../mssql_compat/expected/mssql_compat.out    | 519 ++++++++++++
 contrib/mssql_compat/meson.build              |  36 +
 contrib/mssql_compat/mssql_compat--1.0.sql    |  55 ++
 contrib/mssql_compat/mssql_compat.c           | 751 ++++++++++++++++++
 contrib/mssql_compat/mssql_compat.control     |   7 +
 contrib/mssql_compat/results/mssql_compat.out | 519 ++++++++++++
 .../sql/datediff_comprehensive_tests.sql      | 598 ++++++++++++++
 contrib/mssql_compat/sql/mssql_compat.sql     | 173 ++++
 10 files changed, 2680 insertions(+)
 create mode 100644 contrib/mssql_compat/Makefile
 create mode 100644 contrib/mssql_compat/expected/mssql_compat.out
 create mode 100644 contrib/mssql_compat/meson.build
 create mode 100644 contrib/mssql_compat/mssql_compat--1.0.sql
 create mode 100644 contrib/mssql_compat/mssql_compat.c
 create mode 100644 contrib/mssql_compat/mssql_compat.control
 create mode 100644 contrib/mssql_compat/results/mssql_compat.out
 create mode 100644 contrib/mssql_compat/sql/datediff_comprehensive_tests.sql
 create mode 100644 contrib/mssql_compat/sql/mssql_compat.sql

diff --git a/contrib/meson.build b/contrib/meson.build
index ed30ee7d639..6a48df8daf8 100644
--- a/contrib/meson.build
+++ b/contrib/meson.build
@@ -40,6 +40,7 @@ subdir('jsonb_plpython')
 subdir('lo')
 subdir('ltree')
 subdir('ltree_plpython')
+subdir('mssql_compat')
 subdir('oid2name')
 subdir('pageinspect')
 subdir('passwordcheck')
diff --git a/contrib/mssql_compat/Makefile b/contrib/mssql_compat/Makefile
new file mode 100644
index 00000000000..7dbb3409703
--- /dev/null
+++ b/contrib/mssql_compat/Makefile
@@ -0,0 +1,21 @@
+# contrib/mssql_compat/Makefile
+
+MODULES = mssql_compat
+
+EXTENSION = mssql_compat
+DATA = mssql_compat--1.0.sql
+PGFILEDESC = "mssql_compat - SQL Server compatible date functions"
+
+REGRESS = mssql_compat
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/mssql_compat
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
diff --git a/contrib/mssql_compat/expected/mssql_compat.out b/contrib/mssql_compat/expected/mssql_compat.out
new file mode 100644
index 00000000000..3fd45c1ecc6
--- /dev/null
+++ b/contrib/mssql_compat/expected/mssql_compat.out
@@ -0,0 +1,519 @@
+--
+-- Test cases for mssql_compat extension
+-- Covers PRD1a unit tests (UT-01 to UT-15) and edge cases (EC-01 to EC-06)
+--
+CREATE EXTENSION mssql_compat;
+--
+-- Basic Day Calculations (UT-01, UT-02)
+--
+SELECT 'UT-01: Day difference basic' AS test;
+            test             
+-----------------------------
+ UT-01: Day difference basic
+(1 row)
+
+SELECT datediff('day', '2024-01-01'::date, '2024-01-15'::date);
+ datediff 
+----------
+       14
+(1 row)
+
+SELECT 'UT-02: Day difference negative' AS test;
+              test              
+--------------------------------
+ UT-02: Day difference negative
+(1 row)
+
+SELECT datediff('day', '2024-01-15'::date, '2024-01-01'::date);
+ datediff 
+----------
+      -14
+(1 row)
+
+--
+-- Week Calculations (UT-03, UT-04)
+--
+SELECT 'UT-03: Week exact' AS test;
+       test        
+-------------------
+ UT-03: Week exact
+(1 row)
+
+SELECT datediff('week', '2024-01-01'::date, '2024-01-08'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'UT-04: Week partial' AS test;
+        test         
+---------------------
+ UT-04: Week partial
+(1 row)
+
+SELECT datediff('week', '2024-01-01'::date, '2024-01-10'::date);
+ datediff 
+----------
+    1.286
+(1 row)
+
+--
+-- Month Calculations (UT-05, UT-06, UT-07)
+--
+SELECT 'UT-05: Month aligned' AS test;
+         test         
+----------------------
+ UT-05: Month aligned
+(1 row)
+
+SELECT datediff('month', '2024-01-15'::date, '2024-02-15'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'UT-06: Month partial' AS test;
+         test         
+----------------------
+ UT-06: Month partial
+(1 row)
+
+SELECT datediff('month', '2024-01-15'::date, '2024-02-20'::date);
+ datediff 
+----------
+    1.172
+(1 row)
+
+SELECT 'UT-07: Month end-of-month alignment' AS test;
+                test                 
+-------------------------------------
+ UT-07: Month end-of-month alignment
+(1 row)
+
+SELECT datediff('month', '2024-01-31'::date, '2024-02-29'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+--
+-- Quarter Calculations (UT-08, UT-09)
+--
+SELECT 'UT-08: Quarter aligned' AS test;
+          test          
+------------------------
+ UT-08: Quarter aligned
+(1 row)
+
+SELECT datediff('quarter', '2024-01-01'::date, '2024-04-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'UT-09: Quarter partial' AS test;
+          test          
+------------------------
+ UT-09: Quarter partial
+(1 row)
+
+SELECT datediff('quarter', '2024-01-15'::date, '2024-05-20'::date);
+ datediff 
+----------
+    1.385
+(1 row)
+
+--
+-- Year Calculations (UT-10, UT-11)
+--
+SELECT 'UT-10: Year aligned' AS test;
+        test         
+---------------------
+ UT-10: Year aligned
+(1 row)
+
+SELECT datediff('year', '2024-03-15'::date, '2025-03-15'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'UT-11: Year partial leap year' AS test;
+             test              
+-------------------------------
+ UT-11: Year partial leap year
+(1 row)
+
+SELECT datediff('year', '2024-01-01'::date, '2024-07-01'::date);
+ datediff 
+----------
+    0.497
+(1 row)
+
+--
+-- NULL Handling (UT-12, UT-13) - STRICT functions return NULL for NULL inputs
+--
+SELECT 'UT-12: NULL start date' AS test;
+          test          
+------------------------
+ UT-12: NULL start date
+(1 row)
+
+SELECT datediff('day', NULL::date, '2024-01-15'::date);
+ datediff 
+----------
+         
+(1 row)
+
+SELECT 'UT-13: NULL end date' AS test;
+         test         
+----------------------
+ UT-13: NULL end date
+(1 row)
+
+SELECT datediff('day', '2024-01-01'::date, NULL::date);
+ datediff 
+----------
+         
+(1 row)
+
+--
+-- Invalid Datepart (UT-14)
+--
+SELECT 'UT-14: Invalid datepart' AS test;
+          test           
+-------------------------
+ UT-14: Invalid datepart
+(1 row)
+
+SELECT datediff('hour', '2024-01-01'::date, '2024-01-02'::date);
+ERROR:  Invalid datepart: 'hour'
+HINT:  Valid options: year, quarter, month, week, day
+--
+-- Case Insensitivity (UT-15)
+--
+SELECT 'UT-15: Case insensitive datepart' AS test;
+               test               
+----------------------------------
+ UT-15: Case insensitive datepart
+(1 row)
+
+SELECT datediff('MONTH', '2024-01-01'::date, '2024-02-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT datediff('Month', '2024-01-01'::date, '2024-02-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT datediff('month', '2024-01-01'::date, '2024-02-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+--
+-- Edge Cases (EC-01 to EC-06)
+--
+SELECT 'EC-01: Same date' AS test;
+       test       
+------------------
+ EC-01: Same date
+(1 row)
+
+SELECT datediff('day', '2024-01-01'::date, '2024-01-01'::date);
+ datediff 
+----------
+        0
+(1 row)
+
+SELECT 'EC-02: Leap year February 29' AS test;
+             test             
+------------------------------
+ EC-02: Leap year February 29
+(1 row)
+
+SELECT datediff('day', '2024-02-28'::date, '2024-03-01'::date);
+ datediff 
+----------
+        2
+(1 row)
+
+SELECT 'EC-03: Non-leap year February' AS test;
+             test              
+-------------------------------
+ EC-03: Non-leap year February
+(1 row)
+
+SELECT datediff('day', '2023-02-28'::date, '2023-03-01'::date);
+ datediff 
+----------
+        1
+(1 row)
+
+SELECT 'EC-04: Year boundary' AS test;
+         test         
+----------------------
+ EC-04: Year boundary
+(1 row)
+
+SELECT datediff('year', '2024-12-31'::date, '2025-01-01'::date);
+ datediff 
+----------
+    0.003
+(1 row)
+
+SELECT 'EC-05: Multi-year span' AS test;
+          test          
+------------------------
+ EC-05: Multi-year span
+(1 row)
+
+SELECT datediff('year', '2020-01-01'::date, '2025-01-01'::date);
+ datediff 
+----------
+    5.000
+(1 row)
+
+SELECT 'EC-06: Century boundary' AS test;
+          test           
+-------------------------
+ EC-06: Century boundary
+(1 row)
+
+SELECT datediff('day', '1999-12-31'::date, '2000-01-01'::date);
+ datediff 
+----------
+        1
+(1 row)
+
+--
+-- Alias Tests (from PRD1b lines 224-230)
+--
+SELECT 'Alias: yy for year' AS test;
+        test        
+--------------------
+ Alias: yy for year
+(1 row)
+
+SELECT datediff('yy', '2024-01-01'::date, '2025-01-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: yyyy for year' AS test;
+         test         
+----------------------
+ Alias: yyyy for year
+(1 row)
+
+SELECT datediff('yyyy', '2024-01-01'::date, '2025-01-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: mm for month' AS test;
+        test         
+---------------------
+ Alias: mm for month
+(1 row)
+
+SELECT datediff('mm', '2024-01-15'::date, '2024-02-15'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: qq for quarter' AS test;
+         test          
+-----------------------
+ Alias: qq for quarter
+(1 row)
+
+SELECT datediff('qq', '2024-01-01'::date, '2024-04-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: wk for week' AS test;
+        test        
+--------------------
+ Alias: wk for week
+(1 row)
+
+SELECT datediff('wk', '2024-01-01'::date, '2024-01-08'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: dd for day' AS test;
+       test        
+-------------------
+ Alias: dd for day
+(1 row)
+
+SELECT datediff('dd', '2024-01-01'::date, '2024-01-15'::date);
+ datediff 
+----------
+       14
+(1 row)
+
+--
+-- Timestamp Tests
+--
+SELECT 'Timestamp: basic day diff' AS test;
+           test            
+---------------------------
+ Timestamp: basic day diff
+(1 row)
+
+SELECT datediff('day', '2024-01-01 10:30:00'::timestamp, '2024-01-15 14:45:00'::timestamp);
+ datediff 
+----------
+       14
+(1 row)
+
+SELECT 'Timestamp: month diff' AS test;
+         test          
+-----------------------
+ Timestamp: month diff
+(1 row)
+
+SELECT datediff('month', '2024-01-15 08:00:00'::timestamp, '2024-02-20 16:00:00'::timestamp);
+ datediff 
+----------
+    1.172
+(1 row)
+
+--
+-- Timestamptz Tests
+--
+SELECT 'Timestamptz: basic day diff' AS test;
+            test             
+-----------------------------
+ Timestamptz: basic day diff
+(1 row)
+
+SELECT datediff('day', '2024-01-01 10:30:00+00'::timestamptz, '2024-01-15 14:45:00+00'::timestamptz);
+ datediff 
+----------
+       14
+(1 row)
+
+--
+-- Additional Month Calculation Tests
+--
+SELECT 'Month: Jan 25 to Mar 10 (PRD walkthrough example)' AS test;
+                       test                        
+---------------------------------------------------
+ Month: Jan 25 to Mar 10 (PRD walkthrough example)
+(1 row)
+
+SELECT datediff('month', '2024-01-25'::date, '2024-03-10'::date);
+ datediff 
+----------
+    1.483
+(1 row)
+
+SELECT 'Month: subscription proration example' AS test;
+                 test                  
+---------------------------------------
+ Month: subscription proration example
+(1 row)
+
+SELECT datediff('month', '2024-01-15'::date, '2024-02-20'::date);
+ datediff 
+----------
+    1.172
+(1 row)
+
+--
+-- Additional Quarter Calculation Tests
+--
+SELECT 'Quarter: PRD walkthrough example' AS test;
+               test               
+----------------------------------
+ Quarter: PRD walkthrough example
+(1 row)
+
+SELECT datediff('quarter', '2024-01-15'::date, '2024-05-20'::date);
+ datediff 
+----------
+    1.385
+(1 row)
+
+--
+-- Additional Year Calculation Tests
+--
+SELECT 'Year: PRD walkthrough example' AS test;
+             test              
+-------------------------------
+ Year: PRD walkthrough example
+(1 row)
+
+SELECT datediff('year', '2024-03-15'::date, '2025-06-20'::date);
+ datediff 
+----------
+    1.266
+(1 row)
+
+SELECT 'Year: exact 5-year tenure' AS test;
+           test            
+---------------------------
+ Year: exact 5-year tenure
+(1 row)
+
+SELECT datediff('year', '2020-03-15'::date, '2025-03-15'::date);
+ datediff 
+----------
+    5.000
+(1 row)
+
+SELECT 'Year: leap year partial (182 days / 366)' AS test;
+                   test                   
+------------------------------------------
+ Year: leap year partial (182 days / 366)
+(1 row)
+
+SELECT datediff('year', '2024-01-01'::date, '2024-07-01'::date);
+ datediff 
+----------
+    0.497
+(1 row)
+
+--
+-- Week Calculation Additional Tests
+--
+SELECT 'Week: exact 2 weeks' AS test;
+        test         
+---------------------
+ Week: exact 2 weeks
+(1 row)
+
+SELECT datediff('week', '2024-01-01'::date, '2024-01-15'::date);
+ datediff 
+----------
+    2.000
+(1 row)
+
+SELECT 'Week: PRD example 9 days' AS test;
+           test           
+--------------------------
+ Week: PRD example 9 days
+(1 row)
+
+SELECT datediff('week', '2024-01-01'::date, '2024-01-10'::date);
+ datediff 
+----------
+    1.286
+(1 row)
+
+DROP EXTENSION mssql_compat;
diff --git a/contrib/mssql_compat/meson.build b/contrib/mssql_compat/meson.build
new file mode 100644
index 00000000000..d1a412ed879
--- /dev/null
+++ b/contrib/mssql_compat/meson.build
@@ -0,0 +1,36 @@
+# Copyright (c) 2022-2025, PostgreSQL Global Development Group
+
+mssql_compat_sources = files(
+  'mssql_compat.c',
+)
+
+if host_system == 'windows'
+  mssql_compat_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'mssql_compat',
+    '--FILEDESC', 'mssql_compat - SQL Server compatible date functions',])
+endif
+
+mssql_compat = shared_module('mssql_compat',
+  mssql_compat_sources,
+  kwargs: contrib_mod_args,
+)
+
+contrib_targets += mssql_compat
+
+install_data(
+  'mssql_compat--1.0.sql',
+  'mssql_compat.control',
+  kwargs: contrib_data_args,
+)
+
+tests += {
+  'name': 'mssql_compat',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'regress': {
+    'sql': [
+      'mssql_compat',
+    ],
+  },
+}
+
diff --git a/contrib/mssql_compat/mssql_compat--1.0.sql b/contrib/mssql_compat/mssql_compat--1.0.sql
new file mode 100644
index 00000000000..8b950cb4cc4
--- /dev/null
+++ b/contrib/mssql_compat/mssql_compat--1.0.sql
@@ -0,0 +1,55 @@
+/* contrib/mssql_compat/mssql_compat--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION mssql_compat" to load this file. \quit
+
+--
+-- datediff(datepart, start_date, end_date) - SQL Server compatible date difference
+--
+-- Returns the difference between two dates in the specified datepart unit.
+-- Supports: year, quarter, month, week, day (and aliases)
+--
+-- Unlike SQL Server's boundary-crossing semantics, this implementation provides
+-- mathematically accurate results using a hybrid calculation model: full calendar
+-- units plus contextual fractions based on actual period lengths.
+--
+
+-- Date version
+CREATE FUNCTION datediff(
+    datepart TEXT,
+    start_date DATE,
+    end_date DATE
+)
+RETURNS NUMERIC
+AS 'MODULE_PATHNAME', 'datediff_date'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+COMMENT ON FUNCTION datediff(TEXT, DATE, DATE) IS
+'Calculate the difference between two dates in the specified unit (year, quarter, month, week, day)';
+
+-- Timestamp version
+CREATE FUNCTION datediff(
+    datepart TEXT,
+    start_ts TIMESTAMP,
+    end_ts TIMESTAMP
+)
+RETURNS NUMERIC
+AS 'MODULE_PATHNAME', 'datediff_timestamp'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+COMMENT ON FUNCTION datediff(TEXT, TIMESTAMP, TIMESTAMP) IS
+'Calculate the difference between two timestamps in the specified unit (year, quarter, month, week, day)';
+
+-- Timestamptz version
+CREATE FUNCTION datediff(
+    datepart TEXT,
+    start_tstz TIMESTAMPTZ,
+    end_tstz TIMESTAMPTZ
+)
+RETURNS NUMERIC
+AS 'MODULE_PATHNAME', 'datediff_timestamptz'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+COMMENT ON FUNCTION datediff(TEXT, TIMESTAMPTZ, TIMESTAMPTZ) IS
+'Calculate the difference between two timestamps with timezone in the specified unit (year, quarter, month, week, day)';
+
diff --git a/contrib/mssql_compat/mssql_compat.c b/contrib/mssql_compat/mssql_compat.c
new file mode 100644
index 00000000000..1db22f37982
--- /dev/null
+++ b/contrib/mssql_compat/mssql_compat.c
@@ -0,0 +1,751 @@
+/*-------------------------------------------------------------------------
+ *
+ * mssql_compat.c
+ *		SQL Server compatible datediff function for PostgreSQL.
+ *
+ * This extension provides datediff(datepart, start_date, end_date) which
+ * calculates the difference between two dates using a hybrid calculation
+ * model: full calendar units plus contextual fractions based on actual
+ * period lengths.
+ *
+ * Copyright (c) 2024, PostgreSQL Global Development Group
+ *
+ * contrib/mssql_compat/mssql_compat.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <ctype.h>
+#include <math.h>
+
+#include "datatype/timestamp.h"
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/datetime.h"
+#include "utils/numeric.h"
+#include "utils/timestamp.h"
+
+PG_MODULE_MAGIC_EXT(
+					.name = "mssql_compat",
+					.version = PG_VERSION
+);
+
+/*
+ * Datepart enumeration for routing calculation logic
+ */
+typedef enum
+{
+	DATEPART_DAY,
+	DATEPART_WEEK,
+	DATEPART_MONTH,
+	DATEPART_QUARTER,
+	DATEPART_YEAR,
+	DATEPART_INVALID
+} DatepartType;
+
+/*
+ * parse_datepart - convert datepart string to enum
+ *
+ * Performs case-insensitive comparison and handles aliases.
+ * Returns DATEPART_INVALID for unrecognized input.
+ */
+static DatepartType
+parse_datepart(const char *datepart_str)
+{
+	char		lower[32];
+	int			i;
+
+	/* Convert to lowercase for comparison */
+	for (i = 0; datepart_str[i] && i < 31; i++)
+		lower[i] = tolower((unsigned char) datepart_str[i]);
+	lower[i] = '\0';
+
+	/* Match canonical names and aliases per PRD1b lines 224-230 */
+	if (strcmp(lower, "year") == 0 ||
+		strcmp(lower, "yy") == 0 ||
+		strcmp(lower, "yyyy") == 0 ||
+		strcmp(lower, "y") == 0 ||
+		strcmp(lower, "years") == 0)
+		return DATEPART_YEAR;
+
+	if (strcmp(lower, "quarter") == 0 ||
+		strcmp(lower, "qq") == 0 ||
+		strcmp(lower, "q") == 0 ||
+		strcmp(lower, "quarters") == 0)
+		return DATEPART_QUARTER;
+
+	if (strcmp(lower, "month") == 0 ||
+		strcmp(lower, "mm") == 0 ||
+		strcmp(lower, "m") == 0 ||
+		strcmp(lower, "months") == 0)
+		return DATEPART_MONTH;
+
+	if (strcmp(lower, "week") == 0 ||
+		strcmp(lower, "wk") == 0 ||
+		strcmp(lower, "ww") == 0 ||
+		strcmp(lower, "w") == 0 ||
+		strcmp(lower, "weeks") == 0)
+		return DATEPART_WEEK;
+
+	if (strcmp(lower, "day") == 0 ||
+		strcmp(lower, "dd") == 0 ||
+		strcmp(lower, "d") == 0 ||
+		strcmp(lower, "days") == 0)
+		return DATEPART_DAY;
+
+	return DATEPART_INVALID;
+}
+
+/*
+ * days_in_month_helper - get days in a specific month
+ *
+ * Uses PostgreSQL's day_tab array (per PRD1b lines 235-252)
+ * month is 1-based (1=January, 12=December)
+ */
+static int
+days_in_month_helper(int year, int month)
+{
+	return day_tab[isleap(year) ? 1 : 0][month - 1];
+}
+
+/*
+ * is_end_of_month - check if day is the last day of its month
+ */
+static bool
+is_end_of_month(int year, int month, int day)
+{
+	return day == days_in_month_helper(year, month);
+}
+
+/*
+ * days_in_quarter - get total days in a specific quarter
+ *
+ * Quarter is 1-4.
+ * Q1: Jan+Feb+Mar, Q2: Apr+May+Jun, Q3: Jul+Aug+Sep, Q4: Oct+Nov+Dec
+ */
+static int
+days_in_quarter(int year, int quarter)
+{
+	int			first_month = (quarter - 1) * 3 + 1;
+	int			days = 0;
+	int			i;
+
+	for (i = 0; i < 3; i++)
+		days += days_in_month_helper(year, first_month + i);
+
+	return days;
+}
+
+/*
+ * day_of_quarter - get day position within a quarter (1-92)
+ */
+static int
+day_of_quarter(int year, int month, int day)
+{
+	int			quarter = (month - 1) / 3 + 1;
+	int			first_month = (quarter - 1) * 3 + 1;
+	int			days = 0;
+	int			m;
+
+	/* Sum days in complete months before this month within the quarter */
+	for (m = first_month; m < month; m++)
+		days += days_in_month_helper(year, m);
+
+	return days + day;
+}
+
+/*
+ * bankers_round - round to 3 decimal places using HALF_EVEN (banker's rounding)
+ *
+ * Per PRD1a FR-7: "Decimal results SHALL be rounded to exactly 3 decimal places
+ * using HALF_EVEN (banker's) rounding"
+ */
+static double
+bankers_round(double value)
+{
+	double		scaled = value * 1000.0;
+	double		integer_part;
+	double		frac = modf(scaled, &integer_part);
+	int64		int_val = (int64) integer_part;
+
+	/*
+	 * Banker's rounding: round half to even
+	 * If fraction is exactly 0.5, round to nearest even number
+	 */
+	if (fabs(frac) == 0.5)
+	{
+		/* Round to even */
+		if (int_val % 2 == 0)
+			scaled = integer_part;		/* Already even, truncate */
+		else
+			scaled = integer_part + (value >= 0 ? 1.0 : -1.0);	/* Round away */
+	}
+	else
+	{
+		/* Standard rounding */
+		scaled = round(scaled);
+	}
+
+	return scaled / 1000.0;
+}
+
+/*
+ * make_numeric_result - convert double to NUMERIC with 3 decimal places
+ *
+ * Uses string conversion approach as per PRD1b lines 167-174
+ */
+static Datum
+make_numeric_result(double value)
+{
+	char		result_str[32];
+	Datum		result;
+
+	snprintf(result_str, sizeof(result_str), "%.3f", value);
+	result = DirectFunctionCall3(numeric_in,
+								 CStringGetDatum(result_str),
+								 ObjectIdGetDatum(InvalidOid),
+								 Int32GetDatum(-1));
+	return result;
+}
+
+/*
+ * compute_diff_day - calculate day difference
+ *
+ * Simple subtraction, returns whole number as NUMERIC.
+ */
+static Datum
+compute_diff_day(int start_y, int start_m, int start_d,
+				 int end_y, int end_m, int end_d)
+{
+	int			start_jd = date2j(start_y, start_m, start_d);
+	int			end_jd = date2j(end_y, end_m, end_d);
+	int64		diff = (int64) end_jd - (int64) start_jd;
+
+	return NumericGetDatum(int64_to_numeric(diff));
+}
+
+/*
+ * compute_diff_week - calculate week difference
+ *
+ * Total days / 7, rounded to 3 decimal places.
+ */
+static Datum
+compute_diff_week(int start_y, int start_m, int start_d,
+				  int end_y, int end_m, int end_d)
+{
+	int			start_jd = date2j(start_y, start_m, start_d);
+	int			end_jd = date2j(end_y, end_m, end_d);
+	int64		days = (int64) end_jd - (int64) start_jd;
+	double		weeks = (double) days / 7.0;
+
+	return make_numeric_result(bankers_round(weeks));
+}
+
+/*
+ * compute_diff_month - calculate month difference using hybrid model
+ *
+ * Per PRD1a FR-3/FR-4:
+ * - Aligned dates (same day-of-month or both end-of-month) return whole numbers
+ * - Non-aligned: full months + (remaining days / days in partial period)
+ */
+static Datum
+compute_diff_month(int start_y, int start_m, int start_d,
+				   int end_y, int end_m, int end_d)
+{
+	bool		negated = false;
+	int			full_months;
+	int			remaining_days;
+	int			partial_period_days;
+	double		result;
+	bool		start_eom;
+	bool		end_eom;
+	bool		aligned;
+	int			anniversary_y, anniversary_m, anniversary_d;
+	int			anniversary_jd, end_jd;
+
+	/* Handle negative spans by swapping and negating result (FR-6) */
+	if (start_y > end_y ||
+		(start_y == end_y && start_m > end_m) ||
+		(start_y == end_y && start_m == end_m && start_d > end_d))
+	{
+		int			tmp_y = start_y, tmp_m = start_m, tmp_d = start_d;
+
+		start_y = end_y;
+		start_m = end_m;
+		start_d = end_d;
+		end_y = tmp_y;
+		end_m = tmp_m;
+		end_d = tmp_d;
+		negated = true;
+	}
+
+	/* Check for calendar alignment (FR-4) */
+	start_eom = is_end_of_month(start_y, start_m, start_d);
+	end_eom = is_end_of_month(end_y, end_m, end_d);
+	aligned = (start_d == end_d) || (start_eom && end_eom);
+
+	/* Calculate full months */
+	full_months = (end_y - start_y) * 12 + (end_m - start_m);
+
+	if (aligned)
+	{
+		/* Aligned dates return whole numbers */
+		result = (double) full_months;
+	}
+	else
+	{
+		/*
+		 * Find the last "anniversary" before or on end_date.
+		 * Anniversary is the same day-of-month as start_d, or end-of-month
+		 * if start was end-of-month.
+		 */
+		if (end_d < start_d)
+			full_months--;
+
+		if (full_months < 0)
+			full_months = 0;
+
+		/* Calculate anniversary date */
+		anniversary_y = start_y + (start_m + full_months - 1) / 12;
+		anniversary_m = ((start_m - 1 + full_months) % 12) + 1;
+
+		/*
+		 * Handle case where start_d doesn't exist in anniversary month
+		 * (e.g., Jan 31 -> Feb has no 31st)
+		 */
+		if (start_d > days_in_month_helper(anniversary_y, anniversary_m))
+			anniversary_d = days_in_month_helper(anniversary_y, anniversary_m);
+		else
+			anniversary_d = start_d;
+
+		/* Calculate remaining days after anniversary */
+		anniversary_jd = date2j(anniversary_y, anniversary_m, anniversary_d);
+		end_jd = date2j(end_y, end_m, end_d);
+		remaining_days = end_jd - anniversary_jd;
+
+		/*
+		 * Calculate partial period length (days from anniversary to next
+		 * anniversary)
+		 */
+		{
+			int			next_anniversary_y = anniversary_y + (anniversary_m) / 12;
+			int			next_anniversary_m = (anniversary_m % 12) + 1;
+			int			next_anniversary_d;
+			int			next_anniversary_jd;
+
+			if (start_d > days_in_month_helper(next_anniversary_y, next_anniversary_m))
+				next_anniversary_d = days_in_month_helper(next_anniversary_y, next_anniversary_m);
+			else
+				next_anniversary_d = start_d;
+
+			next_anniversary_jd = date2j(next_anniversary_y, next_anniversary_m, next_anniversary_d);
+			partial_period_days = next_anniversary_jd - anniversary_jd;
+		}
+
+		if (partial_period_days <= 0)
+			partial_period_days = 1;	/* Safety guard */
+
+		result = (double) full_months + (double) remaining_days / (double) partial_period_days;
+	}
+
+	if (negated)
+		result = -result;
+
+	return make_numeric_result(bankers_round(result));
+}
+
+/*
+ * compute_diff_quarter - calculate quarter difference using hybrid model
+ *
+ * Similar to month but with quarter-based periods.
+ */
+static Datum
+compute_diff_quarter(int start_y, int start_m, int start_d,
+					 int end_y, int end_m, int end_d)
+{
+	bool		negated = false;
+	int			start_quarter, end_quarter;
+	int			start_day_of_qtr, end_day_of_qtr;
+	int			full_quarters;
+	int			remaining_days;
+	int			partial_period_days;
+	double		result;
+
+	/* Handle negative spans */
+	if (start_y > end_y ||
+		(start_y == end_y && start_m > end_m) ||
+		(start_y == end_y && start_m == end_m && start_d > end_d))
+	{
+		int			tmp_y = start_y, tmp_m = start_m, tmp_d = start_d;
+
+		start_y = end_y;
+		start_m = end_m;
+		start_d = end_d;
+		end_y = tmp_y;
+		end_m = tmp_m;
+		end_d = tmp_d;
+		negated = true;
+	}
+
+	start_quarter = (start_m - 1) / 3 + 1;
+	end_quarter = (end_m - 1) / 3 + 1;
+	start_day_of_qtr = day_of_quarter(start_y, start_m, start_d);
+	end_day_of_qtr = day_of_quarter(end_y, end_m, end_d);
+
+	/* Calculate full quarters */
+	full_quarters = (end_y - start_y) * 4 + (end_quarter - start_quarter);
+
+	/* Check alignment: same day-of-quarter position */
+	if (start_day_of_qtr == end_day_of_qtr)
+	{
+		result = (double) full_quarters;
+	}
+	else
+	{
+		/*
+		 * Non-aligned: find anniversary (same position in quarter), calculate
+		 * remaining days
+		 */
+		int			anniversary_y, anniversary_quarter, anniversary_m, anniversary_d;
+		int			anniversary_jd, end_jd;
+		/* Adjust full_quarters if end is before anniversary position */
+		if (end_day_of_qtr < start_day_of_qtr)
+			full_quarters--;
+
+		if (full_quarters < 0)
+			full_quarters = 0;
+
+		/* Calculate anniversary date */
+		anniversary_quarter = start_quarter + full_quarters;
+		anniversary_y = start_y + (anniversary_quarter - 1) / 4;
+		anniversary_quarter = ((anniversary_quarter - 1) % 4) + 1;
+
+		/* Convert day-of-quarter back to month and day */
+		{
+			int			first_month = (anniversary_quarter - 1) * 3 + 1;
+			int			days_remaining = start_day_of_qtr;
+			int			m;
+			bool		found = false;
+
+			anniversary_m = first_month;
+			anniversary_d = 1;	/* Default initialization */
+			for (m = first_month; m <= first_month + 2 && days_remaining > 0; m++)
+			{
+				int			days_in_m = days_in_month_helper(anniversary_y, m);
+
+				if (days_remaining <= days_in_m)
+				{
+					anniversary_m = m;
+					anniversary_d = days_remaining;
+					found = true;
+					break;
+				}
+				days_remaining -= days_in_m;
+			}
+
+			/* Handle overflow (day position exceeds quarter length) */
+			if (!found)
+			{
+				anniversary_m = first_month + 2;
+				anniversary_d = days_in_month_helper(anniversary_y, anniversary_m);
+			}
+		}
+
+		/* Ensure anniversary_d is valid */
+		if (anniversary_d > days_in_month_helper(anniversary_y, anniversary_m))
+			anniversary_d = days_in_month_helper(anniversary_y, anniversary_m);
+
+		anniversary_jd = date2j(anniversary_y, anniversary_m, anniversary_d);
+		end_jd = date2j(end_y, end_m, end_d);
+		remaining_days = end_jd - anniversary_jd;
+
+		/* Partial period is the quarter containing the anniversary */
+		partial_period_days = days_in_quarter(anniversary_y, anniversary_quarter);
+
+		if (partial_period_days <= 0)
+			partial_period_days = 1;
+
+		result = (double) full_quarters + (double) remaining_days / (double) partial_period_days;
+	}
+
+	if (negated)
+		result = -result;
+
+	return make_numeric_result(bankers_round(result));
+}
+
+/*
+ * compute_diff_year - calculate year difference using hybrid model
+ *
+ * Similar to month but with year-based periods.
+ */
+static Datum
+compute_diff_year(int start_y, int start_m, int start_d,
+				  int end_y, int end_m, int end_d)
+{
+	bool		negated = false;
+	int			full_years;
+	int			remaining_days;
+	int			partial_period_days;
+	double		result;
+	bool		aligned;
+	int			anniversary_y, anniversary_m, anniversary_d;
+	int			anniversary_jd, end_jd;
+
+	/* Handle negative spans */
+	if (start_y > end_y ||
+		(start_y == end_y && start_m > end_m) ||
+		(start_y == end_y && start_m == end_m && start_d > end_d))
+	{
+		int			tmp_y = start_y, tmp_m = start_m, tmp_d = start_d;
+
+		start_y = end_y;
+		start_m = end_m;
+		start_d = end_d;
+		end_y = tmp_y;
+		end_m = tmp_m;
+		end_d = tmp_d;
+		negated = true;
+	}
+
+	/* Check alignment: same month and day, or Feb 29 -> Feb 28 in non-leap */
+	aligned = (start_m == end_m && start_d == end_d);
+
+	/* Special case: Feb 29 in leap year aligns with Feb 28 in non-leap */
+	if (!aligned && start_m == 2 && start_d == 29 && end_m == 2 && end_d == 28)
+	{
+		if (!isleap(end_y))
+			aligned = true;
+	}
+	if (!aligned && start_m == 2 && start_d == 28 && end_m == 2 && end_d == 29)
+	{
+		if (!isleap(start_y))
+			aligned = true;
+	}
+
+	/* Calculate full years */
+	full_years = end_y - start_y;
+	if (end_m < start_m || (end_m == start_m && end_d < start_d))
+		full_years--;
+
+	if (full_years < 0)
+		full_years = 0;
+
+	if (aligned && full_years > 0)
+	{
+		result = (double) full_years;
+	}
+	else if (aligned && full_years == 0 && end_y > start_y)
+	{
+		/* Exact one year */
+		result = 1.0;
+	}
+	else if (start_y == end_y && start_m == end_m && start_d == end_d)
+	{
+		/* Same date */
+		result = 0.0;
+	}
+	else
+	{
+		/* Non-aligned: calculate fractional part */
+		anniversary_y = start_y + full_years;
+		anniversary_m = start_m;
+
+		/* Handle Feb 29 when anniversary year is not a leap year */
+		if (start_m == 2 && start_d == 29 && !isleap(anniversary_y))
+			anniversary_d = 28;
+		else if (start_d > days_in_month_helper(anniversary_y, anniversary_m))
+			anniversary_d = days_in_month_helper(anniversary_y, anniversary_m);
+		else
+			anniversary_d = start_d;
+
+		anniversary_jd = date2j(anniversary_y, anniversary_m, anniversary_d);
+		end_jd = date2j(end_y, end_m, end_d);
+		remaining_days = end_jd - anniversary_jd;
+
+		/*
+		 * Partial period: days from anniversary to next anniversary The
+		 * period uses the year that contains the partial span
+		 */
+		{
+			int			next_anniversary_y = anniversary_y + 1;
+			int			next_anniversary_m = anniversary_m;
+			int			next_anniversary_d;
+			int			next_anniversary_jd;
+
+			if (start_m == 2 && start_d == 29 && !isleap(next_anniversary_y))
+				next_anniversary_d = 28;
+			else if (start_d > days_in_month_helper(next_anniversary_y, next_anniversary_m))
+				next_anniversary_d = days_in_month_helper(next_anniversary_y, next_anniversary_m);
+			else
+				next_anniversary_d = start_d;
+
+			next_anniversary_jd = date2j(next_anniversary_y, next_anniversary_m, next_anniversary_d);
+			partial_period_days = next_anniversary_jd - anniversary_jd;
+		}
+
+		if (partial_period_days <= 0)
+			partial_period_days = 1;
+
+		result = (double) full_years + (double) remaining_days / (double) partial_period_days;
+	}
+
+	if (negated)
+		result = -result;
+
+	return make_numeric_result(bankers_round(result));
+}
+
+/*
+ * datediff_internal - core calculation dispatcher
+ *
+ * Takes year, month, day for both dates and computes the difference
+ * based on the specified datepart.
+ */
+static Datum
+datediff_internal(const char *datepart_str,
+				  int start_y, int start_m, int start_d,
+				  int end_y, int end_m, int end_d)
+{
+	DatepartType datepart = parse_datepart(datepart_str);
+
+	/* Validate datepart (FR-2) */
+	if (datepart == DATEPART_INVALID)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("Invalid datepart: '%s'", datepart_str),
+				 errhint("Valid options: year, quarter, month, week, day")));
+	}
+
+	/* Dispatch to appropriate calculator */
+	switch (datepart)
+	{
+		case DATEPART_DAY:
+			return compute_diff_day(start_y, start_m, start_d,
+									end_y, end_m, end_d);
+		case DATEPART_WEEK:
+			return compute_diff_week(start_y, start_m, start_d,
+									 end_y, end_m, end_d);
+		case DATEPART_MONTH:
+			return compute_diff_month(start_y, start_m, start_d,
+									  end_y, end_m, end_d);
+		case DATEPART_QUARTER:
+			return compute_diff_quarter(start_y, start_m, start_d,
+										end_y, end_m, end_d);
+		case DATEPART_YEAR:
+			return compute_diff_year(start_y, start_m, start_d,
+									 end_y, end_m, end_d);
+		default:
+			/* Should not reach here */
+			ereport(ERROR,
+					(errcode(ERRCODE_INTERNAL_ERROR),
+					 errmsg("Unexpected datepart type")));
+			return (Datum) 0;	/* Keep compiler happy */
+	}
+}
+
+/*-------------------------------------------------------------------------
+ * Public Entry Points
+ *-------------------------------------------------------------------------
+ */
+
+PG_FUNCTION_INFO_V1(datediff_date);
+
+/*
+ * datediff_date - DATE version of datediff
+ */
+Datum
+datediff_date(PG_FUNCTION_ARGS)
+{
+	text	   *datepart_text = PG_GETARG_TEXT_PP(0);
+	DateADT		start_date = PG_GETARG_DATEADT(1);
+	DateADT		end_date = PG_GETARG_DATEADT(2);
+	char	   *datepart_str;
+	int			start_y, start_m, start_d;
+	int			end_y, end_m, end_d;
+
+	datepart_str = text_to_cstring(datepart_text);
+
+	/* Convert dates to year/month/day using j2date */
+	j2date(start_date + POSTGRES_EPOCH_JDATE, &start_y, &start_m, &start_d);
+	j2date(end_date + POSTGRES_EPOCH_JDATE, &end_y, &end_m, &end_d);
+
+	return datediff_internal(datepart_str,
+							 start_y, start_m, start_d,
+							 end_y, end_m, end_d);
+}
+
+PG_FUNCTION_INFO_V1(datediff_timestamp);
+
+/*
+ * datediff_timestamp - TIMESTAMP version of datediff
+ *
+ * Ignores time component, uses only date portion.
+ */
+Datum
+datediff_timestamp(PG_FUNCTION_ARGS)
+{
+	text	   *datepart_text = PG_GETARG_TEXT_PP(0);
+	Timestamp	start_ts = PG_GETARG_TIMESTAMP(1);
+	Timestamp	end_ts = PG_GETARG_TIMESTAMP(2);
+	char	   *datepart_str;
+	struct pg_tm start_tm, end_tm;
+	fsec_t		fsec;
+
+	datepart_str = text_to_cstring(datepart_text);
+
+	/* Decompose timestamps to get date components */
+	if (timestamp2tm(start_ts, NULL, &start_tm, &fsec, NULL, NULL) != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range")));
+
+	if (timestamp2tm(end_ts, NULL, &end_tm, &fsec, NULL, NULL) != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range")));
+
+	return datediff_internal(datepart_str,
+							 start_tm.tm_year, start_tm.tm_mon, start_tm.tm_mday,
+							 end_tm.tm_year, end_tm.tm_mon, end_tm.tm_mday);
+}
+
+PG_FUNCTION_INFO_V1(datediff_timestamptz);
+
+/*
+ * datediff_timestamptz - TIMESTAMPTZ version of datediff
+ *
+ * Converts to local time then uses date portion.
+ */
+Datum
+datediff_timestamptz(PG_FUNCTION_ARGS)
+{
+	text	   *datepart_text = PG_GETARG_TEXT_PP(0);
+	TimestampTz start_tstz = PG_GETARG_TIMESTAMPTZ(1);
+	TimestampTz end_tstz = PG_GETARG_TIMESTAMPTZ(2);
+	char	   *datepart_str;
+	struct pg_tm start_tm, end_tm;
+	fsec_t		fsec;
+	int			tz;
+
+	datepart_str = text_to_cstring(datepart_text);
+
+	/* Decompose timestamps with timezone to get date components */
+	if (timestamp2tm(start_tstz, &tz, &start_tm, &fsec, NULL, NULL) != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range")));
+
+	if (timestamp2tm(end_tstz, &tz, &end_tm, &fsec, NULL, NULL) != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range")));
+
+	return datediff_internal(datepart_str,
+							 start_tm.tm_year, start_tm.tm_mon, start_tm.tm_mday,
+							 end_tm.tm_year, end_tm.tm_mon, end_tm.tm_mday);
+}
+
diff --git a/contrib/mssql_compat/mssql_compat.control b/contrib/mssql_compat/mssql_compat.control
new file mode 100644
index 00000000000..cf669a4eaee
--- /dev/null
+++ b/contrib/mssql_compat/mssql_compat.control
@@ -0,0 +1,7 @@
+# mssql_compat extension
+comment = 'SQL Server compatible datediff function'
+default_version = '1.0'
+module_pathname = '$libdir/mssql_compat'
+relocatable = true
+trusted = true
+
diff --git a/contrib/mssql_compat/results/mssql_compat.out b/contrib/mssql_compat/results/mssql_compat.out
new file mode 100644
index 00000000000..3fd45c1ecc6
--- /dev/null
+++ b/contrib/mssql_compat/results/mssql_compat.out
@@ -0,0 +1,519 @@
+--
+-- Test cases for mssql_compat extension
+-- Covers PRD1a unit tests (UT-01 to UT-15) and edge cases (EC-01 to EC-06)
+--
+CREATE EXTENSION mssql_compat;
+--
+-- Basic Day Calculations (UT-01, UT-02)
+--
+SELECT 'UT-01: Day difference basic' AS test;
+            test             
+-----------------------------
+ UT-01: Day difference basic
+(1 row)
+
+SELECT datediff('day', '2024-01-01'::date, '2024-01-15'::date);
+ datediff 
+----------
+       14
+(1 row)
+
+SELECT 'UT-02: Day difference negative' AS test;
+              test              
+--------------------------------
+ UT-02: Day difference negative
+(1 row)
+
+SELECT datediff('day', '2024-01-15'::date, '2024-01-01'::date);
+ datediff 
+----------
+      -14
+(1 row)
+
+--
+-- Week Calculations (UT-03, UT-04)
+--
+SELECT 'UT-03: Week exact' AS test;
+       test        
+-------------------
+ UT-03: Week exact
+(1 row)
+
+SELECT datediff('week', '2024-01-01'::date, '2024-01-08'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'UT-04: Week partial' AS test;
+        test         
+---------------------
+ UT-04: Week partial
+(1 row)
+
+SELECT datediff('week', '2024-01-01'::date, '2024-01-10'::date);
+ datediff 
+----------
+    1.286
+(1 row)
+
+--
+-- Month Calculations (UT-05, UT-06, UT-07)
+--
+SELECT 'UT-05: Month aligned' AS test;
+         test         
+----------------------
+ UT-05: Month aligned
+(1 row)
+
+SELECT datediff('month', '2024-01-15'::date, '2024-02-15'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'UT-06: Month partial' AS test;
+         test         
+----------------------
+ UT-06: Month partial
+(1 row)
+
+SELECT datediff('month', '2024-01-15'::date, '2024-02-20'::date);
+ datediff 
+----------
+    1.172
+(1 row)
+
+SELECT 'UT-07: Month end-of-month alignment' AS test;
+                test                 
+-------------------------------------
+ UT-07: Month end-of-month alignment
+(1 row)
+
+SELECT datediff('month', '2024-01-31'::date, '2024-02-29'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+--
+-- Quarter Calculations (UT-08, UT-09)
+--
+SELECT 'UT-08: Quarter aligned' AS test;
+          test          
+------------------------
+ UT-08: Quarter aligned
+(1 row)
+
+SELECT datediff('quarter', '2024-01-01'::date, '2024-04-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'UT-09: Quarter partial' AS test;
+          test          
+------------------------
+ UT-09: Quarter partial
+(1 row)
+
+SELECT datediff('quarter', '2024-01-15'::date, '2024-05-20'::date);
+ datediff 
+----------
+    1.385
+(1 row)
+
+--
+-- Year Calculations (UT-10, UT-11)
+--
+SELECT 'UT-10: Year aligned' AS test;
+        test         
+---------------------
+ UT-10: Year aligned
+(1 row)
+
+SELECT datediff('year', '2024-03-15'::date, '2025-03-15'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'UT-11: Year partial leap year' AS test;
+             test              
+-------------------------------
+ UT-11: Year partial leap year
+(1 row)
+
+SELECT datediff('year', '2024-01-01'::date, '2024-07-01'::date);
+ datediff 
+----------
+    0.497
+(1 row)
+
+--
+-- NULL Handling (UT-12, UT-13) - STRICT functions return NULL for NULL inputs
+--
+SELECT 'UT-12: NULL start date' AS test;
+          test          
+------------------------
+ UT-12: NULL start date
+(1 row)
+
+SELECT datediff('day', NULL::date, '2024-01-15'::date);
+ datediff 
+----------
+         
+(1 row)
+
+SELECT 'UT-13: NULL end date' AS test;
+         test         
+----------------------
+ UT-13: NULL end date
+(1 row)
+
+SELECT datediff('day', '2024-01-01'::date, NULL::date);
+ datediff 
+----------
+         
+(1 row)
+
+--
+-- Invalid Datepart (UT-14)
+--
+SELECT 'UT-14: Invalid datepart' AS test;
+          test           
+-------------------------
+ UT-14: Invalid datepart
+(1 row)
+
+SELECT datediff('hour', '2024-01-01'::date, '2024-01-02'::date);
+ERROR:  Invalid datepart: 'hour'
+HINT:  Valid options: year, quarter, month, week, day
+--
+-- Case Insensitivity (UT-15)
+--
+SELECT 'UT-15: Case insensitive datepart' AS test;
+               test               
+----------------------------------
+ UT-15: Case insensitive datepart
+(1 row)
+
+SELECT datediff('MONTH', '2024-01-01'::date, '2024-02-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT datediff('Month', '2024-01-01'::date, '2024-02-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT datediff('month', '2024-01-01'::date, '2024-02-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+--
+-- Edge Cases (EC-01 to EC-06)
+--
+SELECT 'EC-01: Same date' AS test;
+       test       
+------------------
+ EC-01: Same date
+(1 row)
+
+SELECT datediff('day', '2024-01-01'::date, '2024-01-01'::date);
+ datediff 
+----------
+        0
+(1 row)
+
+SELECT 'EC-02: Leap year February 29' AS test;
+             test             
+------------------------------
+ EC-02: Leap year February 29
+(1 row)
+
+SELECT datediff('day', '2024-02-28'::date, '2024-03-01'::date);
+ datediff 
+----------
+        2
+(1 row)
+
+SELECT 'EC-03: Non-leap year February' AS test;
+             test              
+-------------------------------
+ EC-03: Non-leap year February
+(1 row)
+
+SELECT datediff('day', '2023-02-28'::date, '2023-03-01'::date);
+ datediff 
+----------
+        1
+(1 row)
+
+SELECT 'EC-04: Year boundary' AS test;
+         test         
+----------------------
+ EC-04: Year boundary
+(1 row)
+
+SELECT datediff('year', '2024-12-31'::date, '2025-01-01'::date);
+ datediff 
+----------
+    0.003
+(1 row)
+
+SELECT 'EC-05: Multi-year span' AS test;
+          test          
+------------------------
+ EC-05: Multi-year span
+(1 row)
+
+SELECT datediff('year', '2020-01-01'::date, '2025-01-01'::date);
+ datediff 
+----------
+    5.000
+(1 row)
+
+SELECT 'EC-06: Century boundary' AS test;
+          test           
+-------------------------
+ EC-06: Century boundary
+(1 row)
+
+SELECT datediff('day', '1999-12-31'::date, '2000-01-01'::date);
+ datediff 
+----------
+        1
+(1 row)
+
+--
+-- Alias Tests (from PRD1b lines 224-230)
+--
+SELECT 'Alias: yy for year' AS test;
+        test        
+--------------------
+ Alias: yy for year
+(1 row)
+
+SELECT datediff('yy', '2024-01-01'::date, '2025-01-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: yyyy for year' AS test;
+         test         
+----------------------
+ Alias: yyyy for year
+(1 row)
+
+SELECT datediff('yyyy', '2024-01-01'::date, '2025-01-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: mm for month' AS test;
+        test         
+---------------------
+ Alias: mm for month
+(1 row)
+
+SELECT datediff('mm', '2024-01-15'::date, '2024-02-15'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: qq for quarter' AS test;
+         test          
+-----------------------
+ Alias: qq for quarter
+(1 row)
+
+SELECT datediff('qq', '2024-01-01'::date, '2024-04-01'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: wk for week' AS test;
+        test        
+--------------------
+ Alias: wk for week
+(1 row)
+
+SELECT datediff('wk', '2024-01-01'::date, '2024-01-08'::date);
+ datediff 
+----------
+    1.000
+(1 row)
+
+SELECT 'Alias: dd for day' AS test;
+       test        
+-------------------
+ Alias: dd for day
+(1 row)
+
+SELECT datediff('dd', '2024-01-01'::date, '2024-01-15'::date);
+ datediff 
+----------
+       14
+(1 row)
+
+--
+-- Timestamp Tests
+--
+SELECT 'Timestamp: basic day diff' AS test;
+           test            
+---------------------------
+ Timestamp: basic day diff
+(1 row)
+
+SELECT datediff('day', '2024-01-01 10:30:00'::timestamp, '2024-01-15 14:45:00'::timestamp);
+ datediff 
+----------
+       14
+(1 row)
+
+SELECT 'Timestamp: month diff' AS test;
+         test          
+-----------------------
+ Timestamp: month diff
+(1 row)
+
+SELECT datediff('month', '2024-01-15 08:00:00'::timestamp, '2024-02-20 16:00:00'::timestamp);
+ datediff 
+----------
+    1.172
+(1 row)
+
+--
+-- Timestamptz Tests
+--
+SELECT 'Timestamptz: basic day diff' AS test;
+            test             
+-----------------------------
+ Timestamptz: basic day diff
+(1 row)
+
+SELECT datediff('day', '2024-01-01 10:30:00+00'::timestamptz, '2024-01-15 14:45:00+00'::timestamptz);
+ datediff 
+----------
+       14
+(1 row)
+
+--
+-- Additional Month Calculation Tests
+--
+SELECT 'Month: Jan 25 to Mar 10 (PRD walkthrough example)' AS test;
+                       test                        
+---------------------------------------------------
+ Month: Jan 25 to Mar 10 (PRD walkthrough example)
+(1 row)
+
+SELECT datediff('month', '2024-01-25'::date, '2024-03-10'::date);
+ datediff 
+----------
+    1.483
+(1 row)
+
+SELECT 'Month: subscription proration example' AS test;
+                 test                  
+---------------------------------------
+ Month: subscription proration example
+(1 row)
+
+SELECT datediff('month', '2024-01-15'::date, '2024-02-20'::date);
+ datediff 
+----------
+    1.172
+(1 row)
+
+--
+-- Additional Quarter Calculation Tests
+--
+SELECT 'Quarter: PRD walkthrough example' AS test;
+               test               
+----------------------------------
+ Quarter: PRD walkthrough example
+(1 row)
+
+SELECT datediff('quarter', '2024-01-15'::date, '2024-05-20'::date);
+ datediff 
+----------
+    1.385
+(1 row)
+
+--
+-- Additional Year Calculation Tests
+--
+SELECT 'Year: PRD walkthrough example' AS test;
+             test              
+-------------------------------
+ Year: PRD walkthrough example
+(1 row)
+
+SELECT datediff('year', '2024-03-15'::date, '2025-06-20'::date);
+ datediff 
+----------
+    1.266
+(1 row)
+
+SELECT 'Year: exact 5-year tenure' AS test;
+           test            
+---------------------------
+ Year: exact 5-year tenure
+(1 row)
+
+SELECT datediff('year', '2020-03-15'::date, '2025-03-15'::date);
+ datediff 
+----------
+    5.000
+(1 row)
+
+SELECT 'Year: leap year partial (182 days / 366)' AS test;
+                   test                   
+------------------------------------------
+ Year: leap year partial (182 days / 366)
+(1 row)
+
+SELECT datediff('year', '2024-01-01'::date, '2024-07-01'::date);
+ datediff 
+----------
+    0.497
+(1 row)
+
+--
+-- Week Calculation Additional Tests
+--
+SELECT 'Week: exact 2 weeks' AS test;
+        test         
+---------------------
+ Week: exact 2 weeks
+(1 row)
+
+SELECT datediff('week', '2024-01-01'::date, '2024-01-15'::date);
+ datediff 
+----------
+    2.000
+(1 row)
+
+SELECT 'Week: PRD example 9 days' AS test;
+           test           
+--------------------------
+ Week: PRD example 9 days
+(1 row)
+
+SELECT datediff('week', '2024-01-01'::date, '2024-01-10'::date);
+ datediff 
+----------
+    1.286
+(1 row)
+
+DROP EXTENSION mssql_compat;
diff --git a/contrib/mssql_compat/sql/datediff_comprehensive_tests.sql b/contrib/mssql_compat/sql/datediff_comprehensive_tests.sql
new file mode 100644
index 00000000000..a763d958a62
--- /dev/null
+++ b/contrib/mssql_compat/sql/datediff_comprehensive_tests.sql
@@ -0,0 +1,598 @@
+--
+-- Comprehensive DATEDIFF Function Tests
+-- 50+ tests covering all permutations and edge cases
+--
+
+-- Setup: Create extension
+DROP EXTENSION IF EXISTS mssql_compat CASCADE;
+CREATE EXTENSION mssql_compat;
+
+-- ============================================================================
+-- SECTION 1: Test Data Setup
+-- ============================================================================
+
+DROP TABLE IF EXISTS date_test_data;
+CREATE TABLE date_test_data (
+    id SERIAL PRIMARY KEY,
+    description TEXT,
+    start_date DATE,
+    end_date DATE,
+    start_ts TIMESTAMP,
+    end_ts TIMESTAMP,
+    start_tstz TIMESTAMPTZ,
+    end_tstz TIMESTAMPTZ
+);
+
+-- Insert comprehensive test data
+INSERT INTO date_test_data (description, start_date, end_date, start_ts, end_ts, start_tstz, end_tstz) VALUES
+-- Basic date ranges
+('Same day', '2024-06-15', '2024-06-15', '2024-06-15 10:00:00', '2024-06-15 18:00:00', '2024-06-15 10:00:00+00', '2024-06-15 18:00:00+00'),
+('One day apart', '2024-06-15', '2024-06-16', '2024-06-15 00:00:00', '2024-06-16 00:00:00', '2024-06-15 00:00:00+00', '2024-06-16 00:00:00+00'),
+('One week apart', '2024-06-01', '2024-06-08', '2024-06-01 12:00:00', '2024-06-08 12:00:00', '2024-06-01 12:00:00+00', '2024-06-08 12:00:00+00'),
+('One month apart (same day)', '2024-05-15', '2024-06-15', '2024-05-15 08:30:00', '2024-06-15 08:30:00', '2024-05-15 08:30:00+00', '2024-06-15 08:30:00+00'),
+('One quarter apart', '2024-01-01', '2024-04-01', '2024-01-01 00:00:00', '2024-04-01 00:00:00', '2024-01-01 00:00:00+00', '2024-04-01 00:00:00+00'),
+('One year apart', '2023-06-15', '2024-06-15', '2023-06-15 15:45:00', '2024-06-15 15:45:00', '2023-06-15 15:45:00+00', '2024-06-15 15:45:00+00'),
+
+-- Leap year scenarios
+('Leap year Feb 28 to Mar 1', '2024-02-28', '2024-03-01', '2024-02-28 00:00:00', '2024-03-01 00:00:00', '2024-02-28 00:00:00+00', '2024-03-01 00:00:00+00'),
+('Leap year Feb 29 exists', '2024-02-29', '2024-03-01', '2024-02-29 00:00:00', '2024-03-01 00:00:00', '2024-02-29 00:00:00+00', '2024-03-01 00:00:00+00'),
+('Non-leap year Feb 28 to Mar 1', '2023-02-28', '2023-03-01', '2023-02-28 00:00:00', '2023-03-01 00:00:00', '2023-02-28 00:00:00+00', '2023-03-01 00:00:00+00'),
+('Leap year full year', '2024-01-01', '2025-01-01', '2024-01-01 00:00:00', '2025-01-01 00:00:00', '2024-01-01 00:00:00+00', '2025-01-01 00:00:00+00'),
+
+-- End of month scenarios
+('Jan 31 to Feb 28 (non-leap)', '2023-01-31', '2023-02-28', '2023-01-31 00:00:00', '2023-02-28 00:00:00', '2023-01-31 00:00:00+00', '2023-02-28 00:00:00+00'),
+('Jan 31 to Feb 29 (leap)', '2024-01-31', '2024-02-29', '2024-01-31 00:00:00', '2024-02-29 00:00:00', '2024-01-31 00:00:00+00', '2024-02-29 00:00:00+00'),
+('Mar 31 to Apr 30', '2024-03-31', '2024-04-30', '2024-03-31 00:00:00', '2024-04-30 00:00:00', '2024-03-31 00:00:00+00', '2024-04-30 00:00:00+00'),
+('Month end to month end chain', '2024-01-31', '2024-05-31', '2024-01-31 00:00:00', '2024-05-31 00:00:00', '2024-01-31 00:00:00+00', '2024-05-31 00:00:00+00'),
+
+-- Negative spans (start > end)
+('Negative: 2 weeks back', '2024-06-22', '2024-06-08', '2024-06-22 00:00:00', '2024-06-08 00:00:00', '2024-06-22 00:00:00+00', '2024-06-08 00:00:00+00'),
+('Negative: 3 months back', '2024-09-15', '2024-06-15', '2024-09-15 00:00:00', '2024-06-15 00:00:00', '2024-09-15 00:00:00+00', '2024-06-15 00:00:00+00'),
+('Negative: 2 years back', '2026-01-01', '2024-01-01', '2026-01-01 00:00:00', '2024-01-01 00:00:00', '2026-01-01 00:00:00+00', '2024-01-01 00:00:00+00'),
+
+-- Year boundary crossings
+('Cross year boundary', '2024-12-31', '2025-01-01', '2024-12-31 23:59:59', '2025-01-01 00:00:01', '2024-12-31 23:59:59+00', '2025-01-01 00:00:01+00'),
+('Cross multiple years', '2020-06-15', '2024-06-15', '2020-06-15 00:00:00', '2024-06-15 00:00:00', '2020-06-15 00:00:00+00', '2024-06-15 00:00:00+00'),
+('Century boundary', '1999-12-31', '2000-01-01', '1999-12-31 00:00:00', '2000-01-01 00:00:00', '1999-12-31 00:00:00+00', '2000-01-01 00:00:00+00'),
+
+-- Partial periods
+('Partial month mid-month', '2024-01-15', '2024-02-20', '2024-01-15 00:00:00', '2024-02-20 00:00:00', '2024-01-15 00:00:00+00', '2024-02-20 00:00:00+00'),
+('Partial quarter', '2024-01-15', '2024-05-20', '2024-01-15 00:00:00', '2024-05-20 00:00:00', '2024-01-15 00:00:00+00', '2024-05-20 00:00:00+00'),
+('Partial year', '2024-03-15', '2025-06-20', '2024-03-15 00:00:00', '2025-06-20 00:00:00', '2024-03-15 00:00:00+00', '2025-06-20 00:00:00+00'),
+
+-- Employee tenure scenarios
+('Employee 90-day probation', '2024-01-15', '2024-04-14', '2024-01-15 09:00:00', '2024-04-14 17:00:00', '2024-01-15 09:00:00+00', '2024-04-14 17:00:00+00'),
+('Employee 5-year anniversary', '2019-03-01', '2024-03-01', '2019-03-01 00:00:00', '2024-03-01 00:00:00', '2019-03-01 00:00:00+00', '2024-03-01 00:00:00+00'),
+('Employee 10-year tenure', '2014-06-15', '2024-06-15', '2014-06-15 08:00:00', '2024-06-15 08:00:00', '2014-06-15 08:00:00+00', '2024-06-15 08:00:00+00'),
+
+-- Billing/subscription scenarios
+('Monthly subscription', '2024-01-01', '2024-01-31', '2024-01-01 00:00:00', '2024-01-31 23:59:59', '2024-01-01 00:00:00+00', '2024-01-31 23:59:59+00'),
+('Quarterly billing', '2024-01-01', '2024-03-31', '2024-01-01 00:00:00', '2024-03-31 00:00:00', '2024-01-01 00:00:00+00', '2024-03-31 00:00:00+00'),
+('Annual subscription', '2023-07-15', '2024-07-15', '2023-07-15 00:00:00', '2024-07-15 00:00:00', '2023-07-15 00:00:00+00', '2024-07-15 00:00:00+00'),
+('Prorated mid-month cancel', '2024-03-01', '2024-03-18', '2024-03-01 00:00:00', '2024-03-18 00:00:00', '2024-03-01 00:00:00+00', '2024-03-18 00:00:00+00'),
+
+-- Large spans
+('Decade span', '2010-01-01', '2020-01-01', '2010-01-01 00:00:00', '2020-01-01 00:00:00', '2010-01-01 00:00:00+00', '2020-01-01 00:00:00+00'),
+('25 years span', '1999-06-15', '2024-06-15', '1999-06-15 00:00:00', '2024-06-15 00:00:00', '1999-06-15 00:00:00+00', '2024-06-15 00:00:00+00');
+
+-- ============================================================================
+-- SECTION 2: DAY Datepart Tests (Tests 1-8)
+-- ============================================================================
+
+SELECT '=== DAY DATEPART TESTS ===' AS section;
+
+-- Test 1: Basic day difference
+SELECT 'Test 1: Basic day difference' AS test_name,
+       datediff('day', '2024-01-01', '2024-01-15') AS result,
+       14 AS expected,
+       CASE WHEN datediff('day', '2024-01-01', '2024-01-15') = 14 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 2: Day difference using 'dd' alias
+SELECT 'Test 2: Day alias dd' AS test_name,
+       datediff('dd', '2024-01-01', '2024-01-15') AS result,
+       14 AS expected,
+       CASE WHEN datediff('dd', '2024-01-01', '2024-01-15') = 14 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 3: Day difference using 'd' alias
+SELECT 'Test 3: Day alias d' AS test_name,
+       datediff('d', '2024-03-01', '2024-03-31') AS result,
+       30 AS expected,
+       CASE WHEN datediff('d', '2024-03-01', '2024-03-31') = 30 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 4: Day difference using 'days' alias
+SELECT 'Test 4: Day alias days' AS test_name,
+       datediff('days', '2024-06-01', '2024-06-30') AS result,
+       29 AS expected,
+       CASE WHEN datediff('days', '2024-06-01', '2024-06-30') = 29 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 5: Negative day difference
+SELECT 'Test 5: Negative day difference' AS test_name,
+       datediff('day', '2024-01-15', '2024-01-01') AS result,
+       -14 AS expected,
+       CASE WHEN datediff('day', '2024-01-15', '2024-01-01') = -14 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 6: Same day returns 0
+SELECT 'Test 6: Same day returns 0' AS test_name,
+       datediff('day', '2024-06-15', '2024-06-15') AS result,
+       0 AS expected,
+       CASE WHEN datediff('day', '2024-06-15', '2024-06-15') = 0 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 7: Leap year February (28th to Mar 1st)
+SELECT 'Test 7: Leap year Feb 28 to Mar 1' AS test_name,
+       datediff('day', '2024-02-28', '2024-03-01') AS result,
+       2 AS expected,
+       CASE WHEN datediff('day', '2024-02-28', '2024-03-01') = 2 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 8: Non-leap year February
+SELECT 'Test 8: Non-leap year Feb 28 to Mar 1' AS test_name,
+       datediff('day', '2023-02-28', '2023-03-01') AS result,
+       1 AS expected,
+       CASE WHEN datediff('day', '2023-02-28', '2023-03-01') = 1 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- ============================================================================
+-- SECTION 3: WEEK Datepart Tests (Tests 9-15)
+-- ============================================================================
+
+SELECT '=== WEEK DATEPART TESTS ===' AS section;
+
+-- Test 9: Exact 1 week
+SELECT 'Test 9: Exact 1 week' AS test_name,
+       datediff('week', '2024-01-01', '2024-01-08') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('week', '2024-01-01', '2024-01-08') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 10: Exact 2 weeks
+SELECT 'Test 10: Exact 2 weeks' AS test_name,
+       datediff('week', '2024-01-01', '2024-01-15') AS result,
+       2.000 AS expected,
+       CASE WHEN datediff('week', '2024-01-01', '2024-01-15') = 2.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 11: Partial week (9 days = 1.286 weeks)
+SELECT 'Test 11: Partial week 9 days' AS test_name,
+       datediff('week', '2024-01-01', '2024-01-10') AS result,
+       1.286 AS expected,
+       CASE WHEN datediff('week', '2024-01-01', '2024-01-10') = 1.286 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 12: Week alias 'wk'
+SELECT 'Test 12: Week alias wk' AS test_name,
+       datediff('wk', '2024-01-01', '2024-01-08') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('wk', '2024-01-01', '2024-01-08') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 13: Week alias 'ww'
+SELECT 'Test 13: Week alias ww' AS test_name,
+       datediff('ww', '2024-01-01', '2024-01-22') AS result,
+       3.000 AS expected,
+       CASE WHEN datediff('ww', '2024-01-01', '2024-01-22') = 3.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 14: Week alias 'weeks'
+SELECT 'Test 14: Week alias weeks' AS test_name,
+       datediff('weeks', '2024-02-01', '2024-02-29') AS result,
+       4.000 AS expected,
+       CASE WHEN datediff('weeks', '2024-02-01', '2024-02-29') = 4.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 15: Negative weeks
+SELECT 'Test 15: Negative weeks' AS test_name,
+       datediff('week', '2024-01-15', '2024-01-01') AS result,
+       -2.000 AS expected,
+       CASE WHEN datediff('week', '2024-01-15', '2024-01-01') = -2.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- ============================================================================
+-- SECTION 4: MONTH Datepart Tests (Tests 16-25)
+-- ============================================================================
+
+SELECT '=== MONTH DATEPART TESTS ===' AS section;
+
+-- Test 16: Aligned month (same day-of-month)
+SELECT 'Test 16: Aligned month same day' AS test_name,
+       datediff('month', '2024-01-15', '2024-02-15') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('month', '2024-01-15', '2024-02-15') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 17: Partial month
+SELECT 'Test 17: Partial month' AS test_name,
+       datediff('month', '2024-01-15', '2024-02-20') AS result,
+       1.172 AS expected,
+       CASE WHEN datediff('month', '2024-01-15', '2024-02-20') = 1.172 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 18: End-of-month alignment (Jan 31 -> Feb 29)
+SELECT 'Test 18: End-of-month alignment' AS test_name,
+       datediff('month', '2024-01-31', '2024-02-29') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('month', '2024-01-31', '2024-02-29') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 19: Month alias 'mm'
+SELECT 'Test 19: Month alias mm' AS test_name,
+       datediff('mm', '2024-01-01', '2024-02-01') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('mm', '2024-01-01', '2024-02-01') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 20: Month alias 'm'
+SELECT 'Test 20: Month alias m' AS test_name,
+       datediff('m', '2024-03-15', '2024-06-15') AS result,
+       3.000 AS expected,
+       CASE WHEN datediff('m', '2024-03-15', '2024-06-15') = 3.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 21: Month alias 'months'
+SELECT 'Test 21: Month alias months' AS test_name,
+       datediff('months', '2024-01-01', '2024-07-01') AS result,
+       6.000 AS expected,
+       CASE WHEN datediff('months', '2024-01-01', '2024-07-01') = 6.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 22: Multiple months with partial
+SELECT 'Test 22: Multiple months partial' AS test_name,
+       datediff('month', '2024-01-25', '2024-03-10') AS result,
+       1.483 AS expected,
+       CASE WHEN datediff('month', '2024-01-25', '2024-03-10') = 1.483 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 23: Negative months
+SELECT 'Test 23: Negative months' AS test_name,
+       datediff('month', '2024-06-15', '2024-03-15') AS result,
+       -3.000 AS expected,
+       CASE WHEN datediff('month', '2024-06-15', '2024-03-15') = -3.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 24: Month spanning year boundary
+SELECT 'Test 24: Month across year boundary' AS test_name,
+       datediff('month', '2024-11-15', '2025-02-15') AS result,
+       3.000 AS expected,
+       CASE WHEN datediff('month', '2024-11-15', '2025-02-15') = 3.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 25: Less than one month
+SELECT 'Test 25: Less than one month' AS test_name,
+       datediff('month', '2024-01-01', '2024-01-15') AS result,
+       0.452 AS expected,  -- 14 days / 31 days in January
+       CASE WHEN datediff('month', '2024-01-01', '2024-01-15') BETWEEN 0.450 AND 0.460 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- ============================================================================
+-- SECTION 5: QUARTER Datepart Tests (Tests 26-33)
+-- ============================================================================
+
+SELECT '=== QUARTER DATEPART TESTS ===' AS section;
+
+-- Test 26: Exact quarter aligned
+SELECT 'Test 26: Exact quarter aligned' AS test_name,
+       datediff('quarter', '2024-01-01', '2024-04-01') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('quarter', '2024-01-01', '2024-04-01') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 27: Partial quarter
+SELECT 'Test 27: Partial quarter' AS test_name,
+       datediff('quarter', '2024-01-15', '2024-05-20') AS result,
+       1.385 AS expected,
+       CASE WHEN datediff('quarter', '2024-01-15', '2024-05-20') = 1.385 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 28: Quarter alias 'qq'
+SELECT 'Test 28: Quarter alias qq' AS test_name,
+       datediff('qq', '2024-01-01', '2024-07-01') AS result,
+       2.000 AS expected,
+       CASE WHEN datediff('qq', '2024-01-01', '2024-07-01') = 2.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 29: Quarter alias 'q'
+SELECT 'Test 29: Quarter alias q' AS test_name,
+       datediff('q', '2024-01-01', '2024-10-01') AS result,
+       3.000 AS expected,
+       CASE WHEN datediff('q', '2024-01-01', '2024-10-01') = 3.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 30: Quarter alias 'quarters'
+SELECT 'Test 30: Quarter alias quarters' AS test_name,
+       datediff('quarters', '2024-01-01', '2025-01-01') AS result,
+       4.000 AS expected,
+       CASE WHEN datediff('quarters', '2024-01-01', '2025-01-01') = 4.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 31: Negative quarters
+SELECT 'Test 31: Negative quarters' AS test_name,
+       datediff('quarter', '2024-10-01', '2024-04-01') AS result,
+       -2.000 AS expected,
+       CASE WHEN datediff('quarter', '2024-10-01', '2024-04-01') = -2.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 32: Less than one quarter
+SELECT 'Test 32: Less than one quarter' AS test_name,
+       datediff('quarter', '2024-01-01', '2024-02-15') AS result,
+       0.495 AS expected,  -- ~45 days / 91 days
+       CASE WHEN datediff('quarter', '2024-01-01', '2024-02-15') BETWEEN 0.490 AND 0.500 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 33: Quarter across year boundary
+SELECT 'Test 33: Quarter across year boundary' AS test_name,
+       datediff('quarter', '2024-10-01', '2025-04-01') AS result,
+       2.000 AS expected,
+       CASE WHEN datediff('quarter', '2024-10-01', '2025-04-01') = 2.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- ============================================================================
+-- SECTION 6: YEAR Datepart Tests (Tests 34-42)
+-- ============================================================================
+
+SELECT '=== YEAR DATEPART TESTS ===' AS section;
+
+-- Test 34: Exact year aligned
+SELECT 'Test 34: Exact year aligned' AS test_name,
+       datediff('year', '2024-03-15', '2025-03-15') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('year', '2024-03-15', '2025-03-15') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 35: Partial year in leap year
+SELECT 'Test 35: Partial year leap year' AS test_name,
+       datediff('year', '2024-01-01', '2024-07-01') AS result,
+       0.497 AS expected,  -- 182 days / 366
+       CASE WHEN datediff('year', '2024-01-01', '2024-07-01') BETWEEN 0.495 AND 0.500 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 36: Year alias 'yy'
+SELECT 'Test 36: Year alias yy' AS test_name,
+       datediff('yy', '2020-01-01', '2025-01-01') AS result,
+       5.000 AS expected,
+       CASE WHEN datediff('yy', '2020-01-01', '2025-01-01') = 5.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 37: Year alias 'yyyy'
+SELECT 'Test 37: Year alias yyyy' AS test_name,
+       datediff('yyyy', '2024-06-15', '2027-06-15') AS result,
+       3.000 AS expected,
+       CASE WHEN datediff('yyyy', '2024-06-15', '2027-06-15') = 3.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 38: Year alias 'y'
+SELECT 'Test 38: Year alias y' AS test_name,
+       datediff('y', '2024-01-01', '2024-12-31') AS result,
+       0.997 AS expected,  -- 365 days / 366
+       CASE WHEN datediff('y', '2024-01-01', '2024-12-31') BETWEEN 0.995 AND 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 39: Year alias 'years'
+SELECT 'Test 39: Year alias years' AS test_name,
+       datediff('years', '2014-06-15', '2024-06-15') AS result,
+       10.000 AS expected,
+       CASE WHEN datediff('years', '2014-06-15', '2024-06-15') = 10.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 40: Year boundary crossing (1 day)
+SELECT 'Test 40: Year boundary 1 day' AS test_name,
+       datediff('year', '2024-12-31', '2025-01-01') AS result,
+       0.003 AS expected,  -- 1 day / 365
+       CASE WHEN datediff('year', '2024-12-31', '2025-01-01') BETWEEN 0.001 AND 0.005 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 41: Negative years
+SELECT 'Test 41: Negative years' AS test_name,
+       datediff('year', '2025-06-15', '2020-06-15') AS result,
+       -5.000 AS expected,
+       CASE WHEN datediff('year', '2025-06-15', '2020-06-15') = -5.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 42: Feb 29 leap year to Feb 28 non-leap (aligned)
+SELECT 'Test 42: Feb 29 to Feb 28 alignment' AS test_name,
+       datediff('year', '2024-02-29', '2025-02-28') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('year', '2024-02-29', '2025-02-28') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- ============================================================================
+-- SECTION 7: Case Insensitivity Tests (Tests 43-45)
+-- ============================================================================
+
+SELECT '=== CASE INSENSITIVITY TESTS ===' AS section;
+
+-- Test 43: UPPERCASE datepart
+SELECT 'Test 43: UPPERCASE MONTH' AS test_name,
+       datediff('MONTH', '2024-01-01', '2024-02-01') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('MONTH', '2024-01-01', '2024-02-01') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 44: Mixed case datepart
+SELECT 'Test 44: Mixed case Quarter' AS test_name,
+       datediff('QuArTeR', '2024-01-01', '2024-04-01') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('QuArTeR', '2024-01-01', '2024-04-01') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 45: Mixed case alias
+SELECT 'Test 45: Mixed case alias YY' AS test_name,
+       datediff('Yy', '2024-01-01', '2025-01-01') AS result,
+       1.000 AS expected,
+       CASE WHEN datediff('Yy', '2024-01-01', '2025-01-01') = 1.000 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- ============================================================================
+-- SECTION 8: TIMESTAMP and TIMESTAMPTZ Tests (Tests 46-48)
+-- ============================================================================
+
+SELECT '=== TIMESTAMP TESTS ===' AS section;
+
+-- Test 46: Timestamp day difference
+SELECT 'Test 46: Timestamp day diff' AS test_name,
+       datediff('day', '2024-01-01 10:30:00'::timestamp, '2024-01-15 14:45:00'::timestamp) AS result,
+       14 AS expected,
+       CASE WHEN datediff('day', '2024-01-01 10:30:00'::timestamp, '2024-01-15 14:45:00'::timestamp) = 14 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 47: Timestamp month difference
+SELECT 'Test 47: Timestamp month diff' AS test_name,
+       datediff('month', '2024-01-15 08:00:00'::timestamp, '2024-02-20 16:00:00'::timestamp) AS result,
+       1.172 AS expected,
+       CASE WHEN datediff('month', '2024-01-15 08:00:00'::timestamp, '2024-02-20 16:00:00'::timestamp) = 1.172 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- Test 48: Timestamptz day difference
+SELECT 'Test 48: Timestamptz day diff' AS test_name,
+       datediff('day', '2024-01-01 10:30:00+00'::timestamptz, '2024-01-15 14:45:00+00'::timestamptz) AS result,
+       14 AS expected,
+       CASE WHEN datediff('day', '2024-01-01 10:30:00+00'::timestamptz, '2024-01-15 14:45:00+00'::timestamptz) = 14 THEN 'PASS' ELSE 'FAIL' END AS status;
+
+-- ============================================================================
+-- SECTION 9: Error Handling Tests (Tests 49-50)
+-- ============================================================================
+
+SELECT '=== ERROR HANDLING TESTS ===' AS section;
+
+-- Test 49: Invalid datepart should error
+SELECT 'Test 49: Invalid datepart error' AS test_name;
+DO $$
+BEGIN
+    PERFORM datediff('hour', '2024-01-01'::date, '2024-01-02'::date);
+    RAISE NOTICE 'FAIL: No error raised for invalid datepart';
+EXCEPTION WHEN invalid_parameter_value THEN
+    RAISE NOTICE 'PASS: Correctly raised error for invalid datepart';
+END $$;
+
+-- Test 50: NULL handling (should return NULL)
+SELECT 'Test 50: NULL handling' AS test_name,
+       datediff('day', NULL::date, '2024-01-15'::date) IS NULL AS null_start_returns_null,
+       datediff('day', '2024-01-01'::date, NULL::date) IS NULL AS null_end_returns_null,
+       CASE 
+           WHEN datediff('day', NULL::date, '2024-01-15'::date) IS NULL 
+            AND datediff('day', '2024-01-01'::date, NULL::date) IS NULL 
+           THEN 'PASS' 
+           ELSE 'FAIL' 
+       END AS status;
+
+-- ============================================================================
+-- SECTION 10: Table-Based Tests
+-- ============================================================================
+
+SELECT '=== TABLE-BASED TESTS ===' AS section;
+
+-- Test all dateparts against table data
+SELECT 
+    description,
+    datediff('day', start_date, end_date) AS days,
+    datediff('week', start_date, end_date) AS weeks,
+    datediff('month', start_date, end_date) AS months,
+    datediff('quarter', start_date, end_date) AS quarters,
+    datediff('year', start_date, end_date) AS years
+FROM date_test_data
+ORDER BY id;
+
+-- Test with timestamp columns
+SELECT 
+    description,
+    datediff('day', start_ts, end_ts) AS ts_days,
+    datediff('month', start_ts, end_ts) AS ts_months
+FROM date_test_data
+WHERE start_ts IS NOT NULL
+ORDER BY id
+LIMIT 10;
+
+-- Test with timestamptz columns  
+SELECT 
+    description,
+    datediff('day', start_tstz, end_tstz) AS tstz_days,
+    datediff('year', start_tstz, end_tstz) AS tstz_years
+FROM date_test_data
+WHERE start_tstz IS NOT NULL
+ORDER BY id
+LIMIT 10;
+
+-- ============================================================================
+-- SECTION 11: Aggregation and Analytics Tests
+-- ============================================================================
+
+SELECT '=== AGGREGATION TESTS ===' AS section;
+
+-- Average tenure calculations
+SELECT 
+    'Average differences across test data' AS metric,
+    ROUND(AVG(datediff('day', start_date, end_date)), 2) AS avg_days,
+    ROUND(AVG(datediff('month', start_date, end_date)), 2) AS avg_months,
+    ROUND(AVG(datediff('year', start_date, end_date)), 2) AS avg_years
+FROM date_test_data
+WHERE start_date <= end_date;
+
+-- Group by ranges
+WITH date_diffs AS (
+    SELECT 
+        id,
+        description,
+        datediff('day', start_date, end_date) AS day_diff
+    FROM date_test_data
+    WHERE start_date <= end_date
+)
+SELECT 
+    CASE 
+        WHEN day_diff < 7 THEN 'Less than 1 week'
+        WHEN day_diff < 30 THEN '1 week to 1 month'
+        WHEN day_diff < 90 THEN '1 to 3 months'
+        WHEN day_diff < 365 THEN '3 months to 1 year'
+        ELSE 'Over 1 year'
+    END AS duration_bucket,
+    COUNT(*) AS count
+FROM date_diffs
+GROUP BY 1
+ORDER BY MIN(day_diff);
+
+-- ============================================================================
+-- SECTION 12: Real-World Scenario Tests
+-- ============================================================================
+
+SELECT '=== REAL-WORLD SCENARIO TESTS ===' AS section;
+
+-- Invoice aging report simulation
+WITH invoices AS (
+    SELECT 
+        generate_series(1, 10) AS invoice_id,
+        '2024-01-01'::date + (random() * 180)::int AS due_date
+)
+SELECT 
+    invoice_id,
+    due_date,
+    CURRENT_DATE AS today,
+    datediff('day', due_date, CURRENT_DATE) AS days_overdue,
+    CASE
+        WHEN datediff('day', due_date, CURRENT_DATE) > 90 THEN 'Critical'
+        WHEN datediff('day', due_date, CURRENT_DATE) > 60 THEN 'Warning'
+        WHEN datediff('day', due_date, CURRENT_DATE) > 30 THEN 'Attention'
+        WHEN datediff('day', due_date, CURRENT_DATE) > 0 THEN 'Overdue'
+        ELSE 'Current'
+    END AS aging_bucket
+FROM invoices
+ORDER BY days_overdue DESC;
+
+-- Subscription proration simulation
+WITH subscriptions AS (
+    SELECT 
+        1 AS sub_id, '2024-01-15'::date AS start_date, '2024-02-20'::date AS cancel_date, 29.99 AS monthly_rate
+    UNION ALL SELECT 
+        2, '2024-03-01', '2024-03-18', 49.99
+    UNION ALL SELECT 
+        3, '2024-06-15', '2024-09-15', 99.99
+)
+SELECT 
+    sub_id,
+    start_date,
+    cancel_date,
+    monthly_rate,
+    datediff('month', start_date, cancel_date) AS months_used,
+    ROUND((datediff('month', start_date, cancel_date) * monthly_rate)::numeric, 2) AS prorated_charge
+FROM subscriptions;
+
+-- Employee tenure report
+WITH employees AS (
+    SELECT 'Alice' AS name, '2019-03-15'::date AS hire_date
+    UNION ALL SELECT 'Bob', '2021-06-01'
+    UNION ALL SELECT 'Carol', '2023-01-10'
+    UNION ALL SELECT 'David', '2024-06-15'
+)
+SELECT 
+    name,
+    hire_date,
+    datediff('year', hire_date, CURRENT_DATE) AS years_tenure,
+    datediff('month', hire_date, CURRENT_DATE) AS months_tenure,
+    datediff('day', hire_date, CURRENT_DATE) AS days_tenure,
+    CASE 
+        WHEN datediff('year', hire_date, CURRENT_DATE) >= 5 THEN 'Senior'
+        WHEN datediff('year', hire_date, CURRENT_DATE) >= 2 THEN 'Mid-level'
+        WHEN datediff('year', hire_date, CURRENT_DATE) >= 1 THEN 'Junior'
+        ELSE 'Probation'
+    END AS tenure_level
+FROM employees
+ORDER BY hire_date;
+
+-- ============================================================================
+-- SUMMARY: Test Results
+-- ============================================================================
+
+SELECT '=== TEST SUMMARY ===' AS section;
+
+-- Count passing tests from table data
+SELECT 
+    'Table data validation' AS category,
+    COUNT(*) FILTER (WHERE datediff('day', start_date, end_date) IS NOT NULL) AS day_tests,
+    COUNT(*) FILTER (WHERE datediff('week', start_date, end_date) IS NOT NULL) AS week_tests,
+    COUNT(*) FILTER (WHERE datediff('month', start_date, end_date) IS NOT NULL) AS month_tests,
+    COUNT(*) FILTER (WHERE datediff('quarter', start_date, end_date) IS NOT NULL) AS quarter_tests,
+    COUNT(*) FILTER (WHERE datediff('year', start_date, end_date) IS NOT NULL) AS year_tests
+FROM date_test_data;
+
+-- Cleanup
+DROP TABLE IF EXISTS date_test_data;
+-- Note: Keeping extension for further manual testing
+-- DROP EXTENSION IF EXISTS mssql_compat;
+
+SELECT 'All comprehensive tests completed!' AS final_status;
+
diff --git a/contrib/mssql_compat/sql/mssql_compat.sql b/contrib/mssql_compat/sql/mssql_compat.sql
new file mode 100644
index 00000000000..f625868b3f7
--- /dev/null
+++ b/contrib/mssql_compat/sql/mssql_compat.sql
@@ -0,0 +1,173 @@
+--
+-- Test cases for mssql_compat extension
+-- Covers PRD1a unit tests (UT-01 to UT-15) and edge cases (EC-01 to EC-06)
+--
+
+CREATE EXTENSION mssql_compat;
+
+--
+-- Basic Day Calculations (UT-01, UT-02)
+--
+SELECT 'UT-01: Day difference basic' AS test;
+SELECT datediff('day', '2024-01-01'::date, '2024-01-15'::date);
+
+SELECT 'UT-02: Day difference negative' AS test;
+SELECT datediff('day', '2024-01-15'::date, '2024-01-01'::date);
+
+--
+-- Week Calculations (UT-03, UT-04)
+--
+SELECT 'UT-03: Week exact' AS test;
+SELECT datediff('week', '2024-01-01'::date, '2024-01-08'::date);
+
+SELECT 'UT-04: Week partial' AS test;
+SELECT datediff('week', '2024-01-01'::date, '2024-01-10'::date);
+
+--
+-- Month Calculations (UT-05, UT-06, UT-07)
+--
+SELECT 'UT-05: Month aligned' AS test;
+SELECT datediff('month', '2024-01-15'::date, '2024-02-15'::date);
+
+SELECT 'UT-06: Month partial' AS test;
+SELECT datediff('month', '2024-01-15'::date, '2024-02-20'::date);
+
+SELECT 'UT-07: Month end-of-month alignment' AS test;
+SELECT datediff('month', '2024-01-31'::date, '2024-02-29'::date);
+
+--
+-- Quarter Calculations (UT-08, UT-09)
+--
+SELECT 'UT-08: Quarter aligned' AS test;
+SELECT datediff('quarter', '2024-01-01'::date, '2024-04-01'::date);
+
+SELECT 'UT-09: Quarter partial' AS test;
+SELECT datediff('quarter', '2024-01-15'::date, '2024-05-20'::date);
+
+--
+-- Year Calculations (UT-10, UT-11)
+--
+SELECT 'UT-10: Year aligned' AS test;
+SELECT datediff('year', '2024-03-15'::date, '2025-03-15'::date);
+
+SELECT 'UT-11: Year partial leap year' AS test;
+SELECT datediff('year', '2024-01-01'::date, '2024-07-01'::date);
+
+--
+-- NULL Handling (UT-12, UT-13) - STRICT functions return NULL for NULL inputs
+--
+SELECT 'UT-12: NULL start date' AS test;
+SELECT datediff('day', NULL::date, '2024-01-15'::date);
+
+SELECT 'UT-13: NULL end date' AS test;
+SELECT datediff('day', '2024-01-01'::date, NULL::date);
+
+--
+-- Invalid Datepart (UT-14)
+--
+SELECT 'UT-14: Invalid datepart' AS test;
+SELECT datediff('hour', '2024-01-01'::date, '2024-01-02'::date);
+
+--
+-- Case Insensitivity (UT-15)
+--
+SELECT 'UT-15: Case insensitive datepart' AS test;
+SELECT datediff('MONTH', '2024-01-01'::date, '2024-02-01'::date);
+SELECT datediff('Month', '2024-01-01'::date, '2024-02-01'::date);
+SELECT datediff('month', '2024-01-01'::date, '2024-02-01'::date);
+
+--
+-- Edge Cases (EC-01 to EC-06)
+--
+SELECT 'EC-01: Same date' AS test;
+SELECT datediff('day', '2024-01-01'::date, '2024-01-01'::date);
+
+SELECT 'EC-02: Leap year February 29' AS test;
+SELECT datediff('day', '2024-02-28'::date, '2024-03-01'::date);
+
+SELECT 'EC-03: Non-leap year February' AS test;
+SELECT datediff('day', '2023-02-28'::date, '2023-03-01'::date);
+
+SELECT 'EC-04: Year boundary' AS test;
+SELECT datediff('year', '2024-12-31'::date, '2025-01-01'::date);
+
+SELECT 'EC-05: Multi-year span' AS test;
+SELECT datediff('year', '2020-01-01'::date, '2025-01-01'::date);
+
+SELECT 'EC-06: Century boundary' AS test;
+SELECT datediff('day', '1999-12-31'::date, '2000-01-01'::date);
+
+--
+-- Alias Tests (from PRD1b lines 224-230)
+--
+SELECT 'Alias: yy for year' AS test;
+SELECT datediff('yy', '2024-01-01'::date, '2025-01-01'::date);
+
+SELECT 'Alias: yyyy for year' AS test;
+SELECT datediff('yyyy', '2024-01-01'::date, '2025-01-01'::date);
+
+SELECT 'Alias: mm for month' AS test;
+SELECT datediff('mm', '2024-01-15'::date, '2024-02-15'::date);
+
+SELECT 'Alias: qq for quarter' AS test;
+SELECT datediff('qq', '2024-01-01'::date, '2024-04-01'::date);
+
+SELECT 'Alias: wk for week' AS test;
+SELECT datediff('wk', '2024-01-01'::date, '2024-01-08'::date);
+
+SELECT 'Alias: dd for day' AS test;
+SELECT datediff('dd', '2024-01-01'::date, '2024-01-15'::date);
+
+--
+-- Timestamp Tests
+--
+SELECT 'Timestamp: basic day diff' AS test;
+SELECT datediff('day', '2024-01-01 10:30:00'::timestamp, '2024-01-15 14:45:00'::timestamp);
+
+SELECT 'Timestamp: month diff' AS test;
+SELECT datediff('month', '2024-01-15 08:00:00'::timestamp, '2024-02-20 16:00:00'::timestamp);
+
+--
+-- Timestamptz Tests
+--
+SELECT 'Timestamptz: basic day diff' AS test;
+SELECT datediff('day', '2024-01-01 10:30:00+00'::timestamptz, '2024-01-15 14:45:00+00'::timestamptz);
+
+--
+-- Additional Month Calculation Tests
+--
+SELECT 'Month: Jan 25 to Mar 10 (PRD walkthrough example)' AS test;
+SELECT datediff('month', '2024-01-25'::date, '2024-03-10'::date);
+
+SELECT 'Month: subscription proration example' AS test;
+SELECT datediff('month', '2024-01-15'::date, '2024-02-20'::date);
+
+--
+-- Additional Quarter Calculation Tests
+--
+SELECT 'Quarter: PRD walkthrough example' AS test;
+SELECT datediff('quarter', '2024-01-15'::date, '2024-05-20'::date);
+
+--
+-- Additional Year Calculation Tests
+--
+SELECT 'Year: PRD walkthrough example' AS test;
+SELECT datediff('year', '2024-03-15'::date, '2025-06-20'::date);
+
+SELECT 'Year: exact 5-year tenure' AS test;
+SELECT datediff('year', '2020-03-15'::date, '2025-03-15'::date);
+
+SELECT 'Year: leap year partial (182 days / 366)' AS test;
+SELECT datediff('year', '2024-01-01'::date, '2024-07-01'::date);
+
+--
+-- Week Calculation Additional Tests
+--
+SELECT 'Week: exact 2 weeks' AS test;
+SELECT datediff('week', '2024-01-01'::date, '2024-01-15'::date);
+
+SELECT 'Week: PRD example 9 days' AS test;
+SELECT datediff('week', '2024-01-01'::date, '2024-01-10'::date);
+
+DROP EXTENSION mssql_compat;
+
-- 
2.52.0

