Wie können wir Codierungspraktiken entwickeln, die vor Schaltjahrfehlern schützen sollen? [geschlossen]

80

Microsoft hat gerade angekündigt, dass ein Softwarefehler bei der Berechnung der Daten (über das Schaltjahr) letzte Woche einen großen Ausfall in Windows Azure verursacht hat .

War es wirklich ein einfacher Fehler bei der Beurteilung DateTime.Now.AddYears(1)eines Schaltjahres?

Welche Codierungspraktiken hätten dies verhindern können?

EDIT Wie dcstraw wies darauf hin , DateTime.Now.AddYears(1)auf einem Schaltjahr ist in der Tat das richtige Datum in .NET zurückzukehren. Es handelt sich also nicht um einen Framework-Fehler, sondern offensichtlich um einen Fehler bei der Datumsberechnung.

Fixer
quelle
7
Da es keine einzige Antwort gibt und dies eher eine Diskussionsfrage ist, sollte sie meiner Meinung nach auf Programmierer migriert werden. Außerdem: Immer Unit-Test Ihres Codes. Immer.
George Stocker
2
DateTime-Probleme sind ein knorriges und kompliziertes Problem. Man könnte so etwas wie alles in UTC tun sagen, aber es wird immer einen Punkt der Übersetzung geben ... und wenn das passiert, ist die Anzahl der Permutationen, die Sie berücksichtigen müssten, um alle Fehler zu vermeiden, umwerfend. Die meisten von uns werden niemals an einem System arbeiten, das sich kümmern muss.
Josh
9
Ich denke, diese Frage ist zu interessant, um geschlossen zu werden :-)
Steven
11
VIER Stimmen zum Schließen? Ich hoffe, dass die schlecht gelaunte, auslösende Happy Close-Sache nur eine Phase in der SO-Evolution ist. Es ist alles nur ein bisschen zu heilig als du.
Iain Holder
7
@IainMH muss Ihnen zustimmen, zumal fast alle, die es geschlossen haben, nie eine Frage mit mehr als ein paar Stimmen gestellt haben.
Lloyd

Antworten:

95

Schamloser Stecker:

Verwenden Sie eine bessere Datums- und Uhrzeit-API

Die integrierten .NET-Datums- und Zeitbibliotheken sind fürchterlich schwer zu verwenden. Sie tun können Sie alles tun , was Sie brauchen, aber Sie können nicht ausdrücken , sich klar durch das Typsystem. DateTimeist ein Chaos , DateTimeOffsetkann Sie in den Gedanken wiegen, dass Sie die Zeitzoneninformationen tatsächlich beibehalten, wenn Sie dies nicht tun, und TimeZoneInfozwingt Sie nicht dazu, über alles nachzudenken, was Sie in Betracht ziehen sollten.

Keines davon bietet eine gute Möglichkeit, "nur eine Tageszeit" oder "nur ein Datum" zu sagen, und sie unterscheiden auch nicht klar zwischen "Ortszeit" und "Zeit in einer bestimmten Zeitzone". Und wenn Sie einen anderen Kalender als den Gregorianischen verwenden möchten, müssen Sie Calendardie ganze Zeit durch die Klasse gehen .

All dies ist der Grund, warum ich Noda Time erstelle - eine alternative Datums- und Zeitbibliothek, die auf einem Port der Joda Time "Engine" basiert, aber eine neue (und schlankere) API enthält.

Einige Punkte, über die Sie nachdenken sollten, die leicht zu übersehen sind, wenn Sie sich ihrer nicht bewusst sind:

  • Das Zuordnen eines lokalen Datums / einer lokalen Uhrzeit zu einem Datum in einer bestimmten Zeitzone ist nicht so einfach, wie Sie vielleicht denken. Ein bestimmtes lokales Datum / eine bestimmte lokale Uhrzeit kann aufgrund von Sommerzeitübergängen einmal, zweimal (Mehrdeutigkeit) oder nullmal (übersprungen) auftreten
  • Zeitzonen variieren historisch - mehr als TimeZoneInfoallgemein offengelegt werden kann. (Es wird keine Zeitzone unterstützt, deren Vorstellung von "Standardzeit" sich im Laufe der Zeit ändert oder die in die permanente Sommerzeit übergeht.)
  • Selbst mit der zoneinfo-Datenbank sind Zeitzonen-IDs nicht unbedingt stabil. (CLDR spricht dies an; etwas, das ich hoffentlich in Noda Time unterstützen werde.)
  • Textdarstellungen von Datum und Uhrzeit sind ein Albtraum, nicht nur in Bezug auf die Reihenfolge, sondern auch auf Datums-, Zeit- und seltsame Dinge wie Genitivmonatsnamen
  • Der Beginn des Tages ist nicht immer Mitternacht - in Brasilien beispielsweise bewegt der Sommerzeitübergang im Frühling die Wanduhr von 23:59:59 Uhr auf 1 Uhr morgens
  • In einigen Fällen (eine, die ich kenne) kann eine Zeitzone das Überspringen eines ganzen Tages erzwingen - der 30. Dezember 2011 ist in Samoa nicht aufgetreten! Ich vermute, die meisten Entwickler können diesen wahrscheinlich ignorieren, aber ...
  • Wenn Sie einen anderen Kalender als den Gregorianischen verwenden, seien Sie vorsichtig und stellen Sie sicher, dass Sie wirklich wissen, wie Sie erwarten, dass er sich verhält.

