Altersberechnung - Extended!

ManicMarble hat gesagt.:
Also wie gesagt, ich habe das nicht wirklich zu Ende gedacht, war nur so ein Ansatz.

Die komischen Zeitangaben im Ergebnis kommen daher, dass mktime() eigentlich einen Zeitpunk (Timestamp) zurückliefert und eben keine Zeitspanne (Timeslice). Sprich: Das Ergebnis ist eigentlich ein Datum mit Uhrzeit - in Deinem Beispiel (Ergebnis "18 Jahre, 12 Monate, 29 Tage, 0 Stunden, 13 Minuten, 13 Sekunden") ist das der 29.12. des Jahres 18 (n.Chr.), 00:13:13 Uhr.

Das mit dem Jahr kann man so also nicht berechnen, da ja 18 n.Chr. deutlich vor dem 1.1.1970 liegt :).
Deshalb habe ich das Jahr außen vor gelassen und berechne das später extra.

Da bei der mktime()-Berechnung (also $alter) theoretisch auch das Vorjahr rauskommen kann (3.3.2005 minus 3 Monate = 3.12.2004) muss in diesen Fällen diese Differenz noch bei der Jahres-Berechnung berücksichtigt werden. Deshalb
date('Y') - $geburtsjahr - (date('Y') - date('Y', $alter))
enspricht im o.g. Beispiel
2005 - 1986 - (2005 - 2004) = 2005 - 1986 - 1 = 18

Wenn Du den Ansatz mal zu Ende denken willst, muss Dir klar sein, wie mktime() arbeitet. Bei dem Alter Deiner Mutter sähe das in Etwa so aus:
$alter = mktime(13 - 0, 44 - 0, 20 - 0, 4 - 7, 1 - 20, 2005, -1);
entspricht
$alter = mktime(13, 44, 20, -3, -19, 2005, -1);
mktime() ist angeblich so schlau, aus den negativen Zahlen wieder ein korrektes Datum zu machen. Funktioniert für Timestamps AFAIK eigentlich wunderbar, wenn man's aber als Timeslice missbraucht, bin ich mir nicht mehr so sicher. Ist dann wohl doch so, dass das nicht richtig klappt... :(

Tja, dann muss es halt doch irgendwie anders gehen. Vielleicht tüftel ich mal noch ein bischen dran rum, jetzt muss ich aber erst mal wieder das machen, wofür ich hier bezahlt werde...;)

Viel Spaß noch
Martin

Also jetzt gibt's bei mir wirklich den Overkill - SYSTEM OVERLOADED, DEADLOCK REGISTERED! ;) :D

Okay, ich versuch das wirklich in mein Hirn reinzukriegen und obwohl ich mich schon als "anfangenden Fortgeschrittenen" bezüglich meiner PHP-Fähigkeiten betrachte, blick ich grad GAR NIX bei deinen Erklärungen.

Nun kann ich dich wohl kaum dazu überreden, es mir so zu erklären, dass auch mein dummes Hirn es begreift, doch mehr als den Unterschied zwischen Zeitspanne (was ich brauche) und Zeitpunkt (was mktime mir liefert) peil ich einfach nicht.

Deine Berechnung ist irgendwie zu hoch für mich, oder ich bin einfach zu blöd dafür... :(
Weil ich blick nicht ganz, was du im Jahr 18 u. Z. willst, wenn ich in 1986 bzw. 1963 bzw. 2005 bin... :(

Ich hoffe, du kannst es mir so erklären, dass auch ein Doofy wie ich das gepeilt kriegt... ;) :)

BtW: Was für 'ne Tätigkeit bei tutorials.de wird bitte bezahlt :D :D :D
 
:D
Wer sagt denn, dass ich bei Tutorials mein Geld verdiene?
Wollte nur andeuten, dass ich eigentlich was arbeiten müsste, anstatt hier im Forum Spaß zu haben...
Da ich aber nun durch meine unqualifizierten Ergüsse einen aufstrebenden Jung-Coder völlig verwirrt habe, stehe ich wohl in der Pflicht, das wieder hinzubiegen. Gruß an meinen Chef: Tut mir leid, das hier ist wichtig! ;)

