web-dev-qa-db-fra.com

Comment obtenir le fuseau horaire de l'utilisateur actuel en c #

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?

32
Ahsan Attari

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.

20
Mike Gledhill

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

19
Chris Felstead

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.

6
Naruto

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];
    }
3
Ernie Thomason

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"];
    
1
Matt Johnson-Pint

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();
}
0
Mohammad