@@ -7829,12 +7829,7 @@ def test_datetimeoffset_read_write(cursor, db_connection):
78297829 assert row is not None
78307830 fetched_id , fetched_dt = row
78317831 assert fetched_dt .tzinfo is not None
7832- expected_utc = dt .astimezone (timezone .utc )
7833- fetched_utc = fetched_dt .astimezone (timezone .utc )
7834- # Ignore sub-microsecond differences
7835- expected_utc = expected_utc .replace (microsecond = int (expected_utc .microsecond / 1000 ) * 1000 )
7836- fetched_utc = fetched_utc .replace (microsecond = int (fetched_utc .microsecond / 1000 ) * 1000 )
7837- assert fetched_utc == expected_utc
7832+ assert fetched_dt == dt
78387833 finally :
78397834 cursor .execute ("DROP TABLE IF EXISTS #pytest_datetimeoffset_read_write;" )
78407835 db_connection .commit ()
@@ -7868,12 +7863,7 @@ def test_datetimeoffset_max_min_offsets(cursor, db_connection):
78687863 assert fetched_id == expected_id , f"ID mismatch: expected { expected_id } , got { fetched_id } "
78697864 assert fetched_dt .tzinfo is not None , f"Fetched datetime object is naive for id { fetched_id } "
78707865
7871- # Compare in UTC to avoid offset differences
7872- expected_utc = expected_dt .astimezone (timezone .utc ).replace (tzinfo = None )
7873- fetched_utc = fetched_dt .astimezone (timezone .utc ).replace (tzinfo = None )
7874- assert fetched_utc == expected_utc , (
7875- f"Value mismatch for id { expected_id } : expected UTC { expected_utc } , got { fetched_utc } "
7876- )
7866+ assert fetched_dt == expected_dt , f"Value mismatch for id { expected_id } : expected { expected_dt } , got { fetched_dt } "
78777867
78787868 finally :
78797869 cursor .execute ("DROP TABLE IF EXISTS #pytest_datetimeoffset_read_write;" )
@@ -7928,12 +7918,7 @@ def test_datetimeoffset_dst_transitions(cursor, db_connection):
79287918 assert fetched_id == expected_id , f"ID mismatch: expected { expected_id } , got { fetched_id } "
79297919 assert fetched_dt .tzinfo is not None , f"Fetched datetime object is naive for id { fetched_id } "
79307920
7931- # Compare UTC time to avoid issues due to offsets changing in DST
7932- expected_utc = expected_dt .astimezone (timezone .utc ).replace (tzinfo = None )
7933- fetched_utc = fetched_dt .astimezone (timezone .utc ).replace (tzinfo = None )
7934- assert fetched_utc == expected_utc , (
7935- f"Value mismatch for id { expected_id } : expected UTC { expected_utc } , got { fetched_utc } "
7936- )
7921+ assert fetched_dt == expected_dt , f"Value mismatch for id { expected_id } : expected { expected_dt } , got { fetched_dt } "
79377922
79387923 finally :
79397924 cursor .execute ("DROP TABLE IF EXISTS #pytest_datetimeoffset_dst_transitions;" )
@@ -8010,17 +7995,7 @@ def test_datetimeoffset_executemany(cursor, db_connection):
80107995 fetched_id , fetched_dto = rows [i ]
80117996 assert fetched_dto .tzinfo is not None , "Fetched datetime object is naive."
80127997
8013- expected_utc = python_dt .astimezone (timezone .utc ).replace (tzinfo = None )
8014- fetched_utc = fetched_dto .astimezone (timezone .utc ).replace (tzinfo = None )
8015-
8016- # Round microseconds to nearest millisecond for comparison
8017- expected_utc = expected_utc .replace (microsecond = int (expected_utc .microsecond / 1000 ) * 1000 )
8018- fetched_utc = fetched_utc .replace (microsecond = int (fetched_utc .microsecond / 1000 ) * 1000 )
8019-
8020- assert fetched_utc == expected_utc , (
8021- f"Value mismatch for test case { i } . "
8022- f"Expected UTC: { expected_utc } , Got UTC: { fetched_utc } "
8023- )
7998+ assert fetched_dto == python_dt , f"Value mismatch for id { fetched_id } : expected { python_dt } , got { fetched_dto } "
80247999 finally :
80258000 cursor .execute ("IF OBJECT_ID('tempdb..#pytest_dto', 'U') IS NOT NULL DROP TABLE #pytest_dto;" )
80268001 db_connection .commit ()
@@ -8086,13 +8061,44 @@ def test_datetimeoffset_extreme_offsets(cursor, db_connection):
80868061 for i , dt in enumerate (extreme_offsets ):
80878062 _ , fetched = rows [i ]
80888063 assert fetched .tzinfo is not None
8089- # Round-trip comparison via UTC
8090- expected_utc = dt .astimezone (timezone .utc ).replace (tzinfo = None )
8091- fetched_utc = fetched .astimezone (timezone .utc ).replace (tzinfo = None )
8092- assert expected_utc == fetched_utc , f"Extreme offset round-trip failed for { dt .tzinfo } "
8064+ assert fetched == dt , f"Value mismatch for id { i } : expected { dt } , got { fetched } "
80938065 finally :
80948066 cursor .execute ("IF OBJECT_ID('tempdb..#pytest_dto', 'U') IS NOT NULL DROP TABLE #pytest_dto;" )
80958067 db_connection .commit ()
8068+
8069+ def test_datetimeoffset_native_vs_string_simple (cursor , db_connection ):
8070+ """
8071+ Replicates the user's testing scenario: fetch DATETIMEOFFSET as native datetime
8072+ and as string using CONVERT(nvarchar(35), ..., 121).
8073+ """
8074+ try :
8075+ cursor .execute ("CREATE TABLE #pytest_dto_user_test (id INT PRIMARY KEY, Systime DATETIMEOFFSET);" )
8076+ db_connection .commit ()
8077+
8078+ # Insert rows similar to user's example
8079+ test_rows = [
8080+ (1 , datetime (2025 , 5 , 14 , 12 , 35 , 52 , 501000 , tzinfo = timezone (timedelta (hours = 1 )))),
8081+ (2 , datetime (2025 , 5 , 14 , 15 , 20 , 30 , 123000 , tzinfo = timezone (timedelta (hours = - 5 ))))
8082+ ]
8083+
8084+ for i , dt in test_rows :
8085+ cursor .execute ("INSERT INTO #pytest_dto_user_test (id, Systime) VALUES (?, ?);" , i , dt )
8086+ db_connection .commit ()
8087+
8088+ # Native fetch (like the user's first execute)
8089+ cursor .execute ("SELECT Systime FROM #pytest_dto_user_test WHERE id=1;" )
8090+ dt_native = cursor .fetchone ()[0 ]
8091+ assert dt_native .tzinfo is not None
8092+ assert dt_native == test_rows [0 ][1 ]
8093+
8094+ # String fetch (like the user's convert to nvarchar)
8095+ cursor .execute ("SELECT CONVERT(nvarchar(35), Systime, 121) FROM #pytest_dto_user_test WHERE id=1;" )
8096+ dt_str = cursor .fetchone ()[0 ]
8097+ assert dt_str .endswith ("+01:00" ) # original offset preserved
8098+
8099+ finally :
8100+ cursor .execute ("DROP TABLE IF EXISTS #pytest_dto_user_test;" )
8101+ db_connection .commit ()
80968102
80978103def test_lowercase_attribute (cursor , db_connection ):
80988104 """Test that the lowercase attribute properly converts column names to lowercase"""
0 commit comments