web-dev-qa-db-fra.com

Ayant des problèmes avec la conversion de mon DateTime en UTC

Je stocke toutes mes dates au format UTC dans ma base de données. Je demande à l'utilisateur son fuseau horaire et je veux utiliser son fuseau horaire plus ce que je suppose, c'est le temps du serveur pour comprendre l'UTC pour lui.

Une fois que je l'ai, je veux faire une recherche pour voir ce qui est dans la base de données en utilisant leur date UTC nouvellement convertie.

mais je reçois toujours cette exception.

System.ArgumentException was unhandled by user code  
Message="The conversion could not be completed because the   
supplied DateTime did not have the Kind property set correctly.  
For example, when the Kind property is DateTimeKind.Local,   
the source time zone must be TimeZoneInfo.Local.  
Parameter name: sourceTimeZone"

Je ne sais pas pourquoi je reçois ça.

J'ai essayé 2 façons

 TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(id);
 // I also tried DateTime.UtcNow
 DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
 var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone );

Cela a échoué alors je suis fatigué

 DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
 var utc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now, 
                                           ZoneId, TimeZoneInfo.Utc.Id);

Cela a également échoué avec la même erreur. Qu'est-ce que je fais mal?

Modifier cela fonctionnerait-il?

 DateTime localServerTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
 TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(id);

 var usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);

 var utc = TimeZoneInfo.ConvertTimeToUtc(usersTime, userInfo);

Modifier 2 @ Jon Skeet

Oui, je pensais juste que je n'aurais peut-être même pas besoin de faire tout ça. Le temps me déroute en ce moment, c'est pourquoi le message n'est peut-être pas aussi clair qu'il devrait l'être. Je ne sais jamais ce que diable DateTime.Now obtient (j'ai essayé de changer mon fuseau horaire en un autre fuseau horaire et il a continué à obtenir mon heure locale).

C'est ce que je voulais que mes trucs fassent. L'utilisateur vient sur le site ajoute une alerte et il est maintenant enregistré en tant qu'utc (avant c'était DateTime.Maintenant, quelqu'un a suggéré de stocker tout UTC).

Donc, avant qu'un utilisateur ne vienne sur mon site et en fonction de l'emplacement de mon serveur d'hébergement, cela pourrait être le lendemain. Donc, si l'alerte devait être affichée le 30 août (leur heure) mais avec le décalage horaire du serveur, ils pourraient venir le 29 août et l'alerte serait affichée.

Je voulais donc combattre cela. Alors maintenant, je ne sais pas si je dois simplement stocker leur heure locale, puis utiliser ce truc de décalage? Ou enregistrez simplement l'heure UTC. Le simple fait de stocker l'heure UTC pourrait toujours être faux, car l'utilisateur penserait probablement à l'heure locale et je ne sais pas comment UTC fonctionne vraiment, il pourrait encore y avoir une différence de temps.

Modifier3

 var info = TimeZoneInfo.FindSystemTimeZoneById(id)

 DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(DataBaseUTCDate,
                                             TimeZoneInfo.Utc, info);
55
chobo2

La structure DateTime ne prend en charge que deux fuseaux horaires:

  • Le fuseau horaire local dans lequel la machine s'exécute.
  • et UTC.

Jetez un œil à la structure DateTimeOffset .

var info = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");

DateTimeOffset localServerTime = DateTimeOffset.Now;

DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);

DateTimeOffset utc = localServerTime.ToUniversalTime();

Console.WriteLine("Local Time:  {0}", localServerTime);
Console.WriteLine("User's Time: {0}", usersTime);
Console.WriteLine("UTC:         {0}", utc);

Production:

Local Time:  30.08.2009 20:48:17 +02:00
User's Time: 31.08.2009 03:48:17 +09:00
UTC:         30.08.2009 18:48:17 +00:00
38
dtb

Vous devez définir Kind sur Unspecified, comme ceci:

DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified);
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone);

DateTimeKind.Local signifie le fuseau horaire local, et aucun autre fuseau horaire. C'est pourquoi vous obtenez l'erreur.

94
SLaks

Comme le dit dtb, vous devez utiliser DateTimeOffset si vous souhaitez stocker une date/heure avec un fuseau horaire spécifique.

Cependant, il n'est pas du tout clair de votre message que vous en avez vraiment besoin. Vous donnez uniquement des exemples en utilisant DateTime.Now et vous dites que vous devinez que vous utilisez l'heure du serveur. Quelle heure voulez-vous réellement? Si vous voulez simplement l'heure actuelle en UTC, utilisez DateTime.UtcNow ou DateTimeOffset.UtcNow. Vous n'avez pas besoin de connaître le fuseau horaire pour connaître l'heure UTC actuelle, précisément parce qu'il est universel.

Si vous obtenez une date/heure de l'utilisateur d'une autre manière, veuillez donner plus d'informations - de cette façon, nous serons en mesure de déterminer ce que vous devez faire. Sinon, nous devinons juste.

7
Jon Skeet

La réponse de tous les autres semble trop complexe. J'avais une exigence spécifique et cela a bien fonctionné pour moi:

void Main()
{
    var startDate = DateTime.Today;
    var StartDateUtc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.SpecifyKind(startDate.Date, DateTimeKind.Unspecified), "Eastern Standard Time", "UTC");
    startDate.Dump();
    StartDateUtc.Dump();
}

Quelles sorties (de linqpad) ce que j'attendais:

20/12/2013 12:00:00 AM

20/12/2013 05:00:00 AM

Props to Slaks pour l'astuce non spécifiée. C'est ce qui me manquait. Mais tout le discours sur le fait qu'il n'y a que deux types de dates (locales et UTC) vient embrouiller le problème pour moi.

Pour info - la machine sur laquelle je l'ai exécuté était dans le fuseau horaire central et l'heure d'été n'était pas en vigueur.

7
JohnOpincar

UTC est juste un fuseau horaire que tout le monde a convenu comme fuseau horaire standard. Plus précisément, c'est un fuseau horaire qui contient Londres, en Angleterre. [~ # ~] modifier [~ # ~] : Notez que ce n'est pas exactement le même fuseau horaire; par exemple, UTC n'a pas d'heure d'été. (Merci, Jon Skeet)

La seule particularité de l'UTC est qu'il est beaucoup plus facile à utiliser dans .Net que tout autre fuseau horaire (DateTime.UtcNow, DateTime.ToUniversalTime Et autres membres).

Par conséquent, comme d'autres l'ont mentionné, la meilleure chose à faire est de stocker toutes les dates en UTC dans votre base de données, puis de les convertir en heure locale de l'utilisateur (en écrivant TimeZoneInfo.ConvertTime(time, usersTimeZone) avant de l'afficher.


Si vous voulez être plus amateur, vous pouvez géolocaliser les adresses IP de vos utilisateurs pour deviner automatiquement leurs fuseaux horaires.

2
SLaks