Soweit spezifische Entwicklungspraktiken:

  • Überlegen Sie, was Sie wirklich darstellen möchten. Ich gehe davon aus, dass der Hauptvorteil von Noda Time darin besteht, Entwickler zu zwingen, zwischen verschiedenen Typen zu wählen, um ihre Daten darzustellen. Machen Sie das richtig und alles andere ist einfacher.
  • Unit Test alles, was Sie sich vorstellen können. Das hängt natürlich genau davon ab, was Ihr System tut, berücksichtigt jedoch insbesondere unterschiedliche Zeitzonen, was bei Sommerzeitübergängen passiert und natürlich Schaltjahre.
  • Ich würde empfehlen, eine "uhrähnliche Schnittstelle" einzufügen - einen Dienst zum Anzeigen der aktuellen Uhrzeit - anstatt explizit aufzurufen DateTime.Nowoder DateTime.UtcNow; es macht den Unit-Test einfacher (machbar!)
  • Wenn Sie mehrere Vorgänge mit "jetzt" ausführen, rufen Sie dieses Datum / diese Uhrzeit einmal ab und merken Sie sich dies, anstatt wiederholt "jetzt" anzufordern. Andernfalls kann sich der Wert zwischen den Aufrufen auf unglückliche Weise ändern.
  • "Alles in UTC tun" ist auch nicht immer die Antwort - wenn ich wissen möchte, "wann genau" in zwei Wochen "in meiner lokalen Zeitzone auftritt?" dann muss ich das lokale Datum / die lokale Uhrzeit sowie die Zeitzone speichern .
Jon Skeet
quelle
2
@flq: Auch hier müssten Sie genau definieren, was Sie unter "Schaltjahrsicherheit" verstehen. Ich bezweifle, dass es sich um einen Framework- Fehler handelte, der das Problem in Azure verursacht hat. Ich gehe davon aus, dass das Framework nur unzureichend verwendet wurde .
Jon Skeet
10
Unzählige Aufzählungspunkte ohne einen einzigen zum Thema Schaltjahre (dh zum Thema). Und dann auf Twitter gepustet. Ich denke du hattest bessere Momente, Jon.
Will Dean
8
@ WillDean: Beachten Sie auch, dass die Frage von George Stocker bearbeitet wurde. Der ursprüngliche Titel war „Defensive Programmierung gegen Datetime - Fehler“ - bei dem Fall , dass ich denke , Sie stimmen meine Post ist durchaus relevant. (Ich habe gerade erst bemerkt, dass der Titel geändert wurde. Dort stand DateTime, als ich anfing zu antworten ...)
Jon Skeet
2
Jon, sorry, ich hatte den vorherigen Titel nicht gesehen! Vielleicht sollte es jemandem obliegen, einen Titel zu ändern, um auch alle Antworten zu bearbeiten ...
Will Dean
3
@ WillDean: Ja ... ich bin versucht, entweder die Titeländerung zurückzusetzen oder sie mit "DateTime-Fehlern (z. B. Schaltjahre)" zu beenden
Jon Skeet
25

Es ist erwähnenswert, dass der Fehler wahrscheinlich nicht auf eine Zeile zurückzuführen ist, wie Sie sie gepostet haben:

DateTime.Now.AddYears(1)

Das schafft kein ungültiges Datum. Wenn du läufst:

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

Sie erhalten den 28. Februar 2013. Ich weiß nicht, in was Azures Gastagent geschrieben ist, aber es muss ein anderer Anruf gewesen sein, der fehlgeschlagen ist. Ein schlechter Weg, dies in .NET zu tun, wäre gewesen:

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

Das löst eine Ausnahme aus, wenn todaySchalttag ist. Im Microsoft-Blog zum Azure-Problem wurde jedoch angegeben, dass ein ungültiges Datum vom 29. Februar 2013 erstellt wurde. Ich bin mir nicht sicher, ob dies DateTimein .NET möglich ist.

Ich sage das nicht DateTimeund bin nicht DateTimeOffsetfehleranfällig, nur dass ich nicht glaube, dass sie dieses spezielle Problem verursacht hätten.

dcstraw
quelle
Ich
vermute,
2
@PhilPursglove: Warum denkst du das? Das Windows-Betriebssystem wurde hauptsächlich in C und C ++ geschrieben und behandelt Schalttage ordnungsgemäß. Dies hatte wahrscheinlich eher mit einem datumsbezogenen Fehler beim Erstellen des Übertragungszertifikats zu tun.
In Silico
Könnte es sein, ein Datum aus einer Zeichenfolge zu analysieren?
Fixer
Und ja, Sie haben Recht mit den AddYears (1), wie Sie betont haben. Ich bin mir nicht sicher, was intern passiert ist. Es war nur ein Versuch, der Frage eine gewisse Perspektive zu geben, ohne zu tief in den Blog-Beitrag einzusteigen.
Fixer
Es macht irgendwie Sinn. Ich weiß ehrlich gesagt nicht, ob dies der Fall war, ist aber glaubwürdig. Ich habe bösen Code von großen Unternehmen gesehen, einschließlich MS. Aber auch hier können wir wirklich nicht viel tun, als an dieser Stelle zu spekulieren.
Alpha
2

Wie können wir Codierungspraktiken entwickeln, die vor Schaltjahrfehlern schützen sollen? Welche Codierungspraktiken hätten dies verhindern können?

Das Testen von Daten für bestimmte Daten, wie John erwähnte, ist eine Code-Praxis, die hilft, aber nichts geht über das, was ich als "manuellen Integrationstest" definiere.

Ändern Sie die Uhr auf Ihrem Entwicklungs- / Testbed-Server und beobachten Sie, was passiert, wenn die Zeit abläuft.

Machen Sie sich keine Gedanken darüber, ob dies eine „Codierungspraxis“ ist. Natürlich können Sie dies nicht für jedes Datum im Kalender tun. Wählen Sie die Daten aus, mit denen Sie sich befassen, sei es am 29. Februar, Ende des Monats Daten oder Sommerzeit-Umstellungstermine.

wal
quelle