paint-brush
Schaltjahre neu denken: Warum der Ansatz Ihrer bevorzugten Programmiersprache möglicherweise fehlerhaft istby@mcsee
1,043
1,043

Schaltjahre neu denken: Warum der Ansatz Ihrer bevorzugten Programmiersprache möglicherweise fehlerhaft ist

Die meisten Sprachen finden nicht das richtige Verhalten für die Berechnung von Schaltjahren
featured image - Schaltjahre neu denken: Warum der Ansatz Ihrer bevorzugten Programmiersprache möglicherweise fehlerhaft ist
Maximiliano Contieri HackerNoon profile picture
0-item

Ein historischer Fehler und wie Sie ihn lösen können


TL;DR: Die meisten Sprachen finden nicht das richtige Verhalten für die Berechnung von Schaltjahren.


Haftungsausschluss: Obwohl ich mein Bestes gegeben habe, um genaue Einblicke in verschiedene Programmiersprachen zu geben, gebe ich zu, dass ich möglicherweise nicht in allen Bereichen ein Experte bin. Wenn Sie einen Fehler entdecken oder mit einem Punkt nicht einverstanden sind, hinterlassen Sie bitte einen respektvollen Kommentar und ich werde mich umgehend darum kümmern.

Der Stand der Technik

Zu bestimmen, ob ein Jahr ein Sprung ist (oder nicht), ist ein einfaches mathematisches Problem.

Jeder Schüler kann es als erste Programmieraufgabe lösen.

Um das Problem zu vereinfachen, gehen wir davon aus, dass ein Jahr ein Schaltjahr ist, wenn es gleichmäßig durch 4 teilbar ist, es sei denn, es ist auch durch 100 teilbar, aber es ist ein Schaltjahr, wenn es durch 400 teilbar ist.

Die reale Welt und die kosmische Mechanik sind etwas komplizierter, aber das würde den Rahmen dieses Artikels sprengen.

Lassen Sie uns untersuchen, wie verschiedene Programmiersprachen dieses Problem lösen:

Schrecklicher Ansatz

PHP:

 <?php $yearNumber = 2024; $isLeap = date('L', mktime(0, 0, 0, 1, 1, $yearNumber));

SQL (PostgreSQL):

 SELECT (EXTRACT(year FROM TIMESTAMP '2024-02-29') IS NOT NULL) AS is_leap_year;

Diese Sprachen versuchen, einen gültigen (oder ungültigen) Schalttag zu erstellen und wahrheitsgemäße Werte auszunutzen.

Dieser Hack verstößt gegen das Fail-Fast-Prinzip und missbraucht den Milliardenfehler .

Der Versuch, ein ungültiges Datum zu erstellen, sollte in seriösen Sprachen eine Ausnahme auslösen, da dies in der realen Domäne geschieht.

Die Durchführung anderer Handlungen, etwa das Verbergen von Fehlern unter der Oberfläche, verstößt gegen das Prinzip der geringsten Überraschung.

Fehlendes Verhalten

Ada:

 function Is_Leap_Year (Year : Integer) return Boolean is begin return (Year mod 4 = 0 and then Year mod 100 /= 0) or else (Year mod 400 = 0); end Is_Leap_Year;

C/C++:

 bool isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }

Gehen:

 package main import ( "fmt" "time" ) func isLeapYear(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) }

Haskell:

 import Data.Time.Calendar (isLeapYear) let year = 2024 let isLeap = isLeapYear year

JavaScript/TypeScript:

 function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); }

Julia:

 using Dates year = 2024 isleap(year)

Lua:

 local year = 2024 local isLeap = (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)

MATLAB:

 year = 2024; isLeap = mod(year, 4) == 0 && (mod(year, 100) ~= 0 || mod(year, 400) == 0);

Ziel c:

 int yearNumber = 2024; BOOL isLeap = (yearNumber % 4 == 0 && yearNumber % 100 != 0) || (yearNumber % 400 == 0);

Power Shell:

 $yearNumber = 2024 $isLeap = ($yearNumber % 4 -eq 0 -and $yearNumber % 100 -ne 0) -or ($yearNumber % 400 -eq 0)

Rost:

 fn is_leap_year(year: i32) -> bool { (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) }

Smalltalk:

 | yearNumber | yearNumber := 2024. (yearNumber \\ 4 = 0) and: [(yearNumber \\ 100 ~= 0) or: [ yearNumber \\ 400 = 0 ]]

