2013-05-02 11 views
12

Przechowuję wszystkie pola DateTime jako czas UTC. Kiedy użytkownik żąda strony internetowej, chciałbym wybrać jego lokalną strefę czasową (a nie lokalną strefę czasową serwera) i automatycznie wyświetlać wszystkie pola DateTime we wszystkich formularzach internetowych jako daty lokalne.Globalnie konwertuj UTC DateTimes na określoną przez użytkownika lokalną datę DateTimes

Oczywiście mogłem zastosować konwersję w każdym wywołaniu funkcji DateTime.ToString() w każdym formularzu lub zaimplementować narzędzie pomocnicze, ale jest to zadanie czasochłonne, a także istnieją składniki innych firm, które są trudne do skonfigurowania z niestandardowe szablony wyświetlania DateTime.

Zasadniczo chciałbym, aby klasa DateTime zachowywać się w sposób następujący:

from this moment on for this web request, 
whenever some code calls DateTime.ToString(), convert it to the local time 
     using the timezone offset given at the very beginning of the web request, 
but if possible, please keep .NET core library DateTime.ToString() calls intact 
     (I don't want to mess up event logging timestamps etc.) 

Czy istnieje jakiś sposób, aby to zrobić?

BTW, używam ASP.NET MVC 4, jeśli to ma znaczenie.

Odpowiedz

3

Krótka odpowiedź brzmi, że nie możesz. HTTP nie wymaga (lub nawet nie zapewnia standardowego sposobu) dla klienta użytkownika (przeglądarki), aby podać informacje o czasie lokalnym lub strefie czasowej w żądaniu HTTP.

albo trzeba

  • poprosić użytkownika o ich korzystnym strefie czasowej, lub
  • mieć po stronie klienta JavaScript zgłosić to do ciebie jakoś (cookie? Ajax? Inny?)

Należy pamiętać, że rozwiązanie javascript po stronie klienta również nie jest doskonałe. JavaScript wyłączony (lub nieistniejący dla niektórych przeglądarek). JavaScript może nie mieć dostępu do informacji o strefie czasowej. Itd.

26

Nie możesz zrobić bezpośrednio tego, o co prosiłeś, ale zasugeruję kilka alternatyw. Jak zauważył Nicholas, w HTTP nie ma nic, co bezpośrednio dałoby strefę czasową.

Wariant 1

  • pierwsze, zdecydować, jaki typ danych strefy czasowej chcesz pracować. Dostępne są dwa różne typy stref czasowych firmy Microsoft: klasa TimeZoneInfo lub strefy czasowe IANA/Olson używane przez resztę świata. Read here for more info. Moim zaleceniem byłby ten drugi, wykorzystujący implementację dostarczoną przez NodaTime.

  • Następnie określ, w której strefie czasowej chcesz dokonać konwersji. Powinieneś zezwolić swojemu użytkownikowi na ustawienie gdzieś, aby wybrać ich strefę czasową.

    • Można wyświetlić listę rozwijaną, aby wybrać jeden z kilku stref czasowych, czy można zrobić coś bardziej użytecznego, jak wyświetlić mapę świata, które mogą kliknąć, aby zaznaczyć swoją strefę czasową. Istnieje kilka bibliotek, które mogą to zrobić w JavaScript, ale moim ulubionym jest this one.

    • Możesz chcieć odgadnąć domyślną strefę czasową, która będzie używana, abyś mógł być tak dokładny, jak to tylko możliwe, zanim wybierze z listy (lub mapy). Jest świetna biblioteka do tego o nazwie jsTimeZoneDetect. Przeszuka zegar przeglądarki i sprawdzi, jakie może być strefa czasowa.Jest dość dobry, ale wciąż jest tylko zgadywaniem. Nie używaj go na ślepo, ale używaj go do określenia punktu początkowego. Aktualizacja Możesz teraz zrobić to również z moment.tz.guess(), w komponencie 01.pliku moment.js.

  • Teraz, gdy wiesz strefę czasową użytkownika, można użyć tej wartości do konwersji UTC DateTime wartości do tej lokalnej strefy czasowej. Niestety, nic nie można ustawić na wątku, który to zrobi. Po zmianie strefy czasowej system jest globalny dla wszystkich procesów i wątków. Nie masz więc innego wyjścia, jak przekazać strefę czasową do każdego miejsca, z którego je odsyłasz. (Sądzę, że to było twoje główne pytanie.) See this almost duplicate here.

  • Zanim skonwertujesz go na ciąg, musisz również znać lokalizację użytkownika (którą możesz uzyskać z wartości Request.UserLanguages). Możesz przypisać go do bieżącego wątku lub przekazać go jako parametr do metody DateTime.ToString(). Nie powoduje to żadnej konwersji strefy czasowej - zapewnia jedynie, że liczby znajdują się we właściwej pozycji, używając odpowiednich separatorów i odpowiedniego języka dla nazw dni lub miesięcy.

Wariant 2

Nie przekonwertować go na czas lokalny na serwerze w ogóle.

  • Ponieważ powiedziałeś, że pracują z wartościami UTC, upewnij się ich .Kind nieruchomość jest Utc. powinieneś to zrobić podczas ładowania z bazy danych, ale jeśli trzeba można to zrobić ręcznie:

    myDateTime = DateTime.SpecifyKind(myDateTime, DateTimeKind.Utc); 
    
  • odesłać go do przeglądarki jako czystego UTC, w niezmiennym formacie jak ISO8601. Innymi słowy:

    myDateTime.ToString("o"); // example: "2013-05-02T21:01:26.0828604Z" 
    
  • Użyj kodu JavaScript w przeglądarce, aby przeanalizować go jako UTC. Automatycznie pobierze ustawienia czasu lokalnego przeglądarki. Jednym ze sposobów jest użycie wbudowanego Date obiektu w JavaScript, np:

    var dt = new Date('2013-05-02T21:01:26.0828604Z'); 
    

    Jednak będzie to działać tylko w nowszych przeglądarkach obsługujących formacie ISO-8601. Zamiast tego zalecam korzystanie z biblioteki moment.js. Jest spójny w różnych przeglądarkach i zapewnia lepszą obsługę dat i lokalizacji ISO. Dodatkowo otrzymujesz wiele innych użytecznych funkcji analizowania i formatowania.

    // pass the value from your server 
    var m = moment('2013-05-02T21:01:26.0828604Z'); 
    
    // use one of the formats supported by moment.js 
    // this is locale-specific "long date time" format. 
    var s = m.format('LLLL'); 
    

Zaletą wariantu 1 jest to, że można pracować z dowolnym czasie w strefie czasowej. Jeśli możesz poprosić użytkownika o strefę czasową z listy rozwijanej, nie musisz używać JavaScript.

Zaletą opcji 2 jest uzyskanie przeglądarki, która wykona część pracy za Ciebie. Jest to najlepszy sposób na wysyłanie nieprzetworzonych danych, na przykład wywoływanie wywołań AJAX do interfejsu WebAPI. Jednak JavaScript zna tylko UTC i lokalną strefę czasową przeglądarki. Tak więc nie działa tak dobrze, jeśli chcesz przekonwertować na inne strefy.

Należy również pamiętać, że jeśli wybierzesz opcję nr 2, może wystąpić usterka w projekcie ECMAScript 5.1. To wchodzi w grę, jeśli pracujesz z datami, które są objęte innym zestawem reguł czasu letniego niż obecnie obowiązujące. Możesz przeczytać więcej in this question i on my blog.

Byłoby znacznie łatwiej, gdybyśmy mieli pewne informacje o strefach czasowych w nagłówkach HTTP, ale niestety tak nie jest. To dużo obręczy, do których można przejść, ale jest to najlepszy sposób na uzyskanie zarówno elastyczności, jak i dokładności.

+0

Świetna odpowiedź! Powiedziałeś, że można to zrobić za pomocą wbudowanego obiektu 'Date' w JavaScript. Czy mógłbyś zaktualizować swoją odpowiedź, aby pokazać, jak można to zrobić? – Scott

+0

@Scott - Zaktualizowano. Dzięki. –

Powiązane problemy