Also, ich versuch's nochmal mit einer Erklärung:

Die Funktion mktime() liefert einen Unix-Timestamp, also die Anzahl der verstrichenen Sekunden seit dem 1.1.1970. Jetzt gerade eben hatten wir den Unix-Timestamp 1112364125, also 1 Milliarde 112 Millionen 364 Tausend und 125 Sekunden.

Mein Lösungsansatz basierte darauf, mit dem Unix-Timestamp eine Zeitspanne darzustellen. Theoretisch müsste das auch möglich sein, aber nur, wenn man eine Zeitspanne darstellen will, die mindestens 1970 und maximal 2036 (oder so) Jahre umfasst. Da nur wenige Lebewesen so alt werden, eignet sich dieser Lösungsansatz nicht zur Altersberechnung.
Ich habe dann versucht, das zu umgehen, indem ich die Berechnung der Jahre separat veranstaltet habe. War aber wohl zu kurz gedacht - der Unix-Timestamp eignet sich nicht für Timeslice-Berechnungen - was Du ja bewiesen hast.

(Die Sache mit dem "Jahr 18 n.Chr." war mein verunglückter Versuch, dies zu verdeutlichen: Als Ergebnis der Berechnung käme eine Zeitspanne mit irgendwas über 18 Jahren raus, als Zeitpunkt dargestellt also irgend ein Datum im Jahr 18 und das ist als Unix-Timestamp nicht darstellbar).

Vergiss also diesen Lösungsansatz. So geht es nicht - zumindest nicht so einfach.

Die Problematik steckt in der menschlichen Denkweise (und ich meine damit Menschen, nicht Programmierer ;)). Was genau meint jemand, wenn er sagt: "Ich bin 37 Jahre, 6 Monate und 16 Tage alt."? Matthias hat natürlich recht: Das ist nicht exakt. Exakt ist es erst, wenn derjenige dazusagt, welche 6 Monate. Sagt er aber nicht, er ist ja kein Computer. Das macht die Sache ja so interessant.
Also: Was exakt meint der Mensch im allgemeinen Sprachgebrauch mit "6 Monate"? Ich würde sagen, er meint "6 Monatswechsel.

Das nächste Problem wären die Tage:
Nehmen wir an, jemand hat am 2. Oktober Geburtstag und heute wäre der der 27. April. Von Oktober bis April sind es 6 Monatswechsel. Also ist der Mensch x Jahre und 6 Monate alt.
Jetzt die Tage: Vom 2. Oktober bis Monatsende sind es 29 Tage. Vom 1. bis zum 27. April sind es 26 Tage (der 27. April selbst zählt nicht, der ist noch nicht vollständig). 29+26=55. Der Mensch ist also x Jahre, 6 Monate und 55 Tage alt. Hm. Also doch 7 Monate? Ja, ok. Dann müssen wir bei den 55 Tagen aber einen Monat abziehen. Bloß welchen? Den März (31 Tage)? Oder den November (30 Tage)? Oder einen Mittelwert (30.5 Tage)? Puh! So kommen wir IMHO nicht weiter.

Also: Völlig anderer Ansatz.
Berechne zunächst das Alter in Sekunden. Dann teile durch (365.25*24*60*60), das ist die durchschnittliche Anzahl Tage pro Jahr (nicht ganz exakt, eigentlich 365.241, aber das lassen wir jetzt mal gut sein) in Sekunden. Abgerundet auf eine ganze Zahl ist das das Alter in Jahren.
Den Rest teilst Du durch (30.44*24*60*60), die durchschnittliche Anzahl Tage pro Monat in Sekunden, das sind die Monate. Wiederum der Rest wird geteilt durch (24*60*60), das sind die Tage, den Rest durch 3600 (die Stunden), Rest durch 60 (Minuten) und das was bleibt sind die Sekunden. Das müsste bei allen Menschen unter 35 funktionieren und zumindest "mathematisch" annähernd exakt sein.
Bei Deiner Mutter (und bei mir) klappt das nicht, wir wurden vor Unix geboren. Für die "Generation X" muss man also das Alter in Sekunden "von Hand" ausrechnen. Ich mache das mal der Einfachkeit halber nur näherungsweise:
PHP:
$geburtsjahr = 1963;
$geburtsmonat = 7;
$geburtstag = 20;
$geburtsstunde = 13;
$geburtsminute = 0;
$geburtssekunde = 0;

