Le type de données timestamp
est le nom court de timestamp without time zone
.
L'autre option timestamptz
est courte pour timestamp with time zone
.
timestamptz
est le type préféré dans la famille date / heure, littéralement. Il s'est typispreferred
installé pg_type
, ce qui peut être pertinent:
Stockage interne et époque
En interne, les horodatages occupent 8 octets de stockage sur disque et en RAM. Il s'agit d'une valeur entière représentant le nombre de microsecondes depuis l'époque Postgres, 2000-01-01 00:00:00 UTC.
Postgres a également une connaissance intégrée du comptage de temps UNIX couramment utilisé en secondes à partir de l'époque UNIX, 1970-01-01 00:00:00 UTC, et l'utilise dans les fonctions to_timestamp(double precision)
ou EXTRACT(EPOCH FROM timestamptz)
.
Le code source:
* Les horodatages, ainsi que les champs h / m / s des intervalles, sont stockés comme
* valeurs int64 avec unités de microsecondes. (Il était une fois
* valeurs doubles avec unités de secondes.)
Et:
/ * Équivalents à date julienne du jour 0 dans le calcul Unix et Postgres * /
#define UNIX_EPOCH_JDATE 2440588 / * == date2j (1970, 1, 1) * /
#define POSTGRES_EPOCH_JDATE 2451545 / * == date2j (2000, 1, 1) * /
La résolution en microseconde se traduit par un maximum de 6 chiffres fractionnaires pour les secondes.
### timestamp
Une valeur tapée comme indique à Postgres qu'aucun fuseau horaire n'est fourni explicitement. Le fuseau horaire actuel est supposé. Postgres ignore tout modificateur de fuseau horaire ajouté par erreur!timestamp
[without time zone]
Aucune heure n'est décalée pour l'affichage. Avec le même réglage de fuseau horaire, tout va bien. Pour un réglage de fuseau horaire différent, la signification change, mais la valeur et l' affichage restent les mêmes.
### La timestamptz
manipulation de timestamp with time zone
est subtilement différente. Je cite le manuel ici :
Pour timestamp with time zone
, la valeur stockée en interne est toujours en UTC (Universal Coordinated Time ...)
Je souligne le mien. Le fuseau horaire lui-même n'est jamais stocké . Il s'agit d'un modificateur d'entrée utilisé pour calculer l'horodatage UTC correspondant, qui est stocké - ou un modificateur de sortie utilisé pour calculer l'heure locale à afficher - avec le décalage de fuseau horaire ajouté. Si vous n'ajoutez pas de décalage pour l' timestamptz
entrée, le paramètre de fuseau horaire actuel de la session est supposé. Tous les calculs sont effectués avec des valeurs d'horodatage UTC. Si vous devez (ou pourriez devoir) gérer plusieurs fuseaux horaires, utilisez timestamptz
.
Les clients comme psql ou pgAdmin ou toute application communiquant via libpq (comme Ruby avec le pg gem) sont présentés avec l'horodatage plus le décalage pour le fuseau horaire actuel ou selon un fuseau horaire demandé (voir ci-dessous). C'est toujours le même moment dans le temps , seul le format d'affichage varie. Ou, comme le dit le manuel :
Toutes les dates et heures tenant compte du fuseau horaire sont stockées en interne au format UTC. Ils sont convertis en heure locale dans la zone spécifiée par le
paramètre de configuration TimeZone avant d'être affichés au client.
Considérez cet exemple simple (dans psql):
db = # SELECT timestamptz '05 / 03 / 2012 20:00 +03 ';
timestamptz
------------------------
05/03/2012 18:00:00 +01
Je souligne le mien. Que s'est-il passé ici?
J'ai choisi un décalage de fuseau horaire arbitraire +3
pour le littéral d'entrée. Pour Postgres, ce n'est qu'une des nombreuses façons de saisir l'horodatage UTC 2012-03-05 17:00:00
. Le résultat de la requête est affiché pour le réglage du fuseau horaire actuel Vienne / Autriche dans mon test, qui a un décalage +1
pendant l'hiver et +2
pendant l'heure d'été:, 2012-03-05 18:00:00+01
car il tombe à l'heure d'hiver.
Postgres a déjà oublié comment cette valeur a été saisie. Tout ce dont il se souvient est la valeur et le type de données. Tout comme avec un nombre décimal. numeric '003.4'
, numeric '3.40'
ou numeric '+3.4'
- tous donnent exactement la même valeur interne.
### AT TIME ZONE
Dès que vous maîtrisez cette logique, vous pouvez faire tout ce que vous voulez. Tout ce qui manque maintenant, c'est un outil pour interpréter ou représenter les littéraux d'horodatage en fonction d'un fuseau horaire spécifique. C'est là que la AT TIME ZONE
construction entre en jeu. Il existe deux cas d'utilisation différents. timestamptz
est converti en timestamp
et vice versa.
Pour entrer l'UTC timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
... ce qui équivaut à:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
Pour afficher le même moment que HNE timestamp
(heure normale de l'Est):
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
C'est vrai, AT TIME ZONE 'UTC'
deux fois . Le premier interprète la timestamp
valeur comme un horodatage UTC (donné) retournant le type timestamptz
. Le second convertit le timestamptz
en le timestamp
dans le fuseau horaire donné 'EST' - ce qu'une horloge dans le fuseau horaire EST affiche à ce moment unique.
###Exemples
SELECT ts AT TIME ZONE 'UTC'
FROM (
VALUES
(1, timestamptz '2012-03-05 17:00:00+0')
, (2, timestamptz '2012-03-05 18:00:00+1')
, (3, timestamptz '2012-03-05 17:00:00 UTC')
, (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
, (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
, (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ①
, (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ①
, (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ①
, (9, timestamp '2012-03-05 18:00:00+1') -- ② loaded footgun!
) t(id, ts);
Renvoie 8 (ou 9) lignes identiques avec des colonnes timestamptz contenant le même horodatage UTC 2012-03-05 17:00:00
. La 9ème rangée fonctionne dans mon fuseau horaire, mais c'est un piège maléfique. Voir ci-dessous.
① Les lignes 6 à 8 avec le nom du fuseau horaire et l' abréviation du fuseau horaire pour l'heure d'Hawaï sont soumises à l'heure d'été (heure d'été) et peuvent différer, mais pas actuellement. Un nom de fuseau horaire comme 'US/Hawaii'
est conscient des règles DST et de tous les décalages historiques automatiquement, tandis qu'une abréviation comme HST
est juste un code stupide pour un décalage fixe. Vous devrez peut-être ajouter une abréviation différente pour l'heure d'été / standard. Le nom interprète correctement n'importe quel horodatage au fuseau horaire donné. Une abréviation est bon marché, mais doit être la bonne pour l'horodatage donné:
L'heure d'été ne fait pas partie des idées les plus brillantes que l'humanité ait jamais imaginées.
② La ligne 9, marquée comme une arme à pied chargée, fonctionne pour moi , mais seulement par coïncidence. Si vous convertissez explicitement un littéral en timestamp [without time zone]
, tout décalage de fuseau horaire est ignoré ! Seul l'horodatage brut est utilisé. La valeur est alors automatiquement forcée timestamptz
dans l'exemple pour correspondre au type de colonne. Pour cette étape, le timezone
réglage de la session en cours est supposé, qui se trouve être le même fuseau horaire +1
dans mon cas (Europe / Vienne). Mais probablement pas dans votre cas - ce qui entraînera une valeur différente. En bref: ne timestamptz
convertissez pas de littéraux timestamp
ou vous perdez le décalage du fuseau horaire.
##Vos questions
L'utilisateur enregistre une heure, par exemple le 17 mars 2012, 19 h. Je ne veux pas que les conversions de fuseau horaire ou le fuseau horaire soient stockés.
Le fuseau horaire lui-même n'est jamais stocké. Utilisez l'une des méthodes ci-dessus pour entrer un horodatage UTC.
J'utilise uniquement le fuseau horaire spécifié par les utilisateurs pour obtenir des enregistrements «avant» ou «après» l'heure actuelle dans le fuseau horaire local des utilisateurs.
Vous pouvez utiliser une requête pour tous les clients dans différents fuseaux horaires.
Pour le temps global absolu:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
Pour l'heure en fonction de l'horloge locale:
SELECT * FROM tbl WHERE time_col > now()::time
Vous n'êtes pas encore fatigué des informations de base? Il y a plus dans le manuel.
Si vous souhaitez traiter en UTC par défaut:
Dans
config/application.rb
, ajoutez:Ensuite, si vous stockez le nom du fuseau horaire de l'utilisateur actuel,
current_user.timezone
vous pouvez le dire.current_user.timezone
devrait être un nom de fuseau horaire valide, sinon vous obtiendrezArgumentError: Invalid Timezone
, voir la liste complète .la source