web-dev-qa-db-ja.com

C#で現在のユーザーのタイムゾーンを取得する方法

MVC3でアプリケーションを構築していますが、ユーザーがサイトにアクセスしたときに、そのユーザーのタイムゾーンを知りたいと思います。 javaScriptではなくc#でこれを行う方法を知りたいですか?

32
Ahsan Attari

前述したように、ASP.Netサーバーがどのタイムゾーンにいるかについての詳細をクライアントに伝える必要があります。

以下に例を示します。

Angularコントローラがあります。これは、JSON形式でSQL Serverデータベースからレコードのリストを読み込みます。問題は、これらのレコードのDateTime値がUTCタイムゾーンにあることです。 、ユーザーのローカルタイムゾーンの日付/時刻を表示したいと思います。

JavaScriptの「getTimezoneOffset()」関数を使用してユーザーのタイムゾーン(分単位)を決定し、この値を、呼び出したいJSONサービスのURLに追加します。

$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;
    });
}

ASP.Netコードで、timezoneOffsetパラメーターを抽出します...

int timezoneOffset = 0;

string timezoneStr = Request["timezoneOffset"];
if (!string.IsNullOrEmpty(timezoneStr))
    int.TryParse(timezoneStr, out timezoneOffset);

LoadDatabaseRecords(timezoneOffset);

...そして、データベースからレコードをロードする関数に渡します。

データベースの各レコードでC#FromUTCData関数を呼び出したいので少し面倒ですが、LINQ to SQLは生のSQLとC#関数を組み合わせることはできません。

解決策は、最初にレコードを読み込み、次にそれらを反復処理し、各レコードのDateTimeフィールドにタイムゾーンオフセットを適用することです。

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;
}

それはうまく機能しますが、このコードは、世界のさまざまな地域のユーザーに日付/時刻を表示するWebサービスを記述するときに非常に役立ちます。

今、私はこの記事をチューリッヒ時間の午前11時に書いていますが、ロサンゼルスで読んでいるなら、午前2時(現地時間)にそれを編集していることがわかります。このようなコードを使用すると、Webページを取得して、Webサイトの国際ユーザーにとって意味のある日時を表示できます。

ふう。

お役に立てれば。

20
Mike Gledhill

これは、ユーザーのIPアドレスを介して想定するか、ユーザーに何らかの形式のプロファイルで設定するようにしない限り、サーバー側では不可能です。 JavaScriptを介してクライアントの時間を取得できます。

Javacriptソリューションについては、こちらをご覧ください。 JavaScriptでクライアントのタイムゾーンを取得する

19
Chris Felstead

同じ問題が発生しました。残念ながら、サーバーがクライアントのタイムゾーンを知る方法はありません。必要に応じて、ajax呼び出しを行いながら、クライアントのタイムゾーンをヘッダーとして送信できます。

ヘッダーの追加に関する詳細が必要な場合は、この投稿でヘッダーをリクエストに追加する方法が役立つ場合があります: jsまたはjQueryを使用してajaxリクエストにカスタムHTTPヘッダーを追加するにはどうすればよいですか

new Date().getTimezoneOffset();//gets the timezone offset

毎回ヘッダーを追加したくない場合は、すべてのhttpRequestでCookieが送信されるため、Cookieを設定してサーバー側でクライアントのタイムゾーンを取得することができます。しかし、すべてのhttpリクエストで送信したのと同じ理由で、クッキーを追加することは好みません。ありがとう。

6
Naruto

ユーザーが非JavaScriptソリューションについて質問したことは知っていますが、私が思いついたJavaScriptソリューションを投稿したかったのです。私はいくつかのjsライブラリ(jsTimezoneDetect、momentjs)を見つけましたが、それらの出力はIANAコードであり、C#でTimeZoneInfoオブジェクトを取得するのに役に立たなかったようです。 jsTimezoneDetectからアイデアを借りました。 javascriptでは、BaseUtcOffsetとDSTの初日を取得し、サーバーに送信します。サーバーは、これをTimeZoneInfoオブジェクトに変換します。

現在、クライアントのタイムゾーンが「太平洋標準時(米国)」または「バハカリフォルニア」として選択されているかどうかは気にしません。どちらかが正しい時間変換を作成します(私は思う)。複数の一致が見つかった場合、現在最初に見つかったTimeZoneInfo一致を選択します。

次に、データベースのUTC日付を現地時間に変換できます。

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

クライアント側とサーバー側の両方のテクノロジーを使用する必要があります。

クライアント側:
(1つを選択)

  • これは、ほとんどの最新のブラウザーで機能します。

    Intl.DateTimeFormat().resolvedOptions().timeZone
    
  • jsTimeZoneDetectjstz.determine()、または Moment-Timezone の古いブラウザ用のmoment.tz.guess()関数もあります。

いずれかの結果は、America/Los_Angelesなどの IANAタイムゾーン識別子 になります。その結果を好きな方法でサーバーに送信します。

サーバー側:
(1つを選択)

  • TimeZoneInfoの使用(非Windowsシステムのみ):

    TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
    
  • TimeZoneConverter を使用(すべてのOSで):

    TimeZoneInfo tzi = TZConvert.GetTimeZoneInfo("America/New_York");
    
  • NodaTime を使用(すべてのOSで):

    DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];
    
1

Dot Netバージョン3.5以降では、次を使用できます。

TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow);

しかし、Dot Netバージョン3.5では、次の方法で手動で処理できます。

最初にクライアントからオフセットを取得し、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();
    }
}

}

その後、そのようなバックエンドコードでCookieを使用できます。

   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