{"id":301,"date":"2010-01-31T00:00:30","date_gmt":"2010-01-31T00:00:30","guid":{"rendered":"http:\/\/www.sqlserver.fr\/blog\/?p=301"},"modified":"2026-05-02T14:31:28","modified_gmt":"2026-05-02T12:31:28","slug":"datetime-et-datetime2","status":"publish","type":"post","link":"https:\/\/www.sqlserver.fr\/blog\/datetime-et-datetime2\/","title":{"rendered":"Datetime et Datetime2"},"content":{"rendered":"<p>&nbsp;<\/p>\n<p>La version 2008 de SQL Server introduit de nouveaux types de donn\u00e9es li\u00e9s aux dates et aux heures. Apr\u00e8s une br\u00e8ve pr\u00e9sentation de ces nouveaut\u00e9s, je mentionnerai quelques cas dans lesquels leur utilisation \u00e0 la place de l\u2019ancien type datetime n\u00e9cessitera quelques pr\u00e9cautions\u2026<!--more--><br \/>\nTout d\u2019abord, une br\u00e8ve pr\u00e9sentation. En compl\u00e9ment du type datetime pr\u00e9sent dans SQL Server dans les versions pr\u00e9c\u00e9dentes et qui est toujours disponible, la version 2008 apporte 4 nouveaux types : time, date, datetime2 et datetimeoffset.<br \/>\nVoici un bref r\u00e9sum\u00e9 des caract\u00e9ristiques de chacun de ces types :<\/p>\n<table border=\"0\">\n<tbody>\n<tr>\n<td>Type<\/td>\n<td>Plage<\/td>\n<td>Pr\u00e9cision<\/td>\n<td>Taille de stockage<\/td>\n<\/tr>\n<tr>\n<td>datetime<\/td>\n<td>01\/01\/1753 \u2013 31\/12\/9999<\/td>\n<td>10\/3 millisecondes<\/td>\n<td>\u00a08 octets<\/td>\n<\/tr>\n<tr>\n<td>date<\/td>\n<td>01\/01\/0001 \u2013 31\/12\/999<\/td>\n<td>1 jour<\/td>\n<td>3 octets<\/td>\n<\/tr>\n<tr>\n<td>time(n)<\/td>\n<td>00:00:00:00.0000000 \u2013 23:59:59:9999999<\/td>\n<td>10<sup>-n<\/sup> secondes<\/td>\n<td>0&lt;=n&lt;=2 : 3 octets<br \/>\n3&lt;=n&lt;=4 : 4 octets<br \/>\n5&lt;=n&lt;=7 : 5 octets<\/td>\n<\/tr>\n<tr>\n<td>datetime2(n)<\/td>\n<td>Plage du type date + plage du time(n)<\/td>\n<td>10<sup>-n<\/sup> secondes<\/td>\n<td>0&lt;=n&lt;=2 : 6 octets<br \/>\n3&lt;=n&lt;=4 : 7 octets<br \/>\n5&lt;=n&lt;=7 : 8 octets<\/td>\n<\/tr>\n<tr>\n<td>datetimeoffset(n)<\/td>\n<td>Plage du type date + plage du time(n)<br \/>\nPlage de d\u00e9calage de fuseau horaire : 14:00 \u00e0 14:00<\/td>\n<td>10<sup>-n<\/sup>\u00a0secondes<\/td>\n<td>0&lt;=n&lt;=2 : 8 octets<br \/>\n3&lt;=n&lt;=4 : 9 octets<br \/>\n5&lt;=n&lt;=7 : 10 octets<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Je ne vais pas chercher \u00e0 r\u00e9\u00e9crire ici l\u2019aide en ligne, d\u2019autres l\u2019ont fait mieux que moi\u2026 : <a href=\"https:\/\/docs.microsoft.com\/fr-fr\/sql\/t-sql\/functions\/date-and-time-data-types-and-functions-transact-sql\">http:\/\/msdn.microsoft.com\/fr-fr\/library\/ms186724.aspx<\/a><br \/>\nVoici quelques exemples dans lesquels ces nouveaux types de donn\u00e9es peuvent \u00eatre int\u00e9ressants :<br \/>\n&#8211; Stockage de dates (de naissance, d\u2019\u00e9v\u00e9nements particuliers, \u2026) sans s\u2019embarrasser d\u2019octets superflus pour stocker une heure inutile. En rempla\u00e7ant le type datetime par le type date, on \u00e9conomise plus de 60% d\u2019espace disque ! De m\u00eame, lorsque l\u2019on cherche \u00e0 modifier des heures (par exemple dans la gestion de l\u2019emploi d\u2019un emploi du temps scolaire), on peut l\u00e0 encore \u00e9conomiser plus de 60% d\u2019espace disque\u2026<br \/>\n&#8211; Stockage de dates d\u2019\u00e9v\u00e9nements historiques : aussi curieux que cela paraisse, et sans \u00e9voquer les types de donn\u00e9es utilisateurs r\u00e9alisables en .NET sous SQL Server 2005 ou l\u2019utilisation d\u2019un pseudo offset, il n\u2019\u00e9tait pas possible avant SQL Server 2008 de r\u00e9aliser une table d\u2019\u00e9v\u00e9nements historiques (avec une colonne de date) couvrant des \u00e9v\u00e9nements majeurs tels que la c\u00e9l\u00e8bre bataille de Marignan (1515 pour ceux et celles pour qui les cours d\u2019histoire sont un peu trop lointains\u2026). D\u00e9sormais, le nouveau type de donn\u00e9es date (et les types plus complets datetime2 et datetimeoffset) permettent nativement de m\u00e9moriser de telles dates.<\/p>\n<p>&nbsp;<\/p>\n<p>Par contre, voici quelques points de vigilance pour ceux qui souhaitent faire basculer certaines des colonnes de leurs tables du type datetime aux types date ou datetime2.<br \/>\nConstruisons par exemple une fonction f_Lendemain prenant en entr\u00e9e un param\u00e8tre de type datetime et renvoyant le lendemain de cette date.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">USE [tempdb]\r\nGO\r\nIF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[f_Lendemain]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))\r\nDROP FUNCTION [dbo].[f_Lendemain]\r\nGO\r\ncreate function [dbo].[f_Lendemain] (@Jour datetime)\r\nreturns datetime\r\nbegin\r\nreturn (@Jour+1)\r\nend\r\nGO<\/pre>\n<p>Je sais, ce n\u2019est pas super propre comme fa\u00e7on de coder, mais bon, \u00e7a marche\u2026<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">select dbo.f_Lendemain('20100101') as Reponse<\/pre>\n<p><span style=\"font-family: 'courier new', courier;\">Reponse<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">2010-01-02 00:00:00.000<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>Par contre, la limite de date se fait bien s\u00fbr ressentir\u2026<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">select dbo.f_Lendemain('15150101') as Reponse<\/pre>\n<p><span style=\"font-family: 'courier new', courier;\">Reponse<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">Msg 242, Level 16, State 3, Line 1<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>D\u00e9sormais, nous avons \u00e0 notre disposition de nouveaux types de donn\u00e9es, alors profitons-en !<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">USE [tempdb]\r\nGO\r\nIF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[f_Lendemain2]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))\r\nDROP FUNCTION [dbo].[f_Lendemain2]\r\nGO\r\ncreate function [dbo].[f_Lendemain2] (@Jour date)\r\nreturns date\r\nbegin\r\nreturn (@Jour+1)\r\nend\r\nGO<\/pre>\n<p><span style=\"font-family: 'courier new', courier;\">Msg 206, Level 16, State 2, Procedure f_Lendemain, Line 4<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">Operand type clash: date is incompatible with int<\/span><\/p>\n<p>Tiens, c\u2019est nouveau \u00e7a ? Et bien oui, d\u00e9sormais, fini le code pas propre, une date n\u2019est pas \u00e0 consid\u00e9rer comme un nombre r\u00e9el comme c\u2019\u00e9tait implicitement le cas avant (nombre de jours avant la virgule, et fraction de journ\u00e9e pour la suite). Une date, c\u2019est une date, sans que l\u2019on ait \u00e0 s\u2019occuper de la mani\u00e8re dont le syst\u00e8me la stocke en interne. Et pour ajouter une journ\u00e9e, codons proprement, avec les fonctions d\u00e9di\u00e9es !<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">USE [tempdb]\r\nGO\r\nIF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[f_Lendemain2]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))\r\nDROP FUNCTION [dbo].[f_Lendemain2]\r\nGO\r\ncreate function [dbo].[f_Lendemain2] (@Jour date)\r\nreturns date\r\nbegin\r\nreturn DATEADD(day,1,@Jour)\r\nend\r\nGO<\/pre>\n<p>L\u00e0, enfin, \u00e7a compile. Et surtout, \u00e7a fonctionne.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">select dbo.f_Lendemain2('15150101') as Reponse<\/pre>\n<p><span style=\"font-family: 'courier new', courier;\">Reponse<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">&#8212;&#8212;&#8212;-<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">1515-01-02<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>Mais en grattant un peu plus, on s\u2019apper\u00e7oit que tout n\u2019est pas si rose, et que quelques cas particuliers montrent des diff\u00e9rences de comportement&#8230;<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">set dateformat ydm\r\nselect dbo.f_Lendemain('2010-02-03') as [Version datetime],\r\n\tdbo.f_Lendemain2('2010-02-03') as [Version datetime2]<\/pre>\n<p><span style=\"font-family: 'courier new', courier;\">Version datetime Version datetime2<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/span><br \/>\n<span style=\"font-family: 'courier new', courier;\">2010-03-03 00:00:00.000 2010-02-04<\/span><\/p>\n<p>Ce format de date est assez peut usit\u00e9, mais il faut effectivement ne pas oublier de lire compl\u00e8tement l\u2019aide en ligne pour ne pas avoir de mauvaises surprises, et il est clairement indiqu\u00e9 ici que Le param\u00e8tre DATEFORMAT ydm n&rsquo;est pas pris en charge pour les types de donn\u00e9es date, datetime2 et datetimeoffset.<\/p>\n<p>Pour plus de d\u00e9tails, l\u2019aide en ligne pr\u00e9sente notamment les formats conseill\u00e9s pour la repr\u00e9sentation sous forme de cha\u00eene de caract\u00e8res des dates (<a href=\"https:\/\/docs.microsoft.com\/fr-fr\/previous-versions\/sql\/sql-server-2008-r2\/ms180878(v=sql.105)#StringLiteralDateandTimeFormats\">http:\/\/msdn.microsoft.com\/fr-fr\/library\/ms180878.aspx#StringLiteralDateandTimeFormats<\/a>).<\/p>\n<p>&nbsp;<\/p>\n<p>Voici donc pour une premi\u00e8re approche montrant notamment qu\u2019il convient de ne pas chercher \u00e0 se jeter de suite sur les nouveaut\u00e9s sans pr\u00e9cautions et aussi qu\u2019il vaut toujours mieux \u00e9viter les petits passe-droits de codage tol\u00e9r\u00e9s \u00e0 un instant t&#8230;<\/p>\n<p><span style=\"font-family: Calibri; font-size: xx-small;\"><br \/>\n<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; La version 2008 de SQL Server introduit de nouveaux types de donn\u00e9es li\u00e9s aux dates et aux heures. Apr\u00e8s une br\u00e8ve pr\u00e9sentation de ces nouveaut\u00e9s, je mentionnerai quelques cas dans lesquels leur utilisation \u00e0 la place de l\u2019ancien type &hellip; <a href=\"https:\/\/www.sqlserver.fr\/blog\/datetime-et-datetime2\/\">Continuer la lecture <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-301","post","type-post","status-publish","format-standard","hentry","category-article_sql"],"_links":{"self":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/301","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/comments?post=301"}],"version-history":[{"count":5,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/301\/revisions"}],"predecessor-version":[{"id":1974,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/posts\/301\/revisions\/1974"}],"wp:attachment":[{"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/media?parent=301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/categories?post=301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlserver.fr\/blog\/wp-json\/wp\/v2\/tags?post=301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}