Jak możemy opracować praktyki kodowania mające na celu ochronę przed błędami lat przestępnych? [Zamknięte]

80

Firma Microsoft właśnie ogłosiła, że ​​błąd oprogramowania w obliczaniu dat (ponad rok przestępny) spowodował poważną awarię systemu Windows Azure w zeszłym tygodniu.

Czy to naprawdę był zwykły błąd w ocenie DateTime.Now.AddYears(1)sytuacji w roku przestępnym?

Jakie praktyki kodowania mogły temu zapobiec?

EDYCJA Jak wskazał dcstraw DateTime.Now.AddYears(1)w roku przestępnym, w rzeczywistości zwraca poprawną datę w .NET. Więc to nie jest błąd frameworka, ale ewidentnie błąd w obliczeniach Date.

Utrwalacz
źródło
7
Ponieważ nie ma „jednej” odpowiedzi i jest to bardziej dyskusyjne pytanie, myślę, że należy je przenieść do programistów. Ponadto: Zawsze testuj swój kod jednostkowo. Zawsze.
George Stocker,
2
Kwestie związane z datą i czasem są trudnym i skomplikowanym problemem. Można by powiedzieć coś w rodzaju robienia wszystkiego w UTC, ale zawsze będzie jakiś sens tłumaczenia ... a kiedy tak się stanie, liczba permutacji, które trzeba będzie uwzględnić, aby uniknąć wszystkich błędów, jest oszałamiająca. Większość z nas nigdy nie będzie pracować w systemie, który musi się przejmować.
Josh
9
Myślę, że to pytanie jest zbyt interesujące, aby je zamknąć :-)
Steven
11
CZTERY głosy do zamknięcia? Mam nadzieję, że ten zły temperament, radosne zakończenie jest tylko fazą ewolucji SO. To wszystko jest trochę bardziej święte niż ty.
Iain Holder
7
@IainMH muszę się z tobą zgodzić, zwłaszcza że prawie wszyscy, którzy go zamknęli, nigdy nie zadali pytania, mając więcej niż kilka głosów.
Lloyd

Odpowiedzi:

95

Bezwstydna wtyczka:

Użyj lepszego interfejsu API daty i czasu

Wbudowane biblioteki daty i czasu .NET są strasznie trudne w użyciu. Oni nie pozwalają zrobić wszystko, czego potrzeba, ale nie można wyrazić się jasno za pośrednictwem systemu typu. DateTimeto bałagan , DateTimeOffsetmoże cię uświadomić, że faktycznie przechowujesz informacje o strefie czasowej, gdy nie jesteś, i TimeZoneInfonie zmusza do myślenia o wszystkim, co powinieneś rozważyć.

Żaden z nich nie zapewnia przyjemnego sposobu na powiedzenie „tylko pory dnia” lub „tylko daty”, ani też nie czyni wyraźnego rozróżnienia między „czasem lokalnym” a „czasem w określonej strefie czasowej”. A jeśli chcesz korzystać z kalendarza innego niż gregoriański, musisz Calendarcały czas przechodzić przez zajęcia.

Dlatego właśnie buduję Noda Time - alternatywną bibliotekę dat i godzin zbudowaną na porcie "silnika" Joda Time , ale z nowym (i odchudzonym) API na wierzchu.

Kilka punktów, o których warto pomyśleć, a które łatwo przeoczyć, jeśli ich nie znasz:

  • Mapowanie lokalnej daty / godziny na jedną w określonej strefie czasowej nie jest tak proste, jak mogłoby się wydawać. Określona lokalna data / godzina może wystąpić raz, dwa razy (niejednoznaczność) lub zero razy (jest pomijana) z powodu przejścia na czas letni
  • Strefy czasowe różnią się historycznie - TimeZoneInfoszczerze mówiąc więcej, niż jest ogólnie skłonny ujawnić. (Nie obsługuje strefy czasowej, w której idea „czasu standardowego” zmienia się w czasie lub która przechodzi w stały czas letni).
  • Nawet w przypadku bazy danych zoneinfo identyfikatory stref czasowych niekoniecznie są stabilne. (CLDR rozwiązuje ten problem; coś, co mam nadzieję w końcu wesprzeć w Noda Time.)
  • Tekstowe reprezentacje dat i godzin są koszmarem, nie tylko pod względem kolejności, ale także separatorów dat, separatorów czasu i dziwnych rzeczy, takich jak dopełniacz nazw miesięcy
  • Początek dnia nie zawsze jest o północy - na przykład w Brazylii przejście na wiosenny czas letni przesuwa zegar ścienny z godziny 23:59:59 na 1 w nocy.
  • W niektórych przypadkach (cóż, takich, o których wiem), że strefa czasowa może wymusić pominięcie całego dnia - 30 grudnia 2011 r. Nie wystąpił na Samoa! Podejrzewam, że większość programistów może prawdopodobnie zignorować to, ale ...
  • Jeśli zamierzasz używać kalendarza innego niż gregoriański, bądź ostrożny i upewnij się, że naprawdę wiesz, jak ma się zachowywać.