if ($geburtsjahr < 1970) {
	$sekundenVor1970 = (1970 - $geburtsjahr) * 365.25 * 24 * 60 *60;
	$alterInSek = date('U') - mktime($geburtsstunde, $geburtsminute, $geburtssekunde, $geburtsmonat, $geburtstag, 1970, -1);
	$alterInSek += $sekundenVor1970;
} else {
	$alterInSek = date('U') - mktime($geburtsstunde, $geburtsminute, $geburtssekunde, $geburtsmonat, $geburtstag, $geburtsjahr, -1);
}

$alterjahre = floor($alterInSek / (365.25*24*60*60));
$rest = $alterInSek % (365.25*24*60*60);

$altermonate = floor($rest / (30.44*24*60*60));
$rest = $rest % (30.44*24*60*60);

$altertage = floor($rest / (24*60*60));
$rest = $rest % (24*60*60);

$alterstunden = floor($rest / (60*60));
$rest = $rest % (60*60);

$alterminuten = floor($rest / 60);

$altersekunden = $rest % 60;

echo 'Alter am '.date('d.m.Y').' um '.date('H:i:s').' Uhr: <br>';
echo $alterInSek . ' Sekunden<br>';
echo "$alterjahre Jahre, $altermonate Monate, $altertage Tage, $alterstunden Stunden, $alterminuten Minuten, $altersekunden Sekunden.";
Nach menschlicher Denkweise ist das Ergebnis allerdings nicht befriedigend. Also eigentlich wieder nix. Das Ganze wird also schwieriger als man so auf den ersten Blick meinen würde. :)

Ich hoffe, ich habe Dich jetzt nicht erst recht und endgültig verwirrt. Du siehts ja auch, dass es kein spezifisches PHP-Problem ist, sondern ein ganz allgemein programmiertechnisches (wenn nicht gar ein philosophisches ;)). Aber netter Denksport...

Martin
 
ManicMarble hat gesagt.:
:D
Wer sagt denn, dass ich bei Tutorials mein Geld verdiene?
Wollte nur andeuten, dass ich eigentlich was arbeiten müsste, anstatt hier im Forum Spaß zu haben...
Da ich aber nun durch meine unqualifizierten Ergüsse einen aufstrebenden Jung-Coder völlig verwirrt habe, stehe ich wohl in der Pflicht, das wieder hinzubiegen. Gruß an meinen Chef: Tut mir leid, das hier ist wichtig! ;)

Also, ich versuch's nochmal mit einer Erklärung:

Die Funktion mktime() liefert einen Unix-Timestamp, also die Anzahl der verstrichenen Sekunden seit dem 1.1.1970. Jetzt gerade eben hatten wir den Unix-Timestamp 1112364125, also 1 Milliarde 112 Millionen 364 Tausend und 125 Sekunden.

Mein Lösungsansatz basierte darauf, mit dem Unix-Timestamp eine Zeitspanne darzustellen. Theoretisch müsste das auch möglich sein, aber nur, wenn man eine Zeitspanne darstellen will, die mindestens 1970 und maximal 2036 (oder so) Jahre umfasst. Da nur wenige Lebewesen so alt werden, eignet sich dieser Lösungsansatz nicht zur Altersberechnung.
Ich habe dann versucht, das zu umgehen, indem ich die Berechnung der Jahre separat veranstaltet habe. War aber wohl zu kurz gedacht - der Unix-Timestamp eignet sich nicht für Timeslice-Berechnungen - was Du ja bewiesen hast.

