web-dev-qa-db-ja.com

DateTime.NowおよびCulture / Timezone固有

私たちのアプリケーションは、地理的に異なる場所のユーザーを処理するように設計されています。

現在のエンドユーザーのローカル時間とタイムゾーンが何に作用しているかを検出することはできません。ヨーロッパ/ロンドンのタイムゾーンからアクセスする場合でも、sv-se、en-us、ta-Inなどの異なる文化を選択します。

米国のホスティングサーバーでホストしました。アプリケーションユーザーは_Norway/Denmark/Sweden/UK/USA/India_

問題は、レコードの作成/更新日などを保存するために_DateTime.Now_を使用したことです。

ServerはUSAで実行されるため、すべてのユーザーデータは米国時間として保存されます:(

SOで調査した後、すべての履歴日付を_DateTime.UtcNow_としてDBに保存することにしました

問題:

enter image description here

_29 Dec 2013, 3:15 P.M Swedish time_に作成されたレコードがあります。

_ public ActionResult Save(BookingViewModel model)
    {
        Booking booking = new Booking();
        booking.BookingDateTime = model.BookingDateTime; //10 Jan 2014 2:00 P.M
        booking.Name = model.Name;
        booking.CurrentUserId = (User)Session["currentUser"].UserId;
        //USA Server runs in Pacific Time Zone, UTC-08:00
        booking.CreatedDateTime = DateTime.UtcNow; //29 Dec 2013, 6:15 A.M
        BookingRepository.Save(booking);
        return View("Index");
    }
_

インド/スウェーデン/アメリカにログインしたユーザーに同じ履歴時間を表示したいと思います。

現在、ログインしている現在のカルチャユーザーを使用して、構成ファイルからタイムゾーンを選択し、TimeZoneInfoクラスでの変換に使用しています

_<appSettings>
    <add key="sv-se" value="W. Europe Standard Time" />
    <add key="ta-IN" value="India Standard Time" />
</appSettings>

    private DateTime ConvertUTCBasedOnCulture(DateTime utcTime)
    {
        //utcTime is 29 Dec 2013, 6:15 A.M
        string TimezoneId =                  
                System.Configuration.ConfigurationManager.AppSettings
                [System.Threading.Thread.CurrentThread.CurrentCulture.Name];
        // if the user changes culture from sv-se to ta-IN, different date is shown
        TimeZoneInfo tZone = TimeZoneInfo.FindSystemTimeZoneById(TimezoneId);

        return TimeZoneInfo.ConvertTimeFromUtc(utcTime, tZone);
    }
    public ActionResult ViewHistory()
    {
        List<Booking> bookings = new List<Booking>();
        bookings=BookingRepository.GetBookingHistory();
        List<BookingViewModel> viewModel = new List<BookingViewModel>();
        foreach (Booking b in bookings)
        {
            BookingViewModel model = new BookingViewModel();
            model.CreatedTime = ConvertUTCBasedOnCulture(b.CreatedDateTime);
            viewModel.Add(model);
        }
        return View(viewModel);
    }
_

コードを見る

_   @Model.CreatedTime.ToString("dd-MMM-yyyy - HH':'mm")
_

注:ユーザーは、ログインする前にカルチャ/言語を変更できます。そのローカライズベースのアプリケーションは、米国のサーバーで実行されます。

NODATIME を見ましたが、別の場所でホストされているマルチカルチャーWebアプリケーションでどのように役立つか理解できませんでした。

質問

INDIA/USA/Anywhere`にログインしているユーザーに同じレコード作成日_29 Dec 2013, 3:15 P.M_を表示するにはどうすればよいですか?

現在、ConvertUTCBasedOnCultureのロジックは、カルチャにログインしているユーザーに基づいています。ユーザーはインド/アメリカの任意のカルチャを使用してログインできるため、これはカルチャに関係なく行う必要があります

データベース列

CreatedTime:SMALLDATETIME

UPDATE:ATTEMPTED SOLUTION:

_DATABASE COLUMN TYPE: DATETIMEOFFSET_

[〜#〜] ui [〜#〜]

最後に、各リクエストで以下のMomento.jsコードを使用して現在のユーザーの現地時間を送信しています

_$.ajaxSetup({
    beforeSend: function (jqXHR, settings) {
        try {
      //moment.format gives current user date like 2014-01-04T18:27:59+01:00
            jqXHR.setRequestHeader('BrowserLocalTime', moment().format());
        }
        catch (e) {
        }
    }
});
_

[〜#〜] application [〜#〜]

_public static DateTimeOffset GetCurrentUserLocalTime()
{
    try
    {
      return 
      DateTimeOffset.Parse(HttpContext.Current.Request.Headers["BrowserLocalTime"]);
    }
    catch
    {
        return DateTimeOffset.Now;
    }
}
_

で呼び出された

_ model.AddedDateTime = WebAppHelper.GetCurrentUserLocalTime();
_

ビューで

_@Model.AddedDateTime.Value.LocalDateTime.ToString("dd-MMM-yyyy - HH':'mm")
_

ビューではユーザーに現地時間を表示しますが、_dd-MMM-yyyy CET/PST_(2時間前)のように表示したいです。

この2時間前は、エンドユーザーの現地時間から計算する必要があります。タイムゾーン表示とローカルユーザー計算を使用して作成/編集されたスタックオーバーフロー質問とまったく同じです。

例:answered Jan 25 '13 at 17:49 CST (6 hours/days/month ago)したがって、USA/INDIAユーザーの他の視聴者は、このレコードがINDIA/USAから正確に6時間作成されたことを本当に理解できる現在の時刻

表示形式と計算を除き、ほぼすべてを達成したと思います。これどうやってするの?

27
Billa

DateTimeOffsetの代わりにDateTimeを保存する必要があるようです。 couldローカルDateTimeを値を作成しているユーザーに保存するだけですが、これは順序付け操作などを実行できないことを意味します。DateTime.UtcNowを使用することはできません、レコードが作成されたときのユーザーのローカルの日付/時刻を示すものは何も保存しないためです。

あるいは、インスタントをユーザーのタイムゾーンと共に保存することもできます。これは達成が困難ですが、「ユーザーの現地時間は1時間後ですか?」などと言えば、より多くの情報が得られます。

サーバーのhostingは無関係である必要があります。サーバーのタイムゾーンを使用しないでください。ただし、ユーザーの適切なUTCオフセット(またはタイムゾーン)を知る必要があります。これはできないカルチャのみに基づいて行われます-ユーザーのマシンでJavascriptを使用して、興味のある時刻(必ずしも「今」ではない)でUTCオフセットを決定します。

値を保存する方法を考え出し、値を取得するのは簡単です-UTCインスタントとオフセットを既に保存している場合は、そのオフセットを適用するだけで、元のユーザーのローカル時間に戻ります。値をテキストに変換する方法については説明していませんが、単にドロップアウトするだけです。値をフォーマットするだけで、元の現地時間を取得する必要があります。

Noda Timeを使用する場合は、OffsetDateTimeの代わりにDateTimeOffsetを使用します。

14
Jon Skeet

標準的なアプローチは、特定の瞬間が重要な場合、常にUTCとして時刻データを保存することです。その時間は、タイムゾーンの変更や文化の影響を受けません。

タイムゾーンで時間を表示する最も一般的な方法は、時間をUTCとして保存し、値を表示するときに現在のユーザーのカルチャ/タイムゾーンの組み合わせに変換することです。このアプローチは、ストレージにファイルされた単一の日時のみを必要とします。

Webケース(ASP.Netなど)の場合、最初にユーザーのカルチャ/タイムゾーンを把握し、サーバーに送信する必要があることに注意してください(この情報はGETリクエストで利用可能である必要はないため)。

「同じ履歴時間を表示する」内容によっては、現在のカルチャやオフセットなどの追加情報を保存する必要がある場合があります。元のユーザーが見たとおりに時間を表示する必要がある場合は、文字列表現を保存することもできます(フォーマット/翻訳は後で変更され、値が異なって見えるため、また珍しいことです)。

注:カルチャとタイムゾーンは結び付けられていないため、US PSTタイムゾーンでIN-INカルチャのようなケースを処理する方法を決定する必要があります。

11
Alexei Levenkov

私はあなたの質問の言い回しに少し混乱していますが、あなたのユーザーのタイムゾーンを決定したいと思われます。

  • あなたは彼らに尋ねようとしましたか?多くのアプリケーションでは、ユーザーがユーザー設定でタイムゾーンを選択します。

  • ドロップダウンリスト、またはペアのリスト(国、次に国内のタイムゾーン)、または マップベースのタイムゾーンピッカーコントロール から選択できます。

  • 推測する で、ユーザーが変更しない限り、これをデフォルトとして使用できます。

そのルートを下る場合、IANA/Olsonタイムゾーンを使用できるようにする必要があります。これは、 Noda Time が作用する場所です。それらにはDateTimeZoneProviders.Tzdbからアクセスできます。

UTCを使用している場合、ホスティング場所は無関係です。よかったです。

また、Noda Timeを使用している場合は、おそらくSystemClock.Instance.Nowの代わりにDateTime.UtcNowを使用する必要があります。

here および here も参照してください。

また、別の解決策は、UTC時間をブラウザーに渡し、それをJavaScript Dateオブジェクトにロードすることです。ブラウザはそれをユーザーの現地時間に変換できます。 moment.js のようなライブラリを使用して、これを簡単にすることもできます。


更新

カルチャーコードをタイムゾーンにマッピングするアプローチについて:

<appSettings>
    <add key="sv-se" value="W. Europe Standard Time" />
    <add key="ta-IN" value="India Standard Time" />
</appSettings>

そのは機能しません、いくつかの理由で:

  • 多くの人々は、自分のコンピューター上で物理的にいる地域とは異なる文化設定を使用します。たとえば、私はアメリカ英語を話す人ですドイツでは、私の文化コードはおそらくen-USではなくde-DEです。

  • 国を含む文化コードは、言語の方言を区別するために使用されます。 es-MXが表示される場合、それは「メキシコで話されているスペイン語」を意味します。ユーザーが実際にinメキシコにいるという意味ではありません。 「スペイン語で話されるスペイン語」を意味するes-ESと比較して、ユーザーはスペイン語の方言を話すことを意味します。

  • 文化コードの国部分が信頼できる場合でも、多くの国には複数のタイムゾーンがあります!たとえば、en-USのマッピングリストに何を入れますか?私たち全員が東部標準時であると仮定することはできません。

さて、私は説明しましたなぜあなたの現在のアプローチがうまくいかないので、私は元のアドバイスを強く勧めます非常に簡単に:

  1. 上記のリンク先のユーティリティのいずれかの支援を受けて、ユーザーのタイムゾーンを確認してください。

  2. UTCを保存しているので、そのタイムゾーンに変換して表示するだけです。

    Microsoftタイムゾーンの使用
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
    DateTime localDatetime = TimeZoneInfo.ConvertTimeFromUtc(yourUTCDateTime, tz);
    
    IANAタイムゾーンと野田時間の使用
    DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/Stockholm"];
    Instant theInstant = Instant.FromDateTimeUtc(yourUTCDateTime);
    LocalDateTime localDateTime = theInstant.InZone(tz);
    
7

私が最近取り組んだアプリケーションで同様の問題に直面しました。開発中は、全員が同じタイムゾーンにあり、問題は認識されませんでした。そして、いずれにせよ、DBにすでに存在するすべての日時情報を変換することは言うまでもなく、変更するのが面倒だった多くのレガシーコードがありました。したがって、DateTimeOffsetへの変更はオプションではありませんでした。しかし、途中でサーバー時間からユーザー時間に変換し、途中でユーザー時間からサーバー時間に変換することで、一貫性を保つことができました。これは、境界である日付と時刻の比較で行うことも重要でした。したがって、ユーザーが時間の真夜中に何かが切れると予想した場合、その時間をサーバー時間に変換し、サーバー時間ですべての比較を行います。これは多くの作業のように聞こえますが、DateTimeOffsetsを使用するようにアプリケーションとDB全体を変換するよりもはるかに少ない作業でした。

聴覚は、タイムゾーンの問題に対するいくつかの良い解決策があるように見えるスレッドです。

ユーザーのタイムゾーンを決定する

1
SzabV

ユーザーが履歴を表示しているロケールに関係なく、一貫した日付/時刻の履歴をユーザーに表示する場合は、次のようにします。

  1. Save中に、UTC「作成」日時だけでなく、検出されたロケールも保存します
  2. Stored saved from localeを使用して元の日付/時刻を計算し、表示する文字列を出力します(つまり、現在のユーザーロケールを表示するときに使用しません)

ストレージを修正する機能がない場合は、送信を変更して「現在のクライアント時間」を送信し、文字列で保存して(UTCに変換しない)、文字列で表示する(検出されたカルチャに変換しない)ことができます)

しかし、あなたの質問の下で私のコメントで言うように、私はあなたの要求が正しいと確信していません。

0
G. Stoynev