Jeśli chodzi o konkretne praktyki rozwojowe:

  • Pomyśl o tym, co naprawdę próbujesz reprezentować. Spodziewam się, że główną zaletą Noda Time będzie zmuszanie programistów do wybierania między różnymi typami reprezentowania ich danych. Zrób to dobrze, a wszystko inne będzie prostsze.
  • Testuj wszystko, o czym myślisz. Będzie to oczywiście zależeć od tego, co dokładnie robi twój system, ale szczególnie weź pod uwagę różne strefy czasowe, to, co dzieje się podczas przejść do czasu letniego i oczywiście lat przestępnych.
  • Radziłbym wstrzyknąć „interfejs podobny do zegara” - usługę do wskazywania aktualnego czasu - zamiast jawnie wywoływać DateTime.Nowlub DateTime.UtcNow; ułatwia to (wykonalne!) testy jednostkowe
  • Jeśli wykonujesz wiele operacji za pomocą „teraz”, uzyskaj tę datę / godzinę raz i zapamiętaj ją, zamiast wielokrotnie żądać „teraz” - w przeciwnym razie wartość może się zmieniać w niefortunny sposób między wywołaniami.
  • „Zrób wszystko w UTC” też nie zawsze jest odpowiedzią - jeśli chcę wiedzieć, „kiedy dokładnie„ za dwa tygodnie ”wystąpi w mojej lokalnej strefie czasowej?” wtedy muszę zapisać lokalną datę / godzinę, a także strefę czasową.
Jon Skeet
źródło
2
@flq: Ponownie, musiałbyś dokładnie zdefiniować, co masz na myśli, mówiąc „bezpieczny rok przestępny”. Wątpię, czy to błąd frameworka spowodował problem na Azure - spodziewam się, że było to słabe wykorzystanie frameworka.
Jon Skeet
10
Kilkanaście punktorów ogłoszeń bez ani jednego na temat lat przestępnych (tj. Tematu). A potem dmuchnął na Twitterze. Myślę, że miałeś lepsze chwile, Jon.
Will Dean
8
@WillDean: Zwróć również uwagę, że pytanie zostało zredagowane przez George'a Stockera. Oryginalny tytuł brzmiał „Programowanie obronne przed błędami DateTime” - w takim przypadku myślę, że zgodzisz się, że mój post jest w pełni istotny. (Dopiero co zauważyłem, że tytuł został zmieniony. Kiedy zacząłem odpowiadać ... był na nim napis DateTime ...)
Jon Skeet
2
Jon, przepraszam, nie widziałem poprzedniego tytułu! Może ktoś powinien zmienić tytuł, aby edytować wszystkie odpowiedzi ...
Will Dean
3
@WillDean: Tak ... Kusi mnie, aby cofnąć zmianę tytułu lub zakończyć ją na „Błędy związane z datą i czasem (np. Lata przestępne)”
Jon Skeet
25

Warto zauważyć, że błąd prawdopodobnie nie był spowodowany taką linią, jaką opublikowałeś:

DateTime.Now.AddYears(1)

To nie tworzy nieprawidłowej daty. Jeśli biegasz:

(new DateTime(2012, 2, 29)).AddYears(1)

otrzymasz 28 lutego 2013 r. Nie wiem, w jakim języku jest napisany agent gościa platformy Azure, ale musiało to być inne połączenie, które się nie powiodło. Zły sposób na zrobienie tego w .NET to:

new DateTime(today.Year + 1, today.Month, today.Day)

To rzuca wyjątek, jeśli todayjest dzień przestępny. Jednak na blogu firmy Microsoft dotyczącym problemu z platformą Azure podano, że utworzono nieprawidłową datę 29 lutego 2013 r., Co, nie jestem pewien, jest możliwe do zrobienia DateTimew .NET.

Nie mówię tego DateTimei DateTimeOffsetnie są podatne na błędy, po prostu nie sądzę, że spowodowałyby ten konkretny problem.

dcstraw
źródło
Domyślam się, że zostało to zrobione w C ++
PhilPursglove,
2
@PhilPursglove: Dlaczego tak myślisz? System operacyjny Windows został napisany głównie w językach C i C ++ i poprawnie obsługuje dni przestępne. Prawdopodobnie było to bardziej związane z błędem związanym z datą w procesie tworzenia certyfikatu transferu.
In silico
Czy może to być analizowanie daty z ciągu?
Fixer
I tak, masz rację co do AddYears (1), jak wskazałeś. Nie jestem pewien, co się stało wewnętrznie. To była tylko próba nadania pytaniu odrobiny perspektywy, bez zagłębiania się w post na blogu.
Fixer
To ma sens. Szczerze mówiąc, nie wiem, czy tak było, ale jest wiarygodne. Widziałem paskudny kod od dużych firm, w tym MS. Ale znowu, na tym etapie naprawdę nie możemy wiele zrobić, ale spekulować.
Alpha
2

Jak możemy opracować praktyki kodowania mające na celu ochronę przed błędami lat przestępnych? Jakie praktyki kodowania mogły temu zapobiec?

Testy jednostkowe w konkretnych datach, jak wspomniał John, to jedna praktyka kodowania, która pomoże, ale nic nie przebije tego, co określam jako „ręczny test integracji”

zmień zegar na swoim serwerze deweloperskim / testowym i obserwuj, co się stanie, gdy upłynie czas.

Nie ugrzęźnij w szczegółach, czy jest to `` praktyka kodowania '' - Oczywiście nie możesz tego zrobić dla każdej daty w kalendarzu - wybierz daty, które Cię interesują, na przykład 29 lutego, koniec miesiąca daty lub daty zmiany czasu na letni.

wal
źródło