(Die Sache mit dem "Jahr 18 n.Chr." war mein verunglückter Versuch, dies zu verdeutlichen: Als Ergebnis der Berechnung käme eine Zeitspanne mit irgendwas über 18 Jahren raus, als Zeitpunkt dargestellt also irgend ein Datum im Jahr 18 und das ist als Unix-Timestamp nicht darstellbar).

Vergiss also diesen Lösungsansatz. So geht es nicht - zumindest nicht so einfach.

Die Problematik steckt in der menschlichen Denkweise (und ich meine damit Menschen, nicht Programmierer ;)). Was genau meint jemand, wenn er sagt: "Ich bin 37 Jahre, 6 Monate und 16 Tage alt."? Matthias hat natürlich recht: Das ist nicht exakt. Exakt ist es erst, wenn derjenige dazusagt, welche 6 Monate. Sagt er aber nicht, er ist ja kein Computer. Das macht die Sache ja so interessant.
Also: Was exakt meint der Mensch im allgemeinen Sprachgebrauch mit "6 Monate"? Ich würde sagen, er meint "6 Monatswechsel.

Das nächste Problem wären die Tage:
Nehmen wir an, jemand hat am 2. Oktober Geburtstag und heute wäre der der 27. April. Von Oktober bis April sind es 6 Monatswechsel. Also ist der Mensch x Jahre und 6 Monate alt.
Jetzt die Tage: Vom 2. Oktober bis Monatsende sind es 29 Tage. Vom 1. bis zum 27. April sind es 26 Tage (der 27. April selbst zählt nicht, der ist noch nicht vollständig). 29+26=55. Der Mensch ist also x Jahre, 6 Monate und 55 Tage alt. Hm. Also doch 7 Monate? Ja, ok. Dann müssen wir bei den 55 Tagen aber einen Monat abziehen. Bloß welchen? Den März (31 Tage)? Oder den November (30 Tage)? Oder einen Mittelwert (30.5 Tage)? Puh! So kommen wir IMHO nicht weiter.

Also: Völlig anderer Ansatz.
Berechne zunächst das Alter in Sekunden. Dann teile durch (365.25*24*60*60), das ist die durchschnittliche Anzahl Tage pro Jahr (nicht ganz exakt, eigentlich 365.241, aber das lassen wir jetzt mal gut sein) in Sekunden. Abgerundet auf eine ganze Zahl ist das das Alter in Jahren.
Den Rest teilst Du durch (30.44*24*60*60), die durchschnittliche Anzahl Tage pro Monat in Sekunden, das sind die Monate. Wiederum der Rest wird geteilt durch (24*60*60), das sind die Tage, den Rest durch 3600 (die Stunden), Rest durch 60 (Minuten) und das was bleibt sind die Sekunden. Das müsste bei allen Menschen unter 35 funktionieren und zumindest "mathematisch" annähernd exakt sein.
Bei Deiner Mutter (und bei mir) klappt das nicht, wir wurden vor Unix geboren. Für die "Generation X" muss man also das Alter in Sekunden "von Hand" ausrechnen. Ich mache das mal der Einfachkeit halber nur näherungsweise:
PHP:
$geburtsjahr = 1963;
 $geburtsmonat = 7;
 $geburtstag = 20;
 $geburtsstunde = 13;
 $geburtsminute = 0;
 $geburtssekunde = 0;
 
 if ($geburtsjahr < 1970) {
 	$sekundenVor1970 = (1970 - $geburtsjahr) * 365.25 * 24 * 60 *60;
 	$alterInSek = date('U') - mktime($geburtsstunde, $geburtsminute, $geburtssekunde, $geburtsmonat, $geburtstag, 1970, -1);
 	$alterInSek += $sekundenVor1970;
 } else {
 	$alterInSek = date('U') - mktime($geburtsstunde, $geburtsminute, $geburtssekunde, $geburtsmonat, $geburtstag, $geburtsjahr, -1);
 }
 
 $alterjahre = floor($alterInSek / (365.25*24*60*60));
 $rest = $alterInSek % (365.25*24*60*60);
 
 $altermonate = floor($rest / (30.44*24*60*60));
 $rest = $rest % (30.44*24*60*60);
 
 $altertage = floor($rest / (24*60*60));
 $rest = $rest % (24*60*60);
 
 $alterstunden = floor($rest / (60*60));
 $rest = $rest % (60*60);
 
 $alterminuten = floor($rest / 60);
 
 $altersekunden = $rest % 60;
 
 echo 'Alter am '.date('d.m.Y').' um '.date('H:i:s').' Uhr: <br>';
 echo $alterInSek . ' Sekunden<br>';
 echo "$alterjahre Jahre, $altermonate Monate, $altertage Tage, $alterstunden Stunden, $alterminuten Minuten, $altersekunden Sekunden.";