Die oben genannten Sprachen bieten keine native Unterstützung.

Sie müssen globale Funktionen definieren oder Hilfsfunktionen verwenden.

Falscher globaler Ansatz

PHP (wieder):

 <?php $yearNumber = 2024; $isLeap = checkdate(2, 29, $yearNumber);

R:

 leap_year(2024)

Rubin:

 year = 2024 is_leap = Date.leap?(year)

Schnell:

 let yearNumber = 2024 let isLeap = Calendar.current.isDateInLeapYear( Date(timeIntervalSince1970: TimeInterval(yearNumber)))

Diese Sprachen verwenden globale Funktionen , um zu prüfen, ob ein Jahr ein Schaltjahr ist.

Diese globalen Dienstprogrammmethoden verlagern die Verantwortung fälschlicherweise an den falschen Ort (einen globalen Zugriffspunkt).

Helfer schlechter Ansatz

C#:

 int yearNumber = 2024; bool isLeap = System.DateTime.IsLeapYear(yearNumber);

Pfeil:

 import 'package:intl/intl.dart'; var year = 2024; var isLeap = DateTime(year).isLeapYear;

Perl:

 use Time::Piece; my $yearNumber = 2024; my $isLeap = Time::Piece ->strptime("$yearNumber-01-01", "%Y-%m-%d")->leapyear;

Python:

 import calendar leap = calendar.isleap(2024)

Visual Basic .NET:

 Dim year As Integer = 2024 Dim isLeap As Boolean = DateTime.IsLeapYear(year)

Diese Sprachen verwenden Hilfsprogramme als Bibliotheken, um zu prüfen, ob ein Jahr ein Sprung ist.

Die verlorene Verantwortung liegt nicht in einem realen Objekt vor, sondern in einer Reihe von DateTime-bezogenen Funktionen .

Der Jahresansatz

Java:

 int yearNumber = 2024; boolean isLeap = java.time.Year.of(yearNumber).isLeap();

Kotlin:

 val yearNumber = 2024 val isLeap = java.time.Year.of(yearNumber).isLeap

Scala:

 val year = 2024 val isLeap = java.time.Year.of(year).isLeap

Diese Sprachen verlassen sich auf das Jahr , um zu prüfen, ob es sich um einen Sprung handelt.

Das Protokoll ist in der Bijektion näher an der realen Welt

Beachten Sie, dass sie Year- Objekte und keine Integer- Objekte erstellen, da dies auch die Bijektion zerstören würde.

Ein Jahr hat ein anderes Protokoll als eine Ganzzahl, und die Modellierung eines Jahres als Ganzzahl wäre ebenfalls ein verfrühter Optimierungsgeruch und ein Symptom für die Vermischung von „was“ und „wie “.

Ein Jahr kann erkennen, ob es sich um einen Sprung handelt (eine Ganzzahl sollte das nicht tun), und kann Ihnen Auskunft über seine Monate geben (bei denen es sich um Months handelt, nicht um 0-basierte Ganzzahlen, 1-basierte Ganzzahlen oder Zeichenfolgen).

Umgekehrt erstrecken sich die Fähigkeiten einer Ganzzahl auf arithmetische Operationen wie Multiplikation und Potenzierung.

Zeit ist kein Scherz

Die Darstellung eines Zeitpunkts als Float , Integer oder einen anderen Datentyp hat Konsequenzen.

Sie können einen Zeitpunkt in der realen Welt in winzige Bruchteile aufteilen (aber nicht zu klein ).

Die Verwendung von Floats ist keine gültige Option.

0,01 + 0,02 ist nicht 0,03, und das hat schreckliche Konsequenzen für den Umgang mit Gleitkommapunkten in der Zeit.

Die Herausforderung

Wir haben über Schaltjahre gesprochen.

Was muss man wissen, wenn ein Jahr ein Sprung ist?

Die von Ihnen modellierten Datums- und Uhrzeitmechanismen müssen den Nachfolger vom 28. Februar 2024 kennen.

Aber das ist NICHT Ihr Problem.

Nach dem Prinzip des Verbergens von Informationen sollten Sie die Verantwortung als privates Protokoll belassen.

Abschluss

Es gibt keine Wunderwaffe .

Verwenden Sie Ihre Sprache mit Bedacht.

Heute ist der 29. Februar, ein Schalttag, um innezuhalten und über die Tools nachzudenken, die Sie täglich verwenden.

Wir sehen uns in 4 Jahren.