Je crée une application dans MVC3 et lorsqu'un utilisateur arrive sur mon site, je veux connaître le fuseau horaire de cet utilisateur. Je veux savoir comment faire cela en c # pas en javaScript?
Comme cela a été mentionné, vous devez que votre client indique à votre serveur ASP.Net les détails sur le fuseau horaire dans lequel il se trouve.
Voici un exemple.
J'ai un contrôleur Angular, qui charge une liste d'enregistrements de ma base de données SQL Server au format JSON. Le problème est que les valeurs DateTime
dans ces enregistrements sont dans le fuseau horaire UTC , et je souhaite montrer à l'utilisateur la date/l'heure dans son fuseau horaire local.
Je détermine le fuseau horaire de l'utilisateur (en minutes) à l'aide de la fonction JavaScript "getTimezoneOffset()
", puis j'ajoute cette valeur à l'URL du service JSON que j'essaie d'appeler:
$scope.loadSomeDatabaseRecords = function () {
var d = new Date()
var timezoneOffset = d.getTimezoneOffset();
return $http({
url: '/JSON/LoadSomeJSONRecords.aspx?timezoneOffset=' + timezoneOffset,
method: 'GET',
async: true,
cache: false,
headers: { 'Accept': 'application/json', 'Pragma': 'no-cache' }
}).success(function (data) {
$scope.listScheduleLog = data.Results;
});
}
Dans mon code ASP.Net, j'extrais le paramètre timezoneOffset
...
int timezoneOffset = 0;
string timezoneStr = Request["timezoneOffset"];
if (!string.IsNullOrEmpty(timezoneStr))
int.TryParse(timezoneStr, out timezoneOffset);
LoadDatabaseRecords(timezoneOffset);
... et passez-le à ma fonction qui charge les enregistrements de la base de données.
C'est un peu compliqué car je veux appeler ma fonction C # FromUTCData
sur chaque enregistrement de la base de données, mais LINQ to SQL ne peut pas combiner SQL brut avec des fonctions C #.
La solution consiste à lire d'abord les enregistrements, puis à les parcourir, en appliquant le décalage de fuseau horaire aux champs DateTime
de chaque enregistrement.
public var LoadDatabaseRecords(int timezoneOffset)
{
MyDatabaseDataContext dc = new MyDatabaseDataContext();
List<MyDatabaseRecords> ListOfRecords = dc.MyDatabaseRecords.ToList();
var results = (from OneRecord in ListOfRecords
select new
{
ID = OneRecord.Log_ID,
Message = OneRecord.Log_Message,
StartTime = FromUTCData(OneRecord.Log_Start_Time, timezoneOffset),
EndTime = FromUTCData(OneRecord.Log_End_Time, timezoneOffset)
}).ToList();
return results;
}
public static DateTime? FromUTCData(DateTime? dt, int timezoneOffset)
{
// Convert a DateTime (which might be null) from UTC timezone
// into the user's timezone.
if (dt == null)
return null;
DateTime newDate = dt.Value - new TimeSpan(timezoneOffset / 60, timezoneOffset % 60, 0);
return newDate;
}
Cela fonctionne bien cependant, et ce code est vraiment utile lors de l'écriture d'un service Web pour afficher la date/l'heure aux utilisateurs dans différentes parties du monde.
En ce moment, j'écris cet article à 11h, heure de Zurich, mais si vous le lisiez à Los Angeles, vous verriez que je l'ai édité à 2h du matin (votre heure locale). En utilisant un code comme celui-ci, vous pouvez obtenir vos pages Web pour afficher les heures de date qui ont du sens pour les utilisateurs internationaux de votre site Web.
Phew.
J'espère que cela t'aides.
Ce n'est pas possible côté serveur à moins que vous ne le supposiez via l'adresse IP des utilisateurs ou que l'utilisateur ne le définisse sous une forme de profil. Vous pouvez obtenir le temps des clients via javascript.
Voir ici pour la solution javacript: Obtenir le fuseau horaire du client en JavaScript
J'ai le même problème, malheureusement, il n'y a aucun moyen pour le serveur de connaître le fuseau horaire du client. Si vous le souhaitez, vous pouvez envoyer le fuseau horaire du client en tant qu'en-tête lors de l'appel ajax.
Dans le cas où vous souhaitez plus d'informations sur l'ajout de l'en-tête, ce message peut aider à ajouter un en-tête à la demande: Comment puis-je ajouter un en-tête HTTP personnalisé à la demande ajax avec js ou jQuery?
new Date().getTimezoneOffset();//gets the timezone offset
Si vous ne voulez pas ajouter d'en-tête à chaque fois, vous pouvez penser à définir un cookie puisque le cookie est envoyé avec toutes les httpRequest, vous pouvez traiter le cookie pour obtenir le fuseau horaire du client côté serveur. Mais je ne préfère pas ajouter de cookies, pour la même raison qu'ils ont envoyé avec toutes les demandes http. Merci.
Je sais que l'utilisateur a posé une question sur une solution non javascript, mais je voulais publier une solution javascript que j'ai trouvée. J'ai trouvé quelques bibliothèques js (jsTimezoneDetect, momentjs), mais leur sortie était un code IANA, qui ne semblait pas m'aider à obtenir un objet TimeZoneInfo en C #. J'ai emprunté des idées à jsTimezoneDetect. En javascript, j'obtiens le BaseUtcOffset et le premier jour de l'heure d'été et l'envoie au serveur. Le serveur convertit ensuite cela en un objet TimeZoneInfo.
Pour l'instant, je ne me soucie pas si le fuseau horaire du client est choisi comme "Pacific Time (US)" ou "Baja California" par exemple, car l'un ou l'autre créera les conversions horaires correctes (je pense). Si je trouve plusieurs correspondances, je choisis actuellement la première correspondance TimeZoneInfo trouvée.
Je peux ensuite convertir mes dates UTC de la base de données en heure locale:
DateTime clientDate = TimeZoneInfo.ConvertTimeFromUtc(utcDate, timeZoneInfo);
Javascript
// Time zone. Sets two form values:
// tzBaseUtcOffset: minutes from UTC (non-DST)
// tzDstDayOffset: number of days from 1/1/2016 until first day of DST ; 0 = no DST
var form = document.forms[0];
var janOffset = -new Date(2016, 0, 1).getTimezoneOffset(); // Jan
var julOffset = -new Date(2016, 6, 1).getTimezoneOffset(); // Jul
var baseUtcOffset = Math.min(janOffset, julOffset); // non DST offset (winter offset)
form.elements["tzBaseUtcOffset"].value = baseUtcOffset;
// Find first day of DST (from 1/1/2016)
var dstDayOffset = 0;
if (janOffset != julOffset) {
var startDay = janOffset > baseUtcOffset ? 180 : 0; // if southern hemisphere, start 180 days into year
for (var day = startDay; day < 365; day++) if (-new Date(2016, 0, day + 1, 12).getTimezoneOffset() > baseUtcOffset) { dstDayOffset = day; break; } // noon
}
form.elements["tzDstDayOffset"].value = dstDayOffset;
C #
private TimeZoneInfo GetTimeZoneInfo(int baseUtcOffset, int dstDayOffset) {
// Converts client/browser data to TimeZoneInfo
// baseUtcOffset: minutes from UTC (non-DST)
// dstDayOffset: number of days from 1/1/2016 until first day of DST ; 0 = no DST
// Returns first zone info that matches input, or server zone if none found
List<TimeZoneInfo> zoneInfoArray = new List<TimeZoneInfo>(); // hold multiple matches
TimeSpan timeSpan = new TimeSpan(baseUtcOffset / 60, baseUtcOffset % 60, 0);
bool supportsDst = dstDayOffset != 0;
foreach (TimeZoneInfo zoneInfo in TimeZoneInfo.GetSystemTimeZones()) {
if (zoneInfo.BaseUtcOffset.Equals(timeSpan) && zoneInfo.SupportsDaylightSavingTime == supportsDst) {
if (!supportsDst) zoneInfoArray.Add(zoneInfo);
else {
// Has DST. Find first day of DST and test for match with sent value. Day = day offset into year
int foundDay = 0;
DateTime janDate = new DateTime(2016, 1, 1, 12, 0, 0); // noon
int startDay = zoneInfo.IsDaylightSavingTime(janDate) ? 180 : 0; // if southern hemsphere, start 180 days into year
for (int day = startDay; day < 365; day++) if (zoneInfo.IsDaylightSavingTime(janDate.AddDays(day))) { foundDay = day; break; }
if (foundDay == dstDayOffset) zoneInfoArray.Add(zoneInfo);
}
}
}
if (zoneInfoArray.Count == 0) return TimeZoneInfo.Local;
else return zoneInfoArray[0];
}
Vous devrez utiliser les technologies côté client et côté serveur.
Côté client:
(en choisir un)
Cela fonctionne dans la plupart des navigateurs modernes:
Intl.DateTimeFormat().resolvedOptions().timeZone
Il existe également la fonction jsTimeZoneDetectjstz.determine()
, ou Moment-Timezonemoment.tz.guess()
pour les navigateurs plus anciens.
Le résultat de l'un ou l'autre sera un identifiant de fuseau horaire IANA , tel que America/Los_Angeles
. Envoyez ce résultat au serveur par tous les moyens que vous souhaitez.
Côté serveur:
(en choisir un)
Utilisation de TimeZoneInfo
(sur les systèmes non Windows uniquement):
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
En utilisant TimeZoneConverter (sur n'importe quel OS):
TimeZoneInfo tzi = TZConvert.GetTimeZoneInfo("America/New_York");
En utilisant NodaTime (sur n'importe quel OS):
DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];
Pour Dot Net version 3.5 et supérieure, vous pouvez utiliser:
TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow);
mais pour Dot Net Under version 3.5, vous pouvez le gérer manuellement de cette façon:
obtenir d'abord le décalage du client et le stocker dans un cookie
function setTimezoneCookie(){
var timezone_cookie = "timezoneoffset";
// if the timezone cookie not exists create one.
if (!$.cookie(timezone_cookie)) {
// check if the browser supports cookie
var test_cookie = 'test cookie';
$.cookie(test_cookie, true);
// browser supports cookie
if ($.cookie(test_cookie)) {
// delete the test cookie
$.cookie(test_cookie, null);
// create a new cookie
$.cookie(timezone_cookie, new Date().getTimezoneOffset());
// re-load the page
location.reload();
}
}
// if the current timezone and the one stored in cookie are different
// then store the new timezone in the cookie and refresh the page.
else {
var storedOffset = parseInt($.cookie(timezone_cookie));
var currentOffset = new Date().getTimezoneOffset();
// user may have changed the timezone
if (storedOffset !== currentOffset) {
$.cookie(timezone_cookie, new Date().getTimezoneOffset());
location.reload();
}
}
}
après cela, vous pouvez utiliser des cookies dans le code backend comme ça:
public static string ToClientTime(this DateTime dt)
{
// read the value from session
var timeOffSet = HttpContext.Current.Session["timezoneoffset"];
if (timeOffSet != null)
{
var offset = int.Parse(timeOffSet.ToString());
dt = dt.AddMinutes(-1 * offset);
return dt.ToString();
}
// if there is no offset in session return the datetime in server timezone
return dt.ToLocalTime().ToString();
}