Nach menschlicher Denkweise ist das Ergebnis allerdings nicht befriedigend. Also eigentlich wieder nix. Das Ganze wird also schwieriger als man so auf den ersten Blick meinen würde. :)

Ich hoffe, ich habe Dich jetzt nicht erst recht und endgültig verwirrt. Du siehts ja auch, dass es kein spezifisches PHP-Problem ist, sondern ein ganz allgemein programmiertechnisches (wenn nicht gar ein philosophisches ;)). Aber netter Denksport...

Martin

Zuerst einmal möchte ich mich entschuldigen, weil die Antwort ja doch recht spät kommt - allerdings war die Badewanne (und der mütterliche Druck ;) :D) stärker und dann kam "Genial Daneben" und ... - Ach, ich hab's einfach verrissen, sorry!

Aufstrebender Jung-Coder? Ich? - Nein, nein, das bin ich doch nicht! ;) :D
(Wenn's glatt geht und ich das Assessment-Center nächsten Samstag bestehe, bin ich ab Oktober Berufsakademie-Student für Software-Entwicklung, aber Jung-Coder? Aufstrebend? - Nene, das ist 'n Missverständnis! ;) :D :D)

Okay, jetzt versteh ich (glaub ich zumindest so halbwegs ;)) dein "Beispiel" mit der "18 u. Z."-Angabe... ;) :D

Warum du nun Monatswechsel als Monatsangabe verwenden willst, blick ich nicht so ganz - aber ich denke mal, es ist das gleiche wie ich es eigentlich wollte: bei mir war 1 Monat = Zeit zwischen dem 03. des Vormonats und dem 03. des aktuellen Monats.

Deinen Denkansatz ("Jetzt die Tage: Vom 2. Oktober bis Monatsende sind es 29 Tage.") geht mir völlig ab - wozu brauchst du die Anzahl der Tage vom 02. Oktober bis zum Ende vom Monat Oktober - Und vor allem: diese 29 Tage liegen doch NACH dem gewünschten Tag (02. Oktober, wenn ich dein Beispiel richtig verstehe...), aber du addierst die 29 Tage zu den 26 Tagen vom Rest-April hinzu

Und zu deinem völlig neuen Ansatz: warum machst du's da nur nährungsweise und nicht auch exakt? Wär' das zuviel Aufwand? - Ich will dich ja nicht unnötig stressen, schließlich ist das hier alles völlig voluntär von dir geleistet und dafür dank ich dir auch SEHR! :)


Martin... :D
 
öööhm, erklärt mich für dumm, aber wieso nutzt du nicht [phpf]gmdate[/phpf], um die gewünschten Werte anzugeben?

mfg
 
meilon hat gesagt.:
öööhm, erklärt mich für dumm, aber wieso nutzt du nicht [phpf]gmdate[/phpf], um die gewünschten Werte anzugeben?

mfg
Wenn du mir jetzt noch sagst, warum ich bzw. mein Namensvetter (;) :D) diese Funktion nutzen sollten, wär' ich sehr dankbar! :)
 
Hmm, sry :D

