framework: Bad datetime format string and datetime data type for SQL Server on non US platforms
Laravel version
Laravel Version: 5.7.21
PHP Driver version or file name
php_pdo_sqlsrv_72_ts_x64.dll 5.3.0.11108 php_sqlsrv_72_ts_x64.dll 5.3.0.11108
SQL Server version
SQL Server 2012 Express x64 11.0.6020.0
Client operating system
Windows 7 Pro x64 FR
PHP version
PHP x64 7.2.12
Microsoft ODBC Driver version
ODBC Driver 13 for SQL Server x64 2015.131.4414.46
Same problem with latest ODBC Driver 17 version
Table schema
CREATE TABLE [dbo].[users]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [nvarchar](255) NOT NULL,
[email] [nvarchar](255) NOT NULL,
[email_verified_at] [datetime] NULL,
[password] [nvarchar](255) NOT NULL,
[remember_token] [nvarchar](100) NULL,
[created_at] [datetime] NULL,
[updated_at] [datetime] NULL,
PRIMARY KEY CLUSTERED ([id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Description and steps to reproduce
Using a fresh Laravel 5.7 project installation with Auth module, registration of new user try executing a parameterized SQL statement which throw an PDOException coming from PDO driver. DateTime parameters are sources of the problem. They are given as object and it seems generated string are not ISO compliant so it fails on french operating system.
Parameterized statement :
insert into [users] ([name], [email], [password], [updated_at], [created_at]) values (?, ?, ?, ?, ?)
Executed statement :
insert into [users] ([name], [email], [password], [updated_at], [created_at]) values ('Benjamin GILLET', 'benjamin.gillet@domain.com', 'hashhashhashhashhashhash', '2019-01-17 11:54:10.233', '2019-01-17 11:54:10.233')
Thrown exception :
SQLSTATE[22007]: [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]La conversion d'un type de données nvarchar en type de données datetime a créé une valeur hors limites.
Expected behavior
DateTime format in SQL Server is a bit tricky. Default type for DateTime fields is datetime
in SQL Server but you should now that this data type is neither ANSI nor ISO8601 compliant.
In Query\SqlServerGrammar.php
class in getDateFormat()
method, datetime string format used by default is Y-m-d H:i:s.v
but this format is not one of the format recognized by datetime
SQL Server field type (see https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetime-transact-sql?view=sql-server-2017
for more details). This should be Ymd H:i:s.v
or Y-m-d\TH:i:s.v
.
Moreover, formatted datetime is formatted according to the timezone configured in config/app.php
file. So, as timezone is not specified in current datetime format string, value recorded in database may not the good one (according to the operating system timezone which may be different than the one configured in config/app.php
).
Anyway, for SQL Server datetime
datatype, is none of the expected one.
I suggest many fixes :
- Using
datetime2
data type instead ofdatetime
with'c'
PHP ISO8061 datetime format string. - Adding parameters to specify SQL engine’s datetime data type and datetime format string.
- Keeping current implementation but changing current datetime format string to
'Ymd H:i:s.v'
or'Y-m-d\TH:i:s.v'
.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 4
- Comments: 15 (4 by maintainers)
@j4kim : Yes, you have the same problem. You don’t always get an error because when month and day are inverted, it may still be a valid date (ex :
2019-03-06
and2019-06-03
are still valid but2019-12-31
and2019-31-12
are not and crash). The fix is quite simple : changingdatetime
data type todatetime2
and using'Y-m-d\TH:i:s'
as date format to make it ISO8601 compliant. But I have not seen any fix yet implemented. You may also choose working withdatetimeoffset
data type instead of standarddatetime
data type. I believe the grammar is well defined to handle this data type.The real bug is the US way of storing date
trailing data means the date in the database is more precise than the format you have set in the model
I’m not sure to understand your question. The issue is data type used for dates in SQL Server is neither
ANSI
norISO8601
compliant as well as the way you write date format string. So, on non US systems, if you useYmd H:i:s.v
date format withdatetime
data type, the date string you give to the server will be translated against your system local. So, if you have a date after day 12 (ex: 2019-04-29), on NON US CULTURE based system (ex: FRENCH), your query will fail because SQL Server will translate the date as (Y=2019, m=29, d=4) instead of (Y=2019, m=4, d=29). To force SQL Server (and I think other SGBD) translating date string correctly, you should use the ISO8601 date format stringY-m-d\TH:i:s.v
. This is the first fix you should apply to all your date string because this bypass your system culture and today I have not seen any fix commited for this. Problem for SQL Server is that thedatetime
datetype is neitherANSI
norISO8601
compliant so SQL Server will fail translating the date string even if written this way. To make it working, you must use at leastdatetime2
data type for your date columns. This type isANSI
andISO8601
compliant. This way, you are sure having a culture independent system. There is another datatype even better thandatetime2
which isdatetimeoffset
. But this one is to use when you want to support timezone as well. But this is not the purpose of this issue.