Jak obsługiwać daty przy międzynarodowych aplikacjach?

0

Witam. Potrzebuję pomocy w zrozumieniu, jak działa konwertowanie dat w ASP .Net CORE. Dla przykładu Klient pochodzi z Polski, a serwer stoi w strefie czasowej -2h względem Polski.
Teraz klient w formularzu klika dodaj subskrypcję i przesyła dane do serwera. U klienta jest teraz 10:00, a data utworzenia subskrypcji zapisuje się na serwerze o 08:00

DateTime now = DateTime.Now;
Subscription subscription = new Subscription()
{
    Id = Guid.NewGuid(),
    ApplicationUserId = currentUser.Id,
    IsDeleted = false,
    CreatingTime = now,
    AvailableBuildingsToCreate = foundedSubscriptionTemplate.AvailableBuildingsToCreate,
    SubscriptionTemplateId = input.SubscriptionTemplateId,

    BeginSubscriptionTime = now,
    EndSubscriptionTime = now,
    DisableSubscriptionTimeIfNotPayed = now.AddDays(0).AddMinutes(10),
    TrialExpireTime = null,

    SubscriptionName = input.SubscriptionName,
};

Użytkownik opłacił subskrypcję i przekierowuje go do jego subskrypcji, pobieranej:

var currentUser = await _userManager.GetUserAsync(User);
if (currentUser == null)
    return new List<SubscriptionDto>();

var now = DateTime.Now;
var activeSubscriptions = await _applicationDbContext.Subscriptions
    .Where(x => !x.IsDeleted)
    .Where(x => x.ApplicationUserId == currentUser.Id)
    .Where(x => x.BeginSubscriptionTime < now)
    .Where(x =>
        (x.BeginSubscriptionTime < now && (!x.DisableSubscriptionTimeIfNotPayed.HasValue || x.DisableSubscriptionTimeIfNotPayed > now) && (!x.EndSubscriptionTime.HasValue || x.EndSubscriptionTime > now))
    )
    .OrderBy(x => x.CreatingTime)
    .ToListAsync();

var subscriptionDtos = activeSubscriptions.Select(x => x.ToDto()).ToList();
return subscriptionDtos;

Niestety w widoku użytkownik widzi datę utworzenia subskrypcji na godzinę 08:00. Jest możliwość sprawienia, aby daty wysyłane od klienta do serwera i od serwera do klienta automatycznie konwertowały się na odpowiednią strefę czasową?

Nie wiem, czy dobrze to wyjaśniłem. W razie co powiedzcie, jakich informacji potrzeba

8

Daty (niezależnie od platformy) zapisujesz w UTC czyli DateTime.UtcNow Równocześnie z datą w UTC przechowujesz informację o strefie czasowej użytkownika i w trakcie prezentowania informacji konwertujesz datę z UTC na strefę czasową użytkownika var userDateTime = TimeZoneInfo.ConvertTimeFromUtc(dateTimeUtc, userTimeZone);

Jak masz dużo logiki opartej o date, czas i strefy czasowe to najlepiej skorzystaj z Noda Time żeby zaoszczędzić sobie roboty https://github.com/nodatime/nodatime

0

@markone_dev: Czyli aby przekazać datę z serwera do klienta muszę z poziomu serwera odczytać lokalizacje klienta i przekonwertować DateTime do strefy czasowej klienta?
Czy na odwrót?: Serwer wysyła swój czas UTC a klient go sobie w js przekonwertuje?

1

jak powiedzieliście zamieniłem DateTime.Now na DateTime.UtcNow oraz dodałem:

builder.Services.AddMvc().AddNewtonsoftJson(options =>
{
    options.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
});
builder.Services.AddControllersWithViews().AddNewtonsoftJson(options =>
{
    options.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
});

aby json przesyłany do widoku był w formacie UTC. Wszystko działa pięknie. Dziękuję Wam!

6

Używanie DateTime to chyba największy błąd, jaki można popełnić w takiej sytuacji.
Zmień to na DateTimeOffset, aby uniknąć wszelkich problemów związanych z zastanawianiem się, co insertujący dane do bazy miał na myśli.

0

Z ciekawości jak jest z obsługą Timestamp w .netCore? W końcu ten system daty został wymyślony właśnie celem rozwiązania takich probelmów jak timezone.
Czy dostosowanie do obsługi SQL Serwer jest na tyle duże, że jedyny sensowny sposób działań to powyższe formaty?

0
jurek1980 napisał(a):

Z ciekawości jak jest z obsługą Timestamp w .netCore? W końcu ten system daty został wymyślony właśnie celem rozwiązania takich probelmów jak timezone.
Czy dostosowanie do obsługi SQL Serwer jest na tyle duże, że jedyny sensowny sposób działań to powyższe formaty?

@jurek1980: Masz na myśli linuksowy timestamp, w sensie liczbę milisekund od 1970? To są metody FromUnixTime i ToUnixTime w typach czasowych.
A SQL Server składuje to sobie jeszcze inaczej, w zależności od typu danych kolumny. Ale jedno jest pewne - zakres dat ma znacznie większy niż epoch, bo jednak w bazie często trzyma się np. datę urodzenia, a istnieją ludzie, którzy mają więcej niż 53 lata. :P

1

@somekind: ale jeśli jest data poniżej 1970 to po prostu jest to liczba ujemna.
Unix Timestamp -2156097167 GMT Thu Sep 05 1901 04:07:13 GMT+0000
Coś jak 100 lat p.n.e.

0
jurek1980 napisał(a):

@somekind: ale jeśli jest data poniżej 1970 to po prostu jest to liczba ujemna.
Unix Timestamp -2156097167 GMT Thu Sep 05 1901 04:07:13 GMT+0000
Coś jak 100 lat p.n.e.

Żeby było śmieszniej to ani DateTime ani DateTimeOffset nie obsługują dat przed naszą erą :P

0
jurek1980 napisał(a):

Z ciekawości jak jest z obsługą Timestamp w .netCore? W końcu ten system daty został wymyślony właśnie celem rozwiązania takich probelmów jak timezone.

Tak i nie. Timestampy są dobre, jeśli dostajemy je od klienta lub je generujemy. W innych wypadkach jest różnie. Przykładowo, jeśli otrzymasz informację o locie, że wylatuje danego dnia o danej godzine z danego lotniska bez podanej strefy czasowej. W takim wypadku wyrokowanie jaka jest tam strefa czasowa może być ciężkie a czasami niemożliwe np. wtedy jak zegar się cofa wraz ze zmianą czasu zimowego na letni.

Oczywiście timestampy to dobre rozwiąznie do praktycznie każdego problemu ale trzeba pamiętać, że mierzenie czasu to bardzo skomplikowany temat

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