Hab ebend selber Versucht ein Script mit gmdate() zu schreiben. Ist nix geworden ;D

Einfach ignorieren :D
 
Na gut, dann will ich doch auch mal... ;)
PHP:
<?

//
// monthdays
// * Gibt Anzahl der Tage eines bestimmten Monats zurück
//
function monthdays($month, $year)
{
	// evtl. Überträge ausgleichen
	if ($month > 12) {
		$month -= 12;
		$year++;
	}
	if ($month < 1) {
		$month += 12;
		$year--;
	}

	switch ($month) {
		case 1: case 3: case 5: case 7: case 8: case 10: case 12:
			return 31;
		case 4: case 6: case 9: case 11:
			return 30;
		case 2:
			// Schaltjahrbestimmung
			if (($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0) {
				return 29;
			} else {
				return 28;
			}
	}

	return -1; // Fehler!
}

//
// timediff
// * Berechnet Differenz zwischen zwei Zeitpunkten
//
function timediff($t1, $t2)
{
	$diff = array();

	$keys = array_keys($t1);

	// Einfache Differenz bilden
	foreach ($keys as $key) {
		$diff[$key] = $t2[$key] - $t1[$key];
	}

	// Negative Differenzen ausgleichen
	while ($diff['M'] < 0 || $diff['D'] < 0
		|| $diff['h'] < 0 || $diff['m'] < 0 || $diff['s'] < 0) {

		if ($diff['M'] < 0) {	// Übertrag der Monate
			$diff['Y']--;
			$diff['M'] += 12;
		}
		
		if ($diff['D'] < 0) {	// Übertrag der Tage
			$diff['M']--;
			$diff['D'] += monthdays($t2['M']-1, $t2['Y']);
		}
		
		if ($diff['h'] < 0) {	// Übertrag der Stunden
			$diff['D']--;
			$diff['h'] += 24;
		}

		if ($diff['m'] < 0) {	// Übertrag der Minuten
			$diff['h']--;
			$diff['m'] += 60;
		}

		if ($diff['s'] < 0) {	// Übertrag der Sekunden
			$diff['m']--;
			$diff['s'] += 60;
		}

		if ($diff['D'] >= 7) {	// Berechnung der Wochen
			$diff['w'] = floor($diff['D'] / 7);
			$diff['D'] %= 7;
		} else {
			$diff['w'] = 0;
		}
	}

	return $diff;

}

// Y: Jahr, M: Monat, D: Tag
// h: Stunde, m: Minute, s: Sekunde

$geb = array(
	'Y' => 1985, 'M' => 8, 'D' => 10,
	'h' => 15, 'm' => 30, 's' => 0
);

$now = array(
	'Y' => date('Y'), 'M' => date('n'), 'D' => date('j'),
	'h' => date('G'), 'm' => date('i'), 's' => date('s')
);

$age = timediff($geb, $now);

print_r($age);

?>
Scheint eigentlich zu funktionieren. Hab jetzt leider nicht genügend Zeit, ein paar Testläufe zu machen oder eine lange Erklärung zu texten... kann ich ja bei Bedarf nachholen :)
 
Ich bin irgendwie sauer - hab den ganzen Tag gewartet, eine Benachrichtigungs-Mail zu kriegen und nix kam. Dann denk ich mir grad nochmal, ich könnt' doch auch so mal gucken und rums - ihr habt fleißig für mich gerackert!

Das find ich nicht gut, wenn ich hier ausgegrenzt werde! :( ;) :D

Zu dem Code: ATM bin ich zu müde, aber morgen werd' ich's testen... :)
 
Mir ist grad was aufgefallen - den if-else-Block, der mit "Berechnung der Wochen" kommentiert ist, sollte man aus der while-Schleife rausziehen (dahinter setzen).
 
Moin! :D

So, hab's getestet, es 1a! *YEEEEEEEEEEEEHAAAAAAAAAAAAAAAW* :D

Könntest du nun so frei sein und mir den Quellcode bisschen genauer erklären? :)
 
Zurück