2009-08-30 7 views
47

Przechowuję wszystkie moje daty w formacie UTC w mojej bazie danych. Pytam użytkownika o strefę czasową i chcę korzystać z ich strefy czasowej oraz tego, co zgaduję, to czas serwera, aby dowiedzieć się o UTC dla nich.Masz problemy z konwersją mojego DataTime na UTC

Kiedy już to zrobię, chcę przeprowadzić wyszukiwanie, aby zobaczyć zakres w bazie danych za pomocą ich nowo skonwertowanej daty UTC.

ale zawsze otrzymuję ten wyjątek.

System.ArgumentException was unhandled by user code 
Message="The conversion could not be completed because the 
supplied DateTime did not have the Kind property set correctly. 
For example, when the Kind property is DateTimeKind.Local, 
the source time zone must be TimeZoneInfo.Local. 
Parameter name: sourceTimeZone" 

Nie wiem, dlaczego to rozumiem.

Próbowałem 2 sposoby

TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(id); 
// I also tried DateTime.UtcNow 
DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone); 

ta nie powiodła się, więc zmęczony

DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
var utc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now, 
              ZoneId, TimeZoneInfo.Utc.Id); 

To również nie zarówno z tego samego błędu. Co ja robię źle?

Czy to działałoby?

DateTime localServerTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(id); 

var usersTime = TimeZoneInfo.ConvertTime(localServerTime, info); 

var utc = TimeZoneInfo.ConvertTimeToUtc(usersTime, userInfo); 

Edycja 2 @ Jon Skeet

Ya Myślałem właśnie o to, że nie może nawet trzeba to wszystko zrobić. Czas rzeczy wprawia mnie w zakłopotanie właśnie teraz, dlatego post może nie być tak wyraźny, jak być powinien. Nigdy nie wiem, co do diaska DateTime.Now jest coraz (próbowałem zmienić moją strefę czasową na inną strefę czasową i to ciągle otrzymywałem mój lokalny czas).

Oto, co chciałem zrobić. Użytkownik przychodzi do witryny dodaje pewien alert i jest teraz zapisywany jako utc (zanim był DateTime. Teraz ktoś zasugerował, aby przechowywać wszystko UTC).

Więc zanim użytkownik przyjdzie do mojej witryny iw zależności od tego, gdzie był mój serwer hostingowy, może być jak następnego dnia. Tak więc, jeśli powiadomiono o wyświetleniu alertu 30 sierpnia (czasu), ale z różnicą czasu serwera, mogły pojawić się 29 sierpnia, a alarm zostanie wyświetlony.

Więc chciałem walczyć z tym. Więc nie jestem pewien, czy powinienem przechowywać ich czas lokalny, a następnie użyć tego offsetu? Lub po prostu przechowuj czas UTC. Po prostu przechowywanie czasu UTC wciąż może być nie tak, ponieważ użytkownik prawdopodobnie prawdopodobnie myślałby w czasie lokalnym i nie jestem pewien, jak działa UTC, ale i tak może skończyć się różnica czasu.

Edit3

var info = TimeZoneInfo.FindSystemTimeZoneById(id) 

DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(DataBaseUTCDate, 
              TimeZoneInfo.Utc, info); 
+0

'DateTime.Now.Kind' jest już' Lokalny'; nie musisz wywoływać na nim 'SpecifyKind'. – SLaks

+0

@Edit 2: Przechowuj powiadomienia w UTC i wyświetlaj je w strefie czasowej podanej przez użytkownika, jak pokazano w mojej odpowiedzi. – dtb

+0

Jak to działa? Dostaję DateTime.UtcNow, a następnie filtrować alerty z db? następnie przekonwertować znalezione wyniki na ich czas lokalny? – chobo2

Odpowiedz

30

Struktura DateTime obsługuje tylko dwie strefy czasowe.

  • lokalny czasowa maszyna pracuje w
  • a UTC.

Zobacz strukturę DateTimeOffset.

var info = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time"); 

DateTimeOffset localServerTime = DateTimeOffset.Now; 

DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(localServerTime, info); 

DateTimeOffset utc = localServerTime.ToUniversalTime(); 

Console.WriteLine("Local Time: {0}", localServerTime); 
Console.WriteLine("User's Time: {0}", usersTime); 
Console.WriteLine("UTC:   {0}", utc); 

wyjściowa:

Local Time: 30.08.2009 20:48:17 +02:00 
User's Time: 31.08.2009 03:48:17 +09:00 
UTC:   30.08.2009 18:48:17 +00:00 
+0

To wydaje się działać. Czas rzeczy myli, muszę przeczytać na temat tego DAteTimeOffSet i dlaczego działa, a konwersja nie ma miejsca. Ale zastanawiam się, czy nie mogę po prostu DateTime.UtcNow? tak jak to robię, to konwertuje mój czas serwera na utc date, co jest takie samo, gdy robiłem stuff dateoffSet (używając czasu West Africa). A może byłyby inne? – chobo2

+1

Jak mówi Jon Skeet, jeśli chcesz tylko bieżący czas w UTC, po prostu użyj 'DateTime.UtcNow' lub' DateTimeOffset.UtcNow'. – dtb

+0

Zastanawiam się, dlaczego DateTime nie udostępnia właściwości TimeZoneInfo, więc możemy określić datę i godzinę wraz ze strefą czasową !? – brighty

85

Musisz ustawić Kind do Unspecified, tak:

DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified); 
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone); 

DateTimeKind.Local oznacza w lokalnej strefy czasowej, a nie inną strefę czasową. Właśnie dlatego dostałeś błąd.

+0

OK Spróbuję tego. Czy to, co właśnie zredagowałem, działa? Wydaje się być wyłączony (strefa czasowa w zachodniej Afryce była wyłączona o około 10 minut), gdy przechodzisz z jednej strefy czasowej do drugiej, ale czas UTC był prawidłowy. – chobo2

+0

Hmm, więc to nie działa, ponieważ nie zna właściwej strefy czasowej? Wciąż coś mam, ponieważ myślę, że otrzymuję błędne czasy. – chobo2

+0

źle odczytałem twój kod; to faktycznie zadziała. – SLaks

6

Jak mówi dtb, powinieneś użyć DateTimeOffset, jeśli chcesz zapisać datę/godzinę z określoną strefą czasową.

Jednak z twojego postu wcale nie wynika, że ​​naprawdę musisz. Podajesz przykłady tylko za pomocą DateTime.Now i mówisz, że jesteś zgadywania, że używasz czasu serwera. O której naprawdę chcesz? Jeśli chcesz tylko bieżący czas w UTC, użyj DateTime.UtcNow lub DateTimeOffset.UtcNow. Nie musisz znać strefy czasowej, aby poznać aktualny czas UTC, właśnie dlatego, że jest uniwersalny.

Jeśli otrzymujesz datę/czas od użytkownika w inny sposób, podaj więcej informacji - w ten sposób będziemy mogli ustalić, co powinieneś zrobić. W przeciwnym razie po prostu zgadujemy.

+0

zobacz mój oryginalny wpis Edit2 – chobo2

2

UTC to strefa czasowa, którą wszyscy zgodzili się jako standardowa strefa czasowa. Konkretnie jest to strefa czasowa, która zawiera Londyn, Anglia. EDYCJA: Należy pamiętać, że nie jest to dokładnie ta sama strefa czasowa; na przykład UTC nie ma czasu letniego. (Dzięki, Jon Skeet)

Jedyną specjalną cechą UTC jest to, że jest znacznie łatwiejszy w użyciu w .Net niż w jakiejkolwiek innej strefie czasowej (DateTime.UtcNow, DateTime.ToUniversalTime i innych członków).

Dlatego, jak wspominają inni, najlepszą rzeczą dla ciebie zrobić, to zapisać wszystkie daty UTC wewnątrz bazy danych, a następnie przekonwertować do czasu lokalnego (użytkownik pisząc TimeZoneInfo.ConvertTime(time, usersTimeZone) przed wyświetleniem.


Jeśli chcesz być bardziej wyszukane, można geolocate adresy IP użytkowników, aby automatycznie odgadnąć ich stref czasowych

+0

To będą przyszłe wersje lol. Muszę najpierw uzyskać podstawowe informacje. – chobo2

+0

Więc co chcesz teraz wiedzieć? – SLaks

+0

Więc nie dałoby to innego czasu niż wtedy. Tak jak po otrzymaniu wszystkich powiadomień według czasu UTC, zmiana ich na czas lokalny, po znalezieniu, nie spowoduje utraty ostrzeżeń? Jak ich nie będzie, że gdzie tylko niektóre, gdzie przez kilka minut lub coś takiego? – chobo2

5

każdy inny odpowiedź wydaje się zbyt skomplikowane miałem specyficzne wymagania i to działało w porządku dla mnie..

void Main() 
{ 
    var startDate = DateTime.Today; 
    var StartDateUtc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.SpecifyKind(startDate.Date, DateTimeKind.Unspecified), "Eastern Standard Time", "UTC"); 
    startDate.Dump(); 
    StartDateUtc.Dump(); 
} 

które wyjścia (z LINQPad), czego się spodziewałem:

20.12.2013 00:00:00

12/20/2013 5:00:00 AM

Props do Slaksa o nieokreśloną wskazówkę. Tego właśnie mi brakowało. Ale cała rozmowa o tym, że są tylko dwa rodzaje dat (lokalne i UTC) po prostu pomieszała mi ten problem.

FYI - urządzenie, na którym to uruchomiłem, znajdowało się w centralnej strefie czasowej, a DST nie obowiązywało.

Powiązane problemy