web-dev-qa-db-fra.com

Où se trouve le spécificateur de format DateTime 'Z'?

[pdate: Les spécificateurs de format ne sont pas la même chose que les chaînes de format; un spécificateur de format est une partie d'une chaîne de format personnalisée, où une chaîne de format est 'stock' et ne fournit pas de personnalisation. Mon problème est avec les spécificateurs, pas les formats ]

J'ai essayé d'effectuer des conversions aller-retour DateTime avec une chaîne de format qui utilise le spécificateur de format 'zzz', qui, je le sais, est lié à l'heure locale. Ainsi, si je tente d'effectuer un aller-retour avec une heure de date UTC, il lève une exception DateTimeInvalidLocalFormat, ce qui devrait être le cas avec ce texte:

La date et l'heure UTC sont converties en texte dans un format qui n'est correct que pour les heures locales. Cela peut arriver lorsque vous appelez DateTime.ToString à l'aide du spécificateur de format 'z', qui inclura un décalage de fuseau horaire local dans la sortie. Dans ce cas, utilisez le spécificateur de format 'Z', qui désigne une heure UTC, ou utilisez la chaîne de format 'o', qui est la méthode recommandée pour conserver un DateTime dans le texte. Cela peut également se produire lors du passage d'un DateTime à sérialiser par XmlConvert ou DataSet. Si vous utilisez XmlConvert.ToString, transmettez XmlDateTimeSerializationMode.RoundtripKind pour une sérialisation correcte. Si vous utilisez DataSet, définissez DateTimeMode sur l'objet DataColumn sur DataSetDateTime.Utc.

Sur la base de cette suggestion, tout ce que je dois faire pour que mon code fonctionne est de remplacer "zzz" par "ZZZ" afin que je puisse me présenter au format UTC. Le problème est que "Z" ne se trouve nulle part dans la documentation et que toute combinaison de formats "Z" que j'essaie, c'est-à-dire "Z", "ZZ", "ZZZ", convertit toujours simplement l'instance DateTime avec ces Z traités comme des littéraux. .

Quelqu'un a-t-il oublié d'implémenter "Z" sans le dire à l'auteur du message d'exception ou me manque-t-il pour échanger un décalage d'heure locale valide avec "+0000" sans piratage?

Exemple de code:

// This is the format with 'zzzzz' representing local time offset
const string format = "ddd MMM dd HH:mm:ss zzzzz yyyy";

// create a UTC time
const string expected = "Fri Dec 19 17:24:18 +0000 2008";
var time = new DateTime(2008, 12, 19, 17, 24, 18, 0, DateTimeKind.Utc);

// If you're using a debugger this will rightfully throw an exception
// with .NET 3.5 SP1 because 'z' is for local time only; however, the exception
// asks me to use the 'Z' specifier for UTC times, but it doesn't exist, so it
// just spits out 'Z' as a literal.
var actual = time.ToString(format, CultureInfo.InvariantCulture);

Assert.AreEqual(expected, actual);
56
Daniel Crenna

Peut-être que le spécificateur de format "K" pourrait être utile. C’est le seul qui semble mentionner l’utilisation du capital "Z".

"Z" est en quelque sorte un cas unique pour DateTimes. Le littéral "Z" fait en réalité partie de la norme ISO 8601 datetime pour les heures UTC. Lorsque "Z" (Zoulou) est cloué à la fin d'un temps, il indique que ce temps est UTC, donc le littéral Z fait partie du temps. Cela crée probablement quelques problèmes pour la bibliothèque de formats de date dans .NET, car il s’agit en fait d’un libellé plutôt que d’un spécificateur de format.

59
Andy White

Lorsque vous utilisez DateTime, vous pouvez stocker une date et une heure dans une variable.

La date peut être une heure locale ou une heure UTC, cela dépend de vous.

Par exemple, je suis en Italie (+2 UTC)

var dt1 = new DateTime(2011, 6, 27, 12, 0, 0); // store 2011-06-27 12:00:00
var dt2 = dt1.ToUniversalTime()  // store 2011-06-27 10:00:00

Alors, que se passe-t-il lorsque j'imprime dt1 et dt2, y compris le fuseau horaire?

dt1.ToString("MM/dd/yyyy hh:mm:ss z") 
// Compiler alert...
// Output: 06/27/2011 12:00:00 +2

dt2.ToString("MM/dd/yyyy hh:mm:ss z") 
// Compiler alert...
// Output: 06/27/2011 10:00:00 +2

dt1 et dt2 ne contiennent que des informations de date et d’heure. dt1 et dt2 ne contiennent pas le décalage de fuseau horaire.

Alors d'où vient le "+2" s'il n'est pas contenu dans les variables dt1 et dt2?

Cela vient du réglage de votre horloge machine.

Le compilateur vous dit que lorsque vous utilisez le format "zzz", vous écrivez une chaîne combinant "DATE ​​+ TIME" (qui sont stockées dans dt1 et dt2) + "TIMEZONE OFFSET" (qui n'est pas contenu dans dt1 et dt2 car ils sont du type DateTyme) et il utilisera le décalage de la machine serveur sur laquelle il exécute le code.

Le compilateur vous dit "Attention: la sortie de votre code dépend du décalage d'horloge de la machine"

Si je lance ce code sur un serveur situé à Londres (+1 UTC), le résultat sera complètement différent: au lieu de "+ 2" il écrira "+ 1 = "

...
dt1.ToString("MM/dd/yyyy hh:mm:ss z") 
// Output: 06/27/2011 12:00:00 +1

dt2.ToString("MM/dd/yyyy hh:mm:ss z") 
// Output: 06/27/2011 10:00:00 +1

La bonne solution consiste à utiliser le type de données DateTimeOffset à la place de DateTime. Il est disponible dans SQL Server à partir de la version 2008 et dans le framework .Net à partir de la version 3.5.

7
Marco Staffoli

Les dates des allers-retours dans les chaînes ont toujours été pénibles ... mais la documentation indique que le spécificateur 'o' est celui à utiliser pour les allers-retours qui capture l'état UTC. Lors de l'analyse, le résultat aura généralement Kind == Utc si l'original était UTC. J'ai constaté que la meilleure chose à faire est de toujours normaliser les dates au format UTC ou local avant de les sérialiser, puis d'indiquer à l'analyseur la normalisation choisie.

DateTime now = DateTime.Now;
DateTime utcNow = now.ToUniversalTime();

string nowStr = now.ToString( "o" );
string utcNowStr = utcNow.ToString( "o" );

now = DateTime.Parse( nowStr );
utcNow = DateTime.Parse( nowStr, null, DateTimeStyles.AdjustToUniversal );

Debug.Assert( now == utcNow );
5
Paul Alexander

Cette page sur MSDN répertorie les chaînes au format DateTime standard, excluant les chaînes à l'aide du caractère "Z".

Mise à jour: vous devez vous assurer que le reste de la chaîne de date suit également le modèle correct (vous n'avez pas fourni d'exemple de ce que vous avez envoyé, il est donc difficile de dire si vous l'avez fait ou non). Pour que le format UTC fonctionne, il devrait ressembler à ceci:

// yyyy'-'MM'-'dd HH':'mm':'ss'Z'
DateTime utcTime = DateTime.Parse("2009-05-07 08:17:25Z");
2
Fredrik Mörk
Label1.Text = dt.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z");

affichera:

07 Mai 2009 | 08:16 | 13 | +02:00 | +02 | +2

Je suis au Danemark, mon décalage par rapport à l'heure GMT est de +2 heures, ce qui est correct.

si vous avez besoin d'obtenir le décalage CLIENT , je vous recommande de vérifier un petit truc que j'ai fait. La page est dans un serveur au Royaume-Uni où GMT est +00: 00 et, comme vous pouvez le voir, vous obtiendrez votre décalage GMT local.


En ce qui concerne votre commentaire, j'ai fait:

DateTime dt1 = DateTime.Now;
DateTime dt2 = dt1.ToUniversalTime();

Label1.Text = dt1.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z");
Label2.Text = dt2.ToString("dd MMM yyyy | hh:mm | FF | ZZZ | ZZ | Z");

et je reçois ceci:

07 Mai 2009 | 08:24 | 14 | +02:00 | +02 | +2
07 Mai 2009 | 06:24 | 14 | ZZZ | ZZ | Z 

Je ne reçois aucune exception, juste ... ça ne fait rien avec Z majuscule :(

Je suis désolé, mais est-ce que je manque quelque chose?


Lecture attentive du MSDN sur chaînes de format de date et d’heure personnalisées

il n'y a pas de support pour la majuscule 'Z'.

2
balexandre

J'avais affaire à DateTimeOffset et malheureusement, le "o" affiche "+0000" et non "Z".

Alors j'ai fini avec:

dateTimeOffset.UtcDateTime.ToString("o")
0
Kugel