AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Alter fehlerfrei errechnen

Alter fehlerfrei errechnen

Ein Tutorial von fkerber · begonnen am 23. Jan 2010
Antwort Antwort
Benutzerbild von fkerber
fkerber
Registriert seit: 9. Jul 2003
Himitsu, Sir Rufo und Wolfgang Mix zeigen in diesem Tutorial, wie man das Alter einer Sache oder einer Person fehlerfrei bestimmen kann.

In Datenbanken kommt es häufig vor, dass man zu einer Person das Alter bestimmen muss.

Die Funktionen von (nicht nur) Delphi Delphi-Referenz durchsuchenYearsBetween und Delphi-Referenz durchsuchenYearSpan liefern schaltjahresabhängig richtige oder falsche Werte, für die Zeitspanne z.B '01.01.2009' und '01.01.2010' falsche Werte, wie Sir Rufo kürzlich gezeigt hat.
Das wird auch klar, wenn man sich die OH von Delphi anschaut:

Zitat:
.. gibt YearsBetween als auch YearSpan eine Näherung zurück, die auf einem Wert von 365,25 Tagen pro Jahr basiert ...
Das ist natürlich völlig inakzeptabel, da es bei Fehlern zur Feststellung von z.B. Volljährigkeit oder Ablauf einer Gewährleistungsfrist zu rechtlichen und finanziellen Problemen führen kann.

Wenn es um die Bestimmung von ganzzahligen Jahren zwischen 2 Daten geht, hilft nachstehend beschriebener Pseudo-Algorithmus von Wolfgang Mix weiter, der plattformunabhängig fehlerfreie Rückgabewerte liefert.
Alternativ kann man auch mit der neu codierten Funktion von YearsBetween von himitsu und Sir Rufo ermitteln. (siehe unten)

Wer mit Jahresteilen mit Jahr >= 1 weiterrechnen muss, ist mit himitsus und Sir Rufos Code bestens bedient. Sie zeigen eindrucksvoll, dass ein bisschen mehr Code erforderlich ist, wenn man mit YearsBetween korrekt rechnen will.


Der Algorithmus, den Wolfgang Mix hier vorstellt, lässt sich am einfachsten ohne PC am Schreibtisch begreifen:

Man nehme ein Blatt Papier und einen Stift und mache folgendes:

1. Schreibe das heutige Datum im Format YYYYMMDD (ohne Trennzeichen) auf.
2. Schreibe darunter Deinen Geburtstag im selben Format.
3. Bilde die Differenz dieser beiden LongInt-Zahlen.
4. Die Ziffern 1-4 (meist nur 3 und 4) ergeben das Alter in ganzen Jahren.
Führende Nullen weglassen!

Dieser Algorithmus funktioniert plattformunabhängig in jeder Programmier-Sprache und wurde von Wolfgang Mix erstmals 2/1992 in DOS International (heute PC-Magazin) vorgestellt, ist aber im WEB nicht mehr greifbar gewesen.

Beispiel 1:
Das Systemdatum sei 24.1.2010 und mein Geburtstag 25.1.1950

1. 20100124
2. 19500125
________
3./4. 00599999 Ziffern 3 und 4 ergeben das Alter: 59

Beispiel 2: Einen Tag später ergibt sich folgendes:

1. 20100125
2. 19500125
________
3./4. 00600000 Ziffern 3 und 4 ergeben das Alter: 60

Vorteil des Verfahrens:
In den meisten Programmiersprachen ist dieser Pseudocode als Einzeiler codierbar... Außerdem braucht mach sich keine Gedanken um Schaltjahresregeln zu machen. Das Ganze funktioniert sogar, wenn etwas älter als 1000 Jahre wird.

Zu beachten ist allerdings, dass ein Datum zuerst in seine Einzelkomponenten zerlegt werden muss oder in diesen vorliegen muss, damit das Verfahren angewendet werden kann.

Zur Umsetzung in Delphi-Code werden hier ein paar Beispiele angefügt, die hier in der DP entstanden sind.
Delphi-Quellcode:
//DeddyH – Delphi - PRAXiS
function Age(BirthDate: TDate): integer;
var y1,y2,m1,m2,d1,d2: Word;
begin
  SysUtils.DecodeDate(date,y1,m1,d1);
  SysUtils.DecodeDate(BirthDate,y2,m2,d2);
  Result := ((y1 * 10000 + m1 * 100 + d1) - (y2 * 10000 + m2 * 100 + d2)) div 10000;
end;

Für Datenbankanwendungen hat Omata folgende Lösung zur Umsetzung des Pseudocodes beigetragen:

SQL-Code:
SELECT ( ( YEAR(GETDATE()) * 10000
           + MONTH(GETDATE()) * 100
           + DAY(GETDATE()))
        - ( YEAR(geburtsdatum) * 10000
           + MONTH(geburtsdatum) * 100
           + DAY(geburtsdatum))) / 10000 AS "alter"
