J'utilise le fuseau horaire Amérique / New York. À l'automne, nous «retombons» d'une heure - effectivement «gagnant» une heure à 2 heures du matin. Au point de transition, ce qui suit se produit:
il est 01:59:00 -04: 00
puis 1 minute plus tard, il devient:
01:00:00 -05: 00
Donc, si vous dites simplement "1h30 du matin", il est ambigu de savoir si vous faites référence à la première fois à 1h30 ou à la seconde. J'essaie d'enregistrer les données de planification dans une base de données MySQL et je ne peux pas déterminer comment enregistrer correctement les temps.
Voici le problème:
"2009-11-01 00:30:00" est stocké en interne sous le nom 2009-11-01 00:30:00 -04: 00
"2009-11-01 01:30:00" est stocké en interne sous 01/11/2009 01:30:00 -05: 00
C'est bien et assez attendu. Mais comment puis-je enregistrer quoi que ce soit à 01:30:00 -04: 00 ? La documentation ne montre aucun support pour spécifier le décalage et, par conséquent, lorsque j'ai essayé de spécifier le décalage, il a été dûment ignoré.
Les seules solutions auxquelles j'ai pensé impliquent de configurer le serveur sur un fuseau horaire qui n'utilise pas l'heure d'été et de faire les transformations nécessaires dans mes scripts (j'utilise PHP pour cela). Mais cela ne semble pas nécessaire.
Merci beaucoup pour vos suggestions.
Réponses:
Les types de date de MySQL sont, franchement, cassés et ne peuvent pas stocker correctement toutes les heures à moins que votre système ne soit réglé sur un fuseau horaire à décalage constant, comme UTC ou GMT-5. (J'utilise MySQL 5.0.45)
En effet, vous ne pouvez enregistrer aucune heure pendant l'heure précédant la fin de l'heure d'été . Quelle que soit la manière dont vous saisissez les dates, chaque fonction de date traitera ces heures comme si elles se situaient dans l'heure suivant le changement.
Le fuseau horaire de mon système est
America/New_York
. Essayons de stocker 1257051600 (dim, 01 nov 2009 06:00:00 +0100).Voici en utilisant la syntaxe propriétaire INTERVAL:
Même
FROM_UNIXTIME()
ne retournera pas l'heure exacte.Curieusement, DATETIME sera toujours stocker et retour (sous forme de chaîne uniquement!) Fois dans le « perdu » l' heure où commence l' heure d' été (par exemple
2009-03-08 02:59:59
). Mais utiliser ces dates dans n'importe quelle fonction MySQL est risqué:À retenir: Si vous avez besoin de stocker et de récupérer à chaque fois dans l'année, vous avez quelques options indésirables:
Stocker les dates sous forme de INT (comme Aaron l'a découvert, TIMESTAMP n'est même pas fiable)
Prétendre que le type DATETIME a un fuseau horaire à décalage constant. Par exemple, si vous y êtes
America/New_York
, convertissez votre date en GMT-5 en dehors de MySQL , puis stockez-la en DATETIME (cela s'avère essentiel: voir la réponse d'Aaron). Ensuite, vous devez faire très attention en utilisant les fonctions date / heure de MySQL, car certaines supposent que vos valeurs sont celles du fuseau horaire du système, d'autres (en particulier les fonctions arithmétiques de l'heure) sont "indépendantes du fuseau horaire" (elles peuvent se comporter comme si les heures étaient UTC).Aaron et moi soupçonnons que les colonnes TIMESTAMP générées automatiquement sont également cassées. Les deux
2009-11-01 01:30 -0400
et2009-11-01 01:30 -0500
seront stockés comme ambigus2009-11-01 01:30
.la source
select '2009-11-01 00:00:00' + INTERVAL 3600 SECOND;
qui est'2009-11-01 01:00:00'
. UNIX_TIMESTAMP tente alors simplement de convertir ceci en UTC dans le contexte du fuseau horaire de la session - il n'essaye pas d'effectuer l'ajout dans le contexte des règles d'heure d'été de ce fuseau horaire.America/New_York
, je ne vois aucun moyen de stocker 1257051600. Avez-vous?Je l'ai compris pour mes besoins. Je vais résumer ce que j'ai appris (désolé, ces notes sont verbeuses; elles sont autant pour ma future référence que pour toute autre chose).
Contrairement à ce que j'ai dit dans l'un de mes commentaires précédents, les champs DATETIME et TIMESTAMP font se comportent différemment. Les champs TIMESTAMP (comme l'indiquent les documents) prennent tout ce que vous leur envoyez au format "AAAA-MM-JJ hh: mm: ss" et le convertit de votre fuseau horaire actuel en heure UTC. L'inverse se produit de manière transparente chaque fois que vous récupérez les données. Les champs DATETIME n'effectuent pas cette conversion. Ils prennent tout ce que vous leur envoyez et le stockent directement.
Ni les types de champ DATETIME ni TIMESTAMP ne peuvent stocker avec précision les données dans un fuseau horaire qui respecte l'heure d'été . Si vous stockez "2009-11-01 01:30:00", les champs n'ont aucun moyen de distinguer la version de 1h30 que vous vouliez - la version -04: 00 ou -05: 00.
Ok, donc nous devons stocker nos données dans un fuseau horaire non DST (comme UTC). Les champs TIMESTAMP ne peuvent pas gérer ces données avec précision pour des raisons que je vais vous expliquer: si votre système est réglé sur un fuseau horaire DST, ce que vous mettez dans TIMESTAMP peut ne pas être ce que vous récupérez. Même si vous lui envoyez des données que vous avez déjà converties en UTC, il assumera toujours les données dans votre fuseau horaire local et effectuera une autre conversion en UTC. Cet aller-retour local-à-UTC-retour-local imposé par TIMESTAMP est avec perte lorsque votre fuseau horaire local observe l'heure d'été (depuis "2009-11-01 01:30:00" correspond à 2 heures possibles différentes).
Avec DATETIME, vous pouvez stocker vos données dans n'importe quel fuseau horaire de votre choix et être sûr que vous récupérerez tout ce que vous envoyez (vous ne serez pas obligé de participer aux conversions aller-retour avec perte que les champs TIMESTAMP vous imposent). La solution consiste donc à utiliser un champ DATETIME et avant d'enregistrer sur le terrain, convertir le fuseau horaire de votre système dans la zone non-DST dans laquelle vous souhaitez l'enregistrer (je pense que UTC est probablement la meilleure option). Cela vous permet de créer la logique de conversion dans votre langage de script afin de pouvoir enregistrer explicitement l'équivalent UTC de "2009-11-01 01:30:00 -04: 00" ou "" 2009-11-01 01:30: 00 -05: 00 ".
Une autre chose importante à noter est que les fonctions mathématiques date / heure de MySQL ne fonctionnent pas correctement autour des limites de l'heure d'été si vous stockez vos dates dans un TZ DST. Raison de plus pour économiser en UTC.
En un mot, je fais maintenant ceci:
Lors de la récupération des données de la base de données:
Interprétez explicitement les données de la base de données comme UTC en dehors de MySQL afin d'obtenir un horodatage Unix précis. J'utilise la fonction strtotime () de PHP ou sa classe DateTime pour cela. Cela ne peut pas être fait de manière fiable à l'intérieur de MySQL en utilisant les fonctions CONVERT_TZ () ou UNIX_TIMESTAMP () de MySQL car CONVERT_TZ affichera uniquement une valeur 'YYYY-MM-DD hh: mm: ss' qui souffre de problèmes d'ambiguïté, et UNIX_TIMESTAMP () suppose son l'entrée est dans le fuseau horaire du système, et non dans le fuseau horaire dans lequel les données ont été réellement stockées (UTC).
Lors du stockage des données dans la base de données:
Convertissez votre date en heure UTC précise que vous désirez en dehors de MySQL. Par exemple: avec la classe DateTime de PHP, vous pouvez spécifier "2009-11-01 1:30:00 EST" distinctement de "2009-11-01 1:30:00 EDT", puis le convertir en UTC et enregistrer l'heure UTC correcte dans votre champ DATETIME.
Phew. Merci beaucoup pour la contribution et l'aide de tous. Espérons que cela évite à quelqu'un d'autre des maux de tête sur la route.
BTW, je vois cela sur MySQL 5.0.22 et 5.0.27
la source
Je pense que le lien de micahwittman a la meilleure solution pratique à ces limitations de MySQL: réglez le fuseau horaire de la session sur UTC lorsque vous vous connectez:
Ensuite, envoyez-lui simplement les horodatages Unix et tout devrait bien se passer.
la source
Ce fil m'a fait paniquer car nous utilisons des
TIMESTAMP
colonnes avecOn UPDATE CURRENT_TIMESTAMP
(ie:)recordTimestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
pour suivre les enregistrements modifiés et ETL vers un entrepôt de données.Au cas où quelqu'un se demanderait, dans ce cas,
TIMESTAMP
se comporter correctement et vous pouvez différencier les deux dates similaires en convertissant l'TIMESTAMP
horodatage en unix:la source
Vous pouvez convertir en UTC comme:
Mieux encore, enregistrez les dates dans un champ TIMESTAMP . Cela est toujours stocké en UTC et UTC ne connaît pas l'heure d'été / d'hiver.
Vous pouvez convertir UTC en heure locale en utilisant CONVERT_TZ :
Où «+00: 00» est UTC, le fuseau horaire de, et «SYSTEM» est le fuseau horaire local du système d'exploitation sur lequel MySQL s'exécute.
la source
Mysql résout intrinsèquement ce problème en utilisant la table time_zone_name de mysql db. Utilisez CONVERT_TZ pendant CRUD pour mettre à jour la date et l'heure sans vous soucier de l'heure d'été.
la source
Je travaillais sur la journalisation des comptes de visites de pages et l'affichage des comptes dans le graphique (en utilisant le plugin Flot jQuery). J'ai rempli le tableau avec des données de test et tout avait l'air bien, mais j'ai remarqué qu'à la fin du graphique, les points étaient un jour de congé selon les étiquettes sur l'axe des x. Après examen, j'ai remarqué que le nombre de vues pour le jour 2015-10-25 a été récupéré deux fois de la base de données et transmis à Flot, donc chaque jour après cette date a été déplacé d'un jour vers la droite.
Après avoir cherché un bogue dans mon code pendant un moment, j'ai réalisé que cette date correspond à celle de l'heure d'été. Puis je suis arrivé à cette page SO ...
... mais les solutions proposées étaient exagérées pour ce dont j'avais besoin ou elles avaient d'autres inconvénients. Je ne suis pas très inquiet de ne pas pouvoir distinguer les horodatages ambigus.J'ai juste besoin de compter et d'afficher les enregistrements par jour.
Tout d'abord, je récupère la plage de dates:
Ensuite, dans une boucle for, commençant par
min_date
, se terminant parmax_date
, par pas d'un jour (60*60*24
), je récupère les décomptes:Ma solution finale et rapide à mon problème était la suivante:
Je ne regarde donc pas la boucle en début de journée, mais deux heures plus tard . Le jour est toujours le même et je récupère toujours les décomptes corrects, car je demande explicitement à la base de données les enregistrements entre 00:00:00 et 23:59:59 du jour, quelle que soit l'heure réelle de l'horodatage. Et quand le temps passe d'une heure, je suis toujours dans le bon jour.
Remarque: je sais que c'est un fil de 5 ans, et je sais que ce n'est pas une réponse à la question des OP, mais cela pourrait aider des gens comme moi qui ont rencontré cette page à chercher une solution au problème que j'ai décrit.
la source
"SELECT CAST(created_timestamp AS date) day,COUNT(*) WHERE item_id=:item_id AND (created_timestamp BETWEEN '".date("Y-m-d 00:00:00", $min_date_timestamp)."' AND '".date("Y-m-d 23:59:59", $max_date_timestamp)."') GROUP BY day ORDER BY day";