web-dev-qa-db-fra.com

DateTime.Parse ("2012-09-30T23: 00: 00.0000000Z") est toujours converti en DateTimeKind.Local

Je veux analyser une chaîne qui représente un DateTime au format UTC.

Ma représentation de chaîne inclut la spécification de temps Zulu qui devrait indiquer que la chaîne représente une heure UTC.

var myDate = DateTime.Parse("2012-09-30T23:00:00.0000000Z");    

D'après ce qui précède, je m'attendrais à ce que myDate.Kind soit DateTimeKind.Utc, à la place, il s'agit de DatetimeKind.Local.

Que fais-je de mal et comment analyser une chaîne qui représente une heure UTC?

Merci beaucoup!

57

J'utiliserais personnellement mon Noda Time projet. (Certes, je suis partial en tant qu'auteur, mais ce serait plus propre ...) Mais si vous ne pouvez pas faire ça ...

Utilisez soit DateTime.ParseExact spécifiant le format exact que vous attendez, et incluez DateTimeStyles.AssumeUniversal et DateTimeStyles.AdjustToUniversal dans le code d'analyse:

using System;
using System.Globalization;

class Test
{
    static void Main()        
    {
        var date = DateTime.ParseExact("2012-09-30T23:00:00.0000000Z",
                                       "yyyy-MM-dd'T'HH:mm:ss.fffffff'Z'",
                                       CultureInfo.InvariantCulture,
                                       DateTimeStyles.AssumeUniversal |
                                       DateTimeStyles.AdjustToUniversal);
        Console.WriteLine(date);
        Console.WriteLine(date.Kind);
    }
}

(Tout à fait pourquoi il s'ajusterait au local par défaut sans AdjustToUniversal me dépasse, mais tant pis ...)

EDIT: Juste pour développer mes objections à la suggestion de mattytommo, je visais à prouver qu'elle perdrait des informations. J'ai échoué jusqu'à présent - mais d'une manière très particulière. Jetez un oeil à cela - en cours d'exécution dans le fuseau horaire Europe/Londres, où les horloges remontent le 28 octobre 2012, à 2h heure locale (1h UTC):

DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");
Console.WriteLine(local1 == local2); // True

DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
Console.WriteLine(utc1 == utc2); // False. Hmm.

Il semble qu'il y ait un indicateur "avec ou sans DST" stocké quelque part , mais je serai ravi si je peux trouver où. Les documents pour TimeZoneInfo.ConvertTimeToUtc état

Si dateTime correspond à une heure ambiguë, cette méthode suppose qu'il s'agit de l'heure standard du fuseau horaire source.

Cela ne semble pas être le cas ici lors de la conversion de local2...

EDIT: D'accord, cela devient encore plus étrange - cela dépend de la version du framework que vous utilisez. Considérez ce programme:

using System;
using System.Globalization;

class Test
{
    static void Main()        
    {
        DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
        DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");

        DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
        DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
        Console.WriteLine(utc1);
        Console.WriteLine(utc2);

        DateTime utc3 = local1.ToUniversalTime();
        DateTime utc4 = local2.ToUniversalTime();
        Console.WriteLine(utc3);
        Console.WriteLine(utc4);
    }
}

Cela prend donc deux valeurs UTC différentes , les analyse avec DateTime.Parse, puis les reconvertit en UTC de deux manières différentes.

Résultats sous .NET 3.5:

28/10/2012 01:30:00 // Look - we've lost information
28/10/2012 01:30:00
28/10/2012 00:30:00 // But ToUniversalTime() seems okay...
28/10/2012 01:30:00

Résultats sous .NET 4.5 beta:

28/10/2012 00:30:00 // It's okay!
28/10/2012 01:30:00
28/10/2012 00:30:00
28/10/2012 01:30:00
81
Jon Skeet

Comme d'habitude, la réponse de Jon est très complète. Cela dit, personne n'a encore mentionné DateTimeStyles.RoundtripKind. Si vous souhaitez convertir un DateTime en une chaîne et revenir au même DateTime (y compris en préservant le DateTime.Kind), utilisez le DateTimeStyles.RoundtripKind drapeau.

Comme Jon l'a dit, la bonne chose à faire est d'utiliser le formateur "O" lors de la conversion d'un objet DateTime en chaîne. Cela préserve à la fois les informations de précision et de fuseau horaire. Encore une fois, comme Jon l'a dit, utilisez DateTime.ParseExact lors de la reconversion. Mais si vous utilisez DateTimeStyles.RoundtripKind, vous récupérez toujours ce que vous avez mis:

var now = DateTime.UtcNow;
var strNow = now.ToString("O");
var newNow = DateTime.ParseExact(strNow, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);

Dans le code ci-dessus, newNow est exactement le même temps que now, y compris le fait qu'il est UTC. Si vous exécutez le même code sauf le substitut DateTime.Now pour DateTime.UtcNow, vous obtiendrez une copie exacte de now en tant que newNow, mais cette fois comme heure locale.

Pour mes besoins, c'était la bonne chose car je voulais m'assurer que tout ce qui était passé et converti était reconverti exactement.

27
Simon Gillbee

Utilisez la classe TimeZoneInfo en utilisant les éléments suivants:

var myDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.Parse("2012-09-30T23:00:00.0000000Z"));
4
mattytommo

Ran dans un problème similaire avant et plusieurs heures (et cheveux tirés) ont fini par utiliser DateTime.SpecifyKind :

DateTime.SpecifyKind(inputDate, DateTimeKind.Utc);

Je pense que quelqu'un y a également échappé dans un commentaire ci-dessus.

2
vandsh

Vous pouvez utiliser le format suivant pour la méthode de l'analyseur: yyyy-MM-ddTHH:mm:ss.ffffffK

Cela traitera correctement les informations de fuseau horaire à la fin ( à partir de .NET 2. ).

RE: ISO 8601

2
port443