Jeff Puckett
Jeff Puckett

Reputation: 41091

How to bind ISO8601 TSQL DATETIME parameter with PDO?

It seems that PDO has a problem with ISO 8601 formatted timestamps.

I'm connecting from 64-bit Ubuntu 16.04 running PHP 7.0.8 using the Microsoft® ODBC Driver 13 (Preview) for SQL Server®

Here's my simple table:

CREATE TABLE dtest (
    "stamp" DATETIME
);

Works:

$pdoDB = new PDO('odbc:Driver=ODBC Driver 13 for SQL Server;
  Server='.DATABASE_SERVER.';
  Database='.DATABASE_NAME,
  DATABASE_USERNAME,
  DATABASE_PASSWORD
);
$pdoDB->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

$sql = "INSERT INTO dtest (stamp) VALUES ('2011-03-15T10:23:01')";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);

Does not work:

$sql = "INSERT INTO dtest (stamp) VALUES (?)";
$stmt = $pdoDB->prepare($sql);
$params = ['2011-03-15T10:23:01'];
$stmt->execute($params);

Fatal error: Uncaught PDOException: SQLSTATE[22018]: Invalid character value for cast specification: 0 [Microsoft][ODBC Driver 13 for SQL Server]Invalid character value for cast specification (SQLExecute[0] at /build/php7.0-lPMnpS/php7.0-7.0.8/ext/pdo_odbc/odbc_stmt.c:260)

This works if I delete the T so '2011-03-15T10:23:01' becomes '2011-03-15 10:23:01'

$sql = "INSERT INTO dtest (stamp) VALUES (?)";
$stmt = $pdoDB->prepare($sql);
$params = ['2011-03-15 10:23:01'];
$stmt->execute($params);

But I'm writing a script that runs nightly on about 2 million records, so I'd really rather not bear the overhead of running millions of str_replace('T', ' ', $param)

I've also tried using bindParam, but it gives the same error:

$sql  = "INSERT INTO dtest (stamp) VALUES (:tdate)";
$stmt = $pdoDB->prepare($sql);
$date = '2011-03-15T10:23:01';
$stmt->bindParam(':tdate',$date,PDO::PARAM_STR);
$stmt->execute();

Is there anyway to bind and execute this parameter as is? I'm a little dubious of the error message because it appears to be coming from SQL Server as if PDO did its job fine, but that doesn't make sense since it's able to handle the type conversion without parameterization.


I've also tried SQL conversion:

Works:

$sql = "INSERT INTO dtest (stamp) VALUES (CONVERT(DATETIME, '2011-03-15T10:23:02', 126))";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);

Does not Work:

$sql = "INSERT INTO dtest (stamp) VALUES (CONVERT(DATETIME, ?, 126))";
$stmt = $pdoDB->prepare($sql);
$params = ['2011-03-15T10:23:02'];
$stmt->execute($params);

Upvotes: 5

Views: 1558

Answers (2)

Massimiliano Arione
Massimiliano Arione

Reputation: 2476

After half a day spent trying to resolve the same issue, I ended up dropping odbc and using dblib instead. I installed php7.0-sybase package, adapted the data source name of my PDO connection and resolved once for all. Now every bind is working.

Upvotes: 1

MonkeyZeus
MonkeyZeus

Reputation: 20757

You will need to use SQL Server's built-in convert() function and specify the format (126) which you are giving it:

$sql = "INSERT INTO dtest (stamp) VALUES (CONVERT(DATETIME, '2011-03-15T10:23:01', 126))";

The documentation mentions :mmm at the end of your string so you might need to manually add :000 at the end of your date string for this to work.

Upvotes: 1

Related Questions