FROM tabelle

MySQL:

SQL-Code:
SELECT ( ( YEAR(CURRENT_DATE()) * 10000
           + MONTH(CURRENT_DATE()) * 100
           + DAY(CURRENT_DATE()))
        - ( YEAR(geburtsdatum) * 10000
           + MONTH(geburtsdatum) * 100
           + DAY(geburtsdatum))) DIV 10000 AS "alter"
FROM tabelle

Firebird:

SQL-Code:
SELECT ( ( EXTRACT(YEAR FROM CURRENT_DATE) * 10000
           + EXTRACT(MONTH FROM CURRENT_DATE) * 100
           + EXTRACT(DAY FROM CURRENT_DATE))
        - ( EXTRACT(YEAR FROM geburtsdatum) * 10000
           + EXTRACT(MONTH FROM geburtsdatum) * 100
           + EXTRACT(DAY FROM geburtsdatum))) / 10000 AS "alter"
FROM tabelle

Shmia zeigt hier, wie man Aufgabe für einen binär rechnenden Computer optimiert.

Delphi-Quellcode:
function Age(BirthDate: TDate): integer;
var y1,y2,m1,m2,d1,d2: Word;
begin
  SysUtils.DecodeDate(date,y1,m1,d1);
  SysUtils.DecodeDate(BirthDate,y2,m2,d2);
{
  Result := ((y1 * 10000 + m1 * 100 + d1)
          - (y2 * 10000 + m2 * 100 + d2)) div 10000;
}
 
  Result := (((y1 shl 4 or m1) shl 5 or d1)
           - ((y2 shl 4 or m2) shl 5 or d2)) shr 9;
end;
Erklärung:
ein Monat hat maximal 31 Tage - also darf man auch mit 32 anstatt mit 100 multiplizieren.
Das entspricht einem Linksshift um 5 Bits.
Ein Jahr hat 12 Monate. Also darf man auch mit 16 multiplizieren.
Das entspricht einem Linksshift um 4 Bits.
Der Rechtsshift um 9 macht alles wieder rückgängig und lässt dabei den Rest wie bei einer Division verschwinden

Himitsu und Sir Rufo haben die Funktion YearsBetween neu programmiert, damit man nicht nur mit ganzen Jahren, sondern auch mit Jahresteilen richtig rechnen kann:


Delphi-Quellcode:
//himitsu - Delphi-PRAXiS
// --------------------------------------------------------------------------
// Tauscht die übergebenen Datumsangaben, damit ANow <= AThen
// --------------------------------------------------------------------------
procedure CheckedSwap( Var ANow, AThen : TDateTime ); Inline;
  Var
    Temp : TDateTime;
  Begin
    If ANow <= AThen Then
      Exit;
    Temp := ANow;
    ANow := AThen;
    AThen := Temp;
  End;

///himitsu und Sir Rufo - Delphi - PRAXiS
// --------------------------------------------------------------------------
// Ermittelt die Jahre zwischen zwei Datumswerten
// --------------------------------------------------------------------------
function YearsBetween(ANow: TDateTime; AThen: TDateTime): Integer;
var Yn, Yt, Mn, Mt, Dn, Dt: Word;
  Begin
    CheckedSwap(ANow, AThen);
    DecodeDate(ANow, Yn, Mn, Dn);
    DecodeDate(AThen, Yt, Mt, Dt);
    Result := Yt - Yn;
    If (Mt < Mn) or ((Mt = Mn) and ((Dt < Dn))) Then Dec(Result);
  End;

Beispiel 1:
AThen = 25.08.2002 > ANow = 21.08.2008
Result = 2008 - 2002
Result = 6 Jahre (+4 Tage)

Anders herum:
AThen = 21.08.2002 > ANow = 25.08.2008
Result = 2008 - 2002
Result = 6 Jahre (-4 Tage)

8=8 und 21<25 ... also Monat ist gleich und Tag ist kleiner
bedeutet also, daß zwischen 25.08. und 21.08. kein ganzes Jahr liegt, weswegen dieses unvollständige Jahr vom Ergebnis abgezogen wird

if ... or ((8 = 8) and (21 < 25)) then Result := Result - 1;
Result = 5 ganze Jahre

Dieses fällt ganz besonders bei extremeren Werten auf.

Beispiel 2:
ANow = 1.12.2002 > AThen = 02.01.2004
Result = 2004 - 2002 = 2
if (1 < 12) or ... then Result := Result - 1;
Result = nur 1 ganzes Jahr Unterschied (genauer gesagt 1 Jahr und 2 Tage)

Beispiel 3:
Delphis YearsBetween macht Fehler z.B für
ANow = 01.01.2009
AThen = 01.01.2010
Result = 0 Jahre, richtig wäre 1 Jahr
 
Themen-Optionen Tutorial durchsuchen
Tutorial durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +2. Es ist jetzt 15:23 Uhr.
Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf