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 of datetime 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)

Most upvoted comments

@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 and 2019-06-03 are still valid but 2019-12-31 and 2019-31-12 are not and crash). The fix is quite simple : changing datetime data type to datetime2 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 with datetimeoffset data type instead of standard datetime 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 nor ISO8601 compliant as well as the way you write date format string. So, on non US systems, if you use Ymd H:i:s.v date format with datetime 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 string Y-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 the datetime datetype is neither ANSI nor ISO8601 compliant so SQL Server will fail translating the date string even if written this way. To make it working, you must use at least datetime2 data type for your date columns. This type is ANSI and ISO8601 compliant. This way, you are sure having a culture independent system. There is another datatype even better than datetime2 which is datetimeoffset. But this one is to use when you want to support timezone as well. But this is not the purpose of this issue.