Pourquoi se bâdrer du temps Universel quant on est attaché à un lieu bien précis, mon village en l’occurrence? Principalement pour travailler en XML ou tout autre format d’exportation tant conventionnel qu’extensible. Atom, RSS, vCard, vCalendar/iCalendar... Non seulement une date locale peut être incompatible avec la plupart de ses formats, mais une date qui indique l’offset UTC (ou décalage horaire) au bout n’est pas davantage accepté. L’exemple suivant n’est pas autorisé avec le format iCalendar :
// Invalide avec décalage horaire DTSTART:19980119T230000-0800 RFC 2445 (iCalendar) // Valide avec l’heure UTC DTSTART:19980119T150000Z Le Z indique une date UTC
UTC est une échelle de temps comprise entre le « Temps atomique international » ou TAI qui est stable mais déconnecté de la rotation de la Terre et le Temps universel (TU), directement lié à la rotation de la Terre et donc lentement variable. Le terme « coordonné » indique que le Temps Universel Coordonné est en fait identique au temps atomique international dont il a la stabilité et l’exactitude à un nombre entier de secondes près, ce qui lui permet de coller au temps universel à moins de 0,9 s près.
Coordinated Universal Time a été abrégé en UTC, au lieu de CUT correspondant à l’acronyme en anglais ou de TUC correspondant à l’acronyme en français. En effet, si les experts de l’Union internationale des télécommunications étaient d’accord pour définir une abréviation commune à toutes les langues, ils étaient divisés sur le choix de la langue. Finalement, c’est le compromis UTC, nécessitant un effort des deux parties, qui fut choisi. C’est cette notation qui est utilisée par la norme ISO 8601. (Source wikipedia.org)
À ne pas confondre avec le temps moyen de Greenwich (GMT), l’heure solaire moyenne au méridien de Greenwich. Utilisé dès 1847 cette fois par les marins britanniques pour calculer leur longitude par rapport au méridien de Greenwich. Officialisé quelques années plus tard comme étant le temps officiel pour être légalement adopté dès 1880 par la Grande-Bretagne. Le temps moyen de Greenwich (GMT) imprécis a été remplacé en 1972 par l’UTC.
Pourquoi faire simple quand on peut faire compliquer!
Los Angeles, États-Unis (UTC-8) : 04:00 Chicago, États-Unis (UTC-6) : 06:00 Montréal, Québec (UTC-5) : 07:00 Halifax, Canada (UTC-4) : 08:00 Londres, Royaume-Uni (UTC+0) : 12:00 Paris, France (UTC+1) : 13:00 Le Cap, Afrique du Sud (UTC+2) : 14:00 Mysore, Inde (UTC+5:30) : 17:30 Katmandou, Népal (UTC+5:45) : 17:45 Séoul, Corée du Sud (UTC+9) : 21:00 Melbourne, Australie (UTC+10) : 22:00
À l’heure de la mondialisation, voilà une invention digne de mention. S’il a fallu attendre le 19e siècle, la mondialisation elle ne date pas d’hier. À en croire les cananéens, inventeurs de l’alphabet, via les businessmans de l’époque à savoir les marins Phéniciens. Et encore, ce n’est pas parfait. Quoi de plus facile que de diviser la terre en fuseaux horaires une fois que l’on connaît sa circonférence (enfin visiblement plus facile que de définir un mètre anglais!). UTC+1 à Paris ou UTC-5 à Montréal, il suffit d’une simple addition.
Le temps c’est l’espace ! Voilà que chaque région met son grain de sel dans cet engrenage pourtant logique. On a une demi-heure par-ci, 15 minutes par là! L’heure d’hiver ou normale et l’heure d’été ou avancée et surtout des centaines de dates et d’heures pour effectuer le changement selon les caprices régionaux ou politique. Hey, on a des lumières aujourd’hui! Et même l’électricité! Enfin, on assiste donc à une escalade d’exception qu’il faut comptabiliser dans une table de références. Résultat, les langages de programmation offrent bien entendu l’« offset », c’est-à-dire la différence selon votre fuseau horaire, mais sans les ajustements géographiques communément appelé le « décalage horaire ». Trop lourd sans doute. Et d’ailleurs une petite parenthèse sur l’énergie déployée et gaspillée quotidiennement pour convertir votre heure n’est certes pas pour aider à consommer raisonnablement!
Le Québec respecte la nouvelle norme nord-américaine en matière de changement d’heure. Ainsi, le passage à l’heure d’hiver se fait le premier dimanche du mois de novembre à 1 h 00. En Europe, le passage à l’heure d’hiver intervient le dernier dimanche d’octobre à 3 heures du matin. En 1918 aux États-Unis, c’était le dernier dimanche d’octobre à 2 h 00 du matin mais précisément le 9 février en 1942, guerre oblige! Donc pour compiler toutes ces exceptions, Arthur David Olson a créé la « Olson database » dite la « tz database » ou la IANA Time Zone Database ou encore zoneinfo database. Dont voici un exemple des plus éloquents (source Wikipédia) :
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule US 1918 1919 - Mar lastSun 2:00 1:00 D Rule US 1918 1919 - Oct lastSun 2:00 0 S Rule US 1942 only - Feb 9 2:00 1:00 W # War Rule US 1945 only - Aug 14 23:00u 1:00 P # Peace Rule US 1945 only - Sep 30 2:00 0 S Rule US 1967 2006 - Oct lastSun 2:00 0 S Rule US 1967 1973 - Apr lastSun 2:00 1:00 D Rule US 1974 only - Jan 6 2:00 1:00 D Rule US 1975 only - Feb 23 2:00 1:00 D Rule US 1976 1986 - Apr lastSun 2:00 1:00 D Rule US 1987 2006 - Apr Sun>=1 2:00 1:00 D Rule US 2007 max - Mar Sun>=8 2:00 1:00 D Rule US 2007 max - Nov Sun>=1 2:00 0 S
Le support est donc, à l’instar de l’encodage UTF, un peu beaucoup chaotique. Certains ne s’en formalise même pas, ignorant tout simplement les ajustements. Personnellement j’ai ajouté un banal -5:00 au bout de la date pendant des années. D’autres font carrément semblant allant même jusqu’à afficher une date erronée! C’est justement ce qui m’a incité à écrire ce tutoriel. Après avoir consulté un agenda d’un compétiteur. On peut même exporter l’événement en format vCalandar, génial ! Mais voilà, on parle d’un format international et qui dit format international dit aussi Temps Universel Coordonné ou UTC. Non seulement la date ne tient pas compte de l’heure avancée d’été, mais en plus ils font carrément fi du décalage horaire (offset)! Après investigation, il s’avère que leur script ne se préoccupe pas la date réelle se contentant d’insérer bêtement la date locale soit EDT (heure de l’Est) sans même l’heure de l’événement.
Solution Script pour nos marins modernes
C’est pourtant très simple. Il ne suffit certes pas de changer de code LCID pour obtenir une conversion de date à volonté. Session.LCID
permet seulement d’adapter le format d’affichage à la manière locale. Si la plupart des langages serveur n’offrent pas la possibilité de récupérer la date UTC, les langages script le peuvent, en se connectant directement sur une horloge atomique! Et donc tant le VBScript, le Javascript que le JScript permet de récupérer l’heure UTC. Bien sûr un langage script est interprété par la machine de l’internaute mais on peut aussi l’exécuter directement sur le serveur en précisant runat="server"
ou encore en indiquant au compilateur d’utiliser son langage script préféré. Voilà un exemple qui sauve la valeur dans une variable session pour la récupérer ailleurs :
// Fichier twDateUTC.asp <%@language="jscript"%> <% var dDate = new Date(); var nAnnee = dDate.getUTCFullYear().toString(); var nMois = String("0" + (dDate.getUTCMonth()+1).toString()).slice(-2); var nDate = String("0" + dDate.getUTCDate().toString()).slice(-2); var nHeure = String("0" + dDate.getUTCHours().toString()).slice(-2); var nMinutes = String("0" + dDate.getUTCMinutes().toString()).slice(-2); var nSecondes = String("0" + dDate.getUTCSeconds().toString()).slice(-2); Session("DateUTC") = nAnnee+"-"+nMois+"-"+nDate+" "+nHeure+":"+nMinutes+":"+nSecondes; %> // Puis exécuter le fichier à partir d’une simple page ASP <% Server.Execute "/commun/twDateUTC.asp" response.write "Date UTD : " & Session("DateUTC") %>
Personnellement j’utilise carrément « l’offset » qu’il suffit d’additionner à la date locale. Noter que ces exemples fonctionnent seulement avec la date et l’heure courante.
// Fichier twOffsetUTC.asp <%@language="jscript"%> <% var dDate = new Date(); Session("OffsetUTC") = dDate.getTimezoneOffset().toString(); %> <% function twDateUTC(dDate) Server.Execute "twOffsetUTC.asp" twDateUTC = DateAdd("n",Session("OffsetUTC"),dDate) end function %> <%=now()%> EDT<br /> <%=twDateUTC(now())%> UTC<br />
GMT Déprécié
Attention, UTC implique que l’heure GMT n’est plus utilisée outre qu’en nomenclature. Plusieurs exemples sur le Web utilisent le vieux date.toGMTString();
à éviter au profit du .getTimezoneOffset()
.
Exemple dynamique
Exemple avec date dynamique. Dans ce cas il faut passer en paramètre la date dynamique. Puisque ce n’est pas possible avec la méthode Server.Execute
sans utiliser une variable session, voilà un nouvel exemple, encore plus simple :
// Fichier exempleUTC.asp <script language="Javascript" runat="server"> function twUTCOffset(dDate) { var d = new Date(dDate); return d.getTimezoneOffset().toString(); } </script> <% dim dAujourdhui : dAujourdhui = "2015/03/25 13:34:00" Response.Write "<br>UTC (Aujourd’hui) : " & DateAdd("n",twUTCOffset(dAujourdhui),dAujourdhui) dim dNormale : dNormale = "2015/01/01 16:54:00" Response.Write "<br>UTC (Heure normale) : " & DateAdd("n",twUTCOffset(dNormale),dNormale) dim dEte : dEte = "2015/06/06 16:54:00" Response.Write "<br>UTC (Heure d’été) : " & DateAdd("n",twUTCOffset(dEte),dEte) %>
Exemple SQL Server
Bien que je n’aime pas cette technique le SQL Server permet aussi de récupérer non seulement l’offset mais carrément ajusté avec TZoffset
. Par contre la forme +01:00 ou -05:00 doit être ajusté pour l’addition...
Dim oConn, oRS, sSQL, sUTCAjustement Set oConn = DBOuvreConnection(strSQLconn) Set oRS = Server.CreateObject("ADODB.RecordSet") sSQL = "SELECT DATENAME (TZoffset, SYSDATETIMEOFFSET())" oRS.Open sSQL,oConn sUTCAjustement = oRS(0) oRS.close set oRS = nothing oConn.close set oConn = nothing // Ajustement sUTCAjustement = replace(sUTCAjustement,":00","") sUTCAjustement = -cInt(sUTCAjustement) dim dAujourdhui : dAujourdhui = "2015/03/25 13:34:00" Response.Write "<br>UTC (Aujourd’hui) : " & DateAdd("h",sUTCAjustement,dAujourdhui)
Le DOS Shell
Enfin, une dernière méthode consiste à récupérer l’offset directement du registre de la machine :
dDate = now() set oShell = CreateObject("WScript.Shell") sCle = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias" nOffset = oShell.RegRead(sCle) dDateUTC = dateadd("n", nOffset, dDate) Response.Write "UTC = " & dDateUTC
Petit truc pour différencier l’heure avancée ou heure d’été (DST ou Daylight Savings Time) avec l’heure normale (pour le Québec)
<script runat="server"> function twUTCOffset(dDate) { var d = new Date(dDate); return d.getTimezoneOffset().toString(); } </script> <% dim nHeureEte : nHeureEte = 4 // Heure d’été du Québec dim dEst : dEst = now() dim dUTC : dUTC = DateAdd("n",twUTCOffset(dEst),dEst) dim sDateEst : sDateEst = FormatDateTime(dEst,4) if DateDiff("h",dEst,dUTC) = nHeureEte then response.write "<br />Temps HAE : " & sDateEst & " Heure avancée de l'est (UTC -4)" else response.write "<br />Temps HNE " & sDateEst & " Heure normal de l'est (UTC -5)" end if Response.Write "<br />Temps UTC : " & FormatDateTime(dUTC,4) & " Greenwich Mean Time (GMT) zone (UTC +0)" %>
Dans ce dernier exemple, il faut bien entendu ajuster ce code selon les conventions de votre fuseau horaire.
Horloge atomique du temps atomique international » ou TAI
Une autre méthode pour déterminer si le « DST » ou l’heure d’été est en vigueur est de faire une requête à un serveur synchronisé avec une horloge atomique. Par exemple le serveur du NIST (National Institute of Standards and Technology) time.nist.gov. Ce serveur retournera plusieurs informations pertinentes dont le DST (le TT) :
Format retourné par le serveur :
JJJJJ AN-MO-JO HH:MM:SS TT L H msADV UST(NIST) OTM
Example: 58013 17-09-17 20:58:48 50 0 0 142.2 UTC(NIST) *
JJJJJ est la Date Julienne Modifiée (MJD). Contrairement à la date julienne (1er janvier 4713 av. J.-C.), les 5 chiffres de la DJM comptent le nombre de jours à partir du 17 novembre 1858. La DJM s’obtient en soustrayant exactement 2 400 000.5 jours de la date julienne.
AN-MO-JO est la date. C’est-à-dire les deux derniers chiffres de l’année, le mois et le jour du mois courant.
HH:MM:SS est l’heure, les minutes et les secondes. Le temps est toujours envoyé en temps universel coordonné (UTC). Vous devez l’ajuster pour obtenir l’heure locale. Par exemple, l’heure de l’Est à Montréal (Eastern Time Zone) a 5 heures de moins que l’heure UTC et 4 heures de moins avec l’heure avancée d’été.
TT est ce qui nous intéresse. C’est un code à deux chiffres (00 à 99) qui indique (en Amérique du Nord) l’heure normale dite temps standard(ST) ou l’heure avancée dite heure d’été (DST). Il indique également quand ST ou DST approche. Ce code est défini par 00 lorsque l’heure normale est en vigueur ou sur 50 lorsque l’heure avancée d’été est en vigueur. Pendant le mois où le changement d’heure se produit, ce nombre diminue chaque jour jusqu’à ce que le changement se produise. Par exemple, pendant le mois de novembre, lors du passage de l’heure d’été à l’heure normale.
Ex :
00 - Heure normal
50 - Heure avancé d’été
1 à 49 - Jours du mois courant avant l’heure avancé d’étéL est un code à un chiffre qui indique si une seconde d’ajustement sera ajoutée ou soustraite à minuit au dernier jour du mois courant. Si le code est 0, aucun ajustement ne se produira ce mois-ci. Si le code est 1, une seconde sera ajoutée à la fin du mois. Cela signifie que la dernière minute du mois contiendra 61 secondes au lieu de 60. Si le code est 2, une seconde sera supprimée le dernier jour du mois. Les périodes d’ajustement se produisent à raison d’environ une par année. Ils sont utilisés pour corriger l’irrégularité dans la rotation de la Terre. La correction est effectuée juste avant minuit, heure UTC (pas l’heure locale).
H est un chiffre qui indique l’état du serveur. Si H = 0, le serveur fonctionne parfaitement. Si H = 1, le serveur fonctionne correctement, mais son temps peut être faussé jusqu’à 5 secondes. Cet état devrait être complètement corrigé dans les 10 minutes suivantes. Si H = 2, le serveur fonctionne correctement, mais son temps est connu pour être incorrect de plus de 5 secondes. Si H = 3, une défaillance matérielle ou logicielle s’est produite et la quantité d’erreurs temporelles est inconnue. Si H = 4, défaillance matériel / logiciel - l’erreur de temps est inconnue.
msADV affiche le nombre de millisecondes que le serveur ajoute pour compenser partiellement les délais du réseau. L’avance est généralement réglée à 50,0 millisecondes.
L’étiquette UTC (NIST) est contenue dans chaque réponse. Elle indique que vous recevez un temps universel coordonnée (UTC) du National Institute of Standards and Technology (NIST).
OTM (marqueur temporel) est un astérisque (*). Les valeurs de temps envoyées par la réponse se réfèrent sur l’heure d’arrivée de l’OTM. En d’autres termes, l’heure d’arrivée de l’OTM reçu du serveur doit être précise.
- pool.ntp.org NTP Pool Address
- time-a.nist.gov NIST, Gaithersburg, Maryland
- time-b.nist.gov NIST, Gaithersburg, Maryland
- time-a.timefreq bldrdoc.gov NIST, Boulder, Colorado
- time-b.timefreq bldrdoc.gov NIST, Boulder, Colorado
- Autres serveurs NIST
Autres serveurs :
<% ' Merci à Lewis Moten Const sURLserveur = "http://time.nist.gov:13" ' Décalage horaire de l’heure normale Const nDecalageHoraire = -5 ' Réglez sur true ou false si vous observez l’heure d'été Const bDST = True ' Requête au serveur NIST Set hRequete = Server.CreateObject ("Microsoft.XMLHTTP") hRequete.Open "GET", sURLserveur, False, "", "" hRequete.Send hReponse = hRequete.ResponseText Set hRequete = Nothing ' Ajustements de la date dDateUTC = Mid(hReponse, 11, 2) & "/" & Mid(hReponse, 14, 2) & "/" & Mid(hReponse, 8, 2) & " " & Mid(hReponse, 16, 9) bHeureDete = CInt(Mid(hReponse, 26, 2)) = 50 Or (Month(dDateUTC) > 6 AND CInt(Mid(hReponse, 26, 2)) > 0) dDateLocale = DateAdd("h", nDecalageHoraire, dDateUTC) If bDST Then If bHeureDete Then dDateLocale = DateAdd("h", 1, dDateLocale) End If Response.Write "URL du serveur : " & sURLserveur & "<br />" Response.Write "Réponse du serveur : " & hReponse & "<br />" Response.Write "Date UTC : " & dDateUTC & "<br />" Response.Write "Décalage horaire : " & nDecalageHoraire & "<br />" Response.Write "Heure avancée d’été? : " & bDST & "<br />" Response.Write "Heure locale : " & dDateLocale & "<br />" %>
Exemple de résultat :
URL du serveur : http://time.nist.gov:13 Réponse du serveur : 58013 17-09-17 20:58:48 50 0 0 142.2 UTC(NIST) * Date UTC : 09/17/17 20:58:48 Décalage horaire : -5 Heure avancée d’été : true Heure locale : 2017-09-17 16:58:48