@@ -498,6 +498,7 @@ SQLRETURN BindParameters(SQLHANDLE hStmt, const py::list& params,
498498 dtoPtr->hour = static_cast <SQLUSMALLINT>(param.attr (" hour" ).cast <int >());
499499 dtoPtr->minute = static_cast <SQLUSMALLINT>(param.attr (" minute" ).cast <int >());
500500 dtoPtr->second = static_cast <SQLUSMALLINT>(param.attr (" second" ).cast <int >());
501+ // SQL server supports in ns, but python datetime supports in µs
501502 dtoPtr->fraction = static_cast <SQLUINTEGER>(param.attr (" microsecond" ).cast <int >() * 1000 );
502503
503504 py::object utcoffset = tzinfo.attr (" utcoffset" )(param);
@@ -1878,7 +1879,6 @@ SQLRETURN BindParameterArray(SQLHANDLE hStmt,
18781879 break ;
18791880 }
18801881 case SQL_C_TYPE_TIMESTAMP: {
1881- std::cout<<" Binding Timestamp param at index " <<paramIndex<<std::endl;
18821882 SQL_TIMESTAMP_STRUCT* tsArray = AllocateParamBufferArray<SQL_TIMESTAMP_STRUCT>(tempBuffers, paramSetSize);
18831883 strLenOrIndArray = AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
18841884 for (size_t i = 0 ; i < paramSetSize; ++i) {
@@ -1902,7 +1902,6 @@ SQLRETURN BindParameterArray(SQLHANDLE hStmt,
19021902 break ;
19031903 }
19041904 case SQL_C_SS_TIMESTAMPOFFSET: {
1905- std::cout<<" Binding DateTimeOffset param at index " <<paramIndex<<std::endl;
19061905 DateTimeOffset* dtoArray = AllocateParamBufferArray<DateTimeOffset>(tempBuffers, paramSetSize);
19071906 strLenOrIndArray = AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
19081907
@@ -1925,39 +1924,26 @@ SQLRETURN BindParameterArray(SQLHANDLE hStmt,
19251924 std::to_string (paramIndex));
19261925 }
19271926
1928- // Convert the Python datetime object to UTC before binding.
1929- // This is the crucial step to ensure timezone normalization.
1930- py::object datetimeModule = py::module_::import (" datetime" );
1931- py::object utc_dt = param.attr (" astimezone" )(datetimeModule.attr (" timezone" ).attr (" utc" ));
1932- std::cout<<" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" <<std::endl;
1933- // --- TEMPORARY DEBUGGING: LOG THE UTC VALUES ---
1934- LOG (" Binding UTC values: {}-{}-{} {}:{}:{}.{} +00:00" ,
1935- utc_dt.attr (" year" ).cast <int >(),
1936- utc_dt.attr (" month" ).cast <int >(),
1937- utc_dt.attr (" day" ).cast <int >(),
1938- utc_dt.attr (" hour" ).cast <int >(),
1939- utc_dt.attr (" minute" ).cast <int >(),
1940- utc_dt.attr (" second" ).cast <int >(),
1941- utc_dt.attr (" microsecond" ).cast <int >()
1942- );
1943-
1944- // Now, populate the C++ struct using the UTC-converted object.
1945- dtoArray[i].year = static_cast <SQLSMALLINT>(utc_dt.attr (" year" ).cast <int >());
1946- dtoArray[i].month = static_cast <SQLUSMALLINT>(utc_dt.attr (" month" ).cast <int >());
1947- dtoArray[i].day = static_cast <SQLUSMALLINT>(utc_dt.attr (" day" ).cast <int >());
1948- dtoArray[i].hour = static_cast <SQLUSMALLINT>(utc_dt.attr (" hour" ).cast <int >());
1949- dtoArray[i].minute = static_cast <SQLUSMALLINT>(utc_dt.attr (" minute" ).cast <int >());
1950- dtoArray[i].second = static_cast <SQLUSMALLINT>(utc_dt.attr (" second" ).cast <int >());
1951- dtoArray[i].fraction = static_cast <SQLUINTEGER>(utc_dt.attr (" microsecond" ).cast <int >() * 1000 );
1952-
1953- // Since we've converted to UTC, the timezone offset is always 0.
1954- dtoArray[i].timezone_hour = 0 ;
1955- dtoArray[i].timezone_minute = 0 ;
1927+ // Populate the C++ struct directly from the Python datetime object.
1928+ dtoArray[i].year = static_cast <SQLSMALLINT>(param.attr (" year" ).cast <int >());
1929+ dtoArray[i].month = static_cast <SQLUSMALLINT>(param.attr (" month" ).cast <int >());
1930+ dtoArray[i].day = static_cast <SQLUSMALLINT>(param.attr (" day" ).cast <int >());
1931+ dtoArray[i].hour = static_cast <SQLUSMALLINT>(param.attr (" hour" ).cast <int >());
1932+ dtoArray[i].minute = static_cast <SQLUSMALLINT>(param.attr (" minute" ).cast <int >());
1933+ dtoArray[i].second = static_cast <SQLUSMALLINT>(param.attr (" second" ).cast <int >());
1934+ // SQL server supports in ns, but python datetime supports in µs
1935+ dtoArray[i].fraction = static_cast <SQLUINTEGER>(param.attr (" microsecond" ).cast <int >() * 1000 );
1936+
1937+ // Compute and preserve the original UTC offset.
1938+ py::object utcoffset = tzinfo.attr (" utcoffset" )(param);
1939+ int total_seconds = static_cast <int >(utcoffset.attr (" total_seconds" )().cast <double >());
1940+ std::div_t div_result = std::div (total_seconds, 3600 );
1941+ dtoArray[i].timezone_hour = static_cast <SQLSMALLINT>(div_result.quot );
1942+ dtoArray[i].timezone_minute = static_cast <SQLSMALLINT>(div (div_result.rem , 60 ).quot );
19561943
19571944 strLenOrIndArray[i] = sizeof (DateTimeOffset);
19581945 }
19591946 }
1960-
19611947 dataPtr = dtoArray;
19621948 bufferLength = sizeof (DateTimeOffset);
19631949 break ;
0 commit comments