Jak przechowywać daty w programach

0

Mam program, który co kilka minut odpytuje serwis X i pobiera z niego informacje o aktualnych statystykach. Te informacje wrzuca do serwisu Y, który będzie gromadził historię statystyk. Serwis Y przechowuje dane w bazie SQLite w pliku. Do każdego rekordu dodaje datę wrzucenia, która wynosi DateTime.Now.

Inne programy (np. webowe) korzystaja z serwisu Y by pozyskać historię statystyk. Problem jest taki, że jak przeniosłem te programy na serwer, to występuja problemy z data. Np okazywało sie, ze gdy w programie webowym dodalem graf z AmCharts to wszystkie statystyki byly przesuniete o 2 godziny w przeszlosc. W serwisie mialem metode DeleteOldData, ktora usuwala stare rekordy (na podstawie daty wrzucenia). Tutaj tez byl problem, gdyz np. dzialalo to dobrze, a przy kolejnym miesiacu przestalo usuwac dane.

Udalo mi sie to rozwiazac poprzez taka sztuczke:
Added = new DateTime(x.Checked.Year, x.Checked.Month, x.Checked.Day, x.Checked.Hour, x.Checked.Minute, x.Checked.Second, x.Checked.Millisecond, DateTimeKind.Local). Za kazdym razem w serwisie po pobraniu informacji z bazy musze zamieniac date na cos takiego, by byla ona dobrze przetwarzana. Domyslam sie, ze jest to slabe obejscie. Jak wiec powinienem lepiej to rozwiazac?

2

DateTime.Utc.Now

0
fasadin napisał(a):

DateTime.Utc.Now

Teraz jest godzina 10:20 i pada deszcz. Chce wrzucic do bazy danych informacje, ze o 10:20 pada deszcz. Teraz stawiam serwer w Polsce, Hiszpanii i USA. Deployuje tam projekt webowy, ktory bedzie wyswietlal informacje z bazy danych. Chce teraz, by kazda strona wyswietlala informacje:
10:20 - pada deszcz.

UtcNow twierdzi, ze teraz jest 8:20.

0

W tym przypadku musisz uwzględnić przesunięcie czasowe.

0
a_s_f napisał(a):

W tym przypadku musisz uwzględnić przesunięcie czasowe.

A nie da się tego zrobić jakoś prościej? Przecież na siłę mógłbym zapisywać datę w bazie jako string i zawsze po parsowaniu miałbym 10:20..

0

Teraz jest godzina 10:20 i pada deszcz. Chce wrzucic do bazy danych informacje, ze o 10:20 pada deszcz. Teraz stawiam serwer w Polsce, Hiszpanii i USA.

wiec 10:20 masz tylko w Polsce, dla Hiszpanii masz inny czas i dla USA masz inny czas. Twoje Teraz odnosi sie tylko i wylacznie do Polski

Co mozesz zrobic jeszcze to zapisac czas w UTC i zapisywac przesuniecie czasowe. Czyli dla Polski bedzie 8:20 + 2 godziny ale dla Hiszpanii bedzie UTC + 1 godzina a dla USA bedzie (powiedzmy, bo zalezy od stanu) UTC - 7. Wtedy, w zaleznosci od regionu gdzie chcesz wyswietlac czas odpowiednio obliczasz czas i dla danego kraju bedziesz miec w miare poprawny czas.

Ale zeby zrobic Ci troszke wieksze pranie mozgu. To co jezeli bedziesz miec serwer w Samoa? Bo oni omineli dzien (z 29 grudnia przeszli na 31 grudnia)
ogladnij to

i utrzymuj to najprosciej jak potrafisz

lub wyjasnij dokladnie co chcesz osiagnac

0

Spróbuj

DateTime.UtcNow.ToLocalTime();
1

autor chce miec zawsze ten sam czas (Polski) niezaleznie gdzie bedzie postawiony serwer (to jest rzecz ktora chce osiagnac)

  1. zapisuj daty w UTC w bazie (bo moze w przyszlosci Ci sie zmieni i bedziesz chcial miec nie tylko dla Polski)
  2. uzyj TimeZoneInfo do przekonwertowania czasu z UTC https://docs.microsoft.com/en-us/dotnet/api/system.timezoneinfo.converttime?view=netframework-4.7.2#System_TimeZoneInfo_ConvertTime_System_DateTime_System_TimeZoneInfo_

ustawiasz sobie TimeZoneInfo na ten który obejmuje Polske, nastepnie uzywasz dwoch metod z TimeZoneInfo ConvertTimeFromUtc oraz ConvertTimeToUtc jedno do wyswietlenia dla uzytkownika, drugie do zapisywania do bazy danych (drugiego mozesz nie potrzebowac, jezeli bedziesz uzywac Utc.Now

2

Jeśli w grę wchodzą strefy czasowe i zmiany czasu, to najbezpieczniej jest używać:
DateTimeOffset

3

Teraz jest godzina 10:20 i pada deszcz.

Jest godzina 10:20 w Polsce, Hiszpanii, czy USA? A jeśli USA to czy w strefie czasu atlantyckiego, centralnego czy pacyficznego? A jaki jest dzień, bo za miesiąc przesunięcie w stosunku to UTC w Polsce będzie inne? A być może za rok taka sytuacja w ogóle nie nastąpi.

Ogólnie: data i czas to wredne stworzenia. Ale w momencie kiedy zapisujesz sobie czas uniwersalny (wraz z datą!) to potem możesz go łatwo przeliczyć. Zapisuj zawsze w UTC, a wyświetlaj konwertując: https://docs.microsoft.com/pl-pl/dotnet/standard/datetime/converting-between-time-zones#converting-utc-to-a-designated-time-zone

var timeUtc = DateTime.UtcNow;
try
{
   TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
   DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);
   Console.WriteLine("The date and time are {0} {1}.", 
                     cstTime, 
                     cstZone.IsDaylightSavingTime(cstTime) ?
                             cstZone.DaylightName : cstZone.StandardName);
}
catch (TimeZoneNotFoundException)
{
   Console.WriteLine("The registry does not define the Central Standard Time zone.");
}                           
catch (InvalidTimeZoneException)
{
   Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.");
}

Dodatkowe kłamstwa na temat czasu, które mogą być fajne:
30 marca 2014 roku, godzina 2:20:42. Co jest z nią nie tak?
Godzina 23:59:60. Czy jest poprawna?

0

Dzięki w wszystkim za odpowiedzi. Niestety nadal walczę z problemem.
W bazie przechowuję teraz datę tylko w formacie UTC.
Projekt webowy stoi na serwerze. Pobiera on z serwisu informacje historyczne, w tym datę. Jako, iż projekt webowy chce wyświetlać ta date, to najpierw zamienia ja na format Europejski. W tym celi napisalem metode rozszerzajaca:

public static DateTime ToEuropeanDateTime(this DateTime date)
{
    var cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
    return TimeZoneInfo.ConvertTimeFromUtc(date, cstZone);
}

A następnie dane z sformatowana data przesylam do pliku .js, ktory wyswietla mi graf. Niestety nie dziala to poprawnie. Gdy odwiedze strone na swoim prywatnym komputerze, to dane sa wyswietlane poprawnie, natomiast na telewizorze dane sa przesuniete o 2 godziny do przodu. Godzina w telewizorze jest dobra, a wyswietlenie daty przez javascript pokazuja ta sama, co u mnie na komputerze.

Udalo mi sie ten problem rozwiazac zamieniajac z:

var europeanDate = x.ToEuropeanDateTime();

na

var europeanDate = new DateTime(x.Checked.Year, x.Checked.Month, x.Checked.Day, x.Checked.Hour, x.Checked.Minute, x.Checked.Second, x.Checked.Millisecond, DateTimeKind.UTC)

Po zmianie na komputerze jak i na telewizorze wyswietlana jest prawidlowa data. Czegos jednak nie rozumiem.. Dlaczego w tej pierwszej opcji graf pokazuje dobre godziny na komputerze? Dlaczego tez nie pokazuje zlych tak samo jak na telewizorze?

Innym sposobem w jaki udalo mi sie to rozwiazac, to nie uzywanie metod ToEuropeanDateTime, tylko przesylanie do javascript daty w UTC i dodawanie do niej 2h. Żadne z tych rozwiazan mnie nie zadowala, bo to raczej obejscie problemu niz go rozwiazanie..

0

Wszystko zależy od konkretnego problemu i kontekstu. Jeśli np. w bazie przechowujesz daty relatywnie do lokacji, np. "w Warszawie o 23:00 padał deszcz" to możesz przechowywać czas lokalny. Jeśli natomiast chcesz ten czas wyświetlać relatywnie do czasu aplikacji klienta, czyli np. "o 18:00 czasu naszego a 23:00 czasu lokalnego w Warszawie padał deszcz" to warto przechowywać te dane albo w UTC Zulu (+00) albo w formacie UTC ale z uwzględnionym offsetem (czyli np. 23:00+02:00).

Nie ważne jakiego podejścia nie zastosujesz to i tak gdzieś będziesz musiał dokonać konwersji (czy to po stronie klienta, czy też po stronie serwera) i Twoja aplikacja/system będzie musiała podążać za przyjętym rozwiązaniem.

Jeśli chodzi o daty to nie ma uniwersalnego rozwiązania. Wszystko zależy od natury problemu i tego co chce się osiągnąć.

0

Tak jak było powiedziane - DateTimeOffset. Gdzieś kiedyś czytałem, że chyba nawet Microsoft odradza używanie DateTime, bo on nie ma informacji o strefie. DateTimeOffset to taki nowszy DateTime z pełnymi informacjami.
Poza tym zawsze możesz zrobić coś swojego. Trzymaj w bazie dwie liczby całkowite. Jedna to timestamp (ilość sekund od 01-01-1970) w UTC, a druga będzie przechowywać nr strefy czasowej. Ale to już wszystko jest właśnie w DateTimeOffset.

These uses for DateTimeOffset values are much more common than those for DateTime values. As a result, DateTimeOffset should be considered the default date and time type for application development.

1 użytkowników online, w tym zalogowanych: 0, gości: 1