[Ajax] einfache Uhr mit serverseitiger Zeit

Das window.setTimeout() würde ich immer so laufen lassen, dass es kurz nach der neuen Sekunde feuert. Das ist ja nicht schwer auszurechnen, da man die aktuelle Zeit ja sowieso schon ausgelesen hat. Wenn es dann mal etwas daneben liegt, wird das beim nächsten Mal korrigiert.

In etwas so: Serverzeit
Code:
<?php
if (array_key_exists("getTime", $_GET)){
	date_default_timezone_set("Europe/Berlin");
	$utime = microtime(true);
	$milliseconds = $utime - floor($utime);
	$ret = array(
		"dateHour" => strftime("%d.%m.%Y %H:"),
		"milliseconds" => (strftime("%M")*60 + strftime("%S") + $milliseconds)*1000
	);
	echo json_encode($ret);
	die();
}
?>
<!DOCTYPE html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Fenstertitel</title>
<script type="text/javascript" src="//kkjs.kkapsner.de/modules/kkjs.load.js?modules=sprintf"></script>
<style type="text/css"></style>
</head>
<body>
Server: <span id="server"><span id="serverDateHour"></span><span id="serverRest"></span></span>
<br>
Client: <span id="client"></span>
<script type="text/javascript">
function refreshServerTime(){
	var requestTime = Date.now();
	var offset = 0;
	var hours = 0;
	kkjs.ajax.advanced({
		url: "?getTime",
		onload: function(txt){
			var requestDuration = Date.now() - requestTime;
			var data = JSON.parse(txt);
			kkjs.$("serverDateHour").innerHTML = data.dateHour;
			
			var now = new Date();
			var serverNow = new Date();
			serverNow.setMinutes(0);
			serverNow.setSeconds(0);
			serverNow.setMilliseconds(data.milliseconds + requestDuration/2);
			
			offset = serverNow - now;
			hours = serverNow.getHours()
			refreshServerTimePartial();
		}
	});
	
	
	function refreshServerTimePartial(){
		var now = new Date();
		now.setTime(now.getTime() + offset);
		kkjs.$("serverRest").innerHTML = kkjs.sprintf(
			"%02d:%02d",
			now.getMinutes(),
			now.getSeconds()
		);
		
		if (now.getHours() !== hours){
			refreshServerTime();
		}
		else {
			window.setTimeout(
				refreshServerTimePartial,
				1001 - now.getMilliseconds()
			);
		}
	}
}

function refreshClientTime(){
	var now = new Date();
	
	kkjs.$("client").innerHTML = kkjs.sprintf(
		"%02d.%02d.%04d %02d:%02d:%02d",
		now.getDate(),
		now.getMonth() + 1,
		now.getFullYear(),
		now.getHours(),
		now.getMinutes(),
		now.getSeconds()
	);
	window.setTimeout(
		refreshClientTime,
		1001 - now.getMilliseconds()
	);
}

refreshServerTime();
refreshClientTime();
</script>
</body>
</html>
 
Zuletzt bearbeitet:
Korbinian, ich würde das gern umschreiben auf jQuery oder reines JS.
Was macht kkjs.ajax.advanced() ? Ist das ein ganz normaler Ajax Request?
 
Hab es mal umgebaut, um zu erreichen, dass
- der unnötige Ajax Request zum Start wegfällt
- jQuery als Lib ausreicht

PHP:
<?php
date_default_timezone_set("Europe/Berlin");
$utime = microtime(true);
$milliseconds = $utime - floor($utime);
$ret = array(
	"dateHour" => strftime("%d.%m.%Y %H:"),
	"milliseconds" => (strftime("%M")*60 + strftime("%S") + $milliseconds)*1000
);
$out = json_encode($ret);

if (array_key_exists("getTime", $_GET)){
	echo json_encode($out);
}
else {
echo <<<EOT
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Fenstertitel</title>
<style type="text/css"></style>
<script src="../javascript/jquery.js"></script>
<script>
function refreshServerTime(start){
	var requestTime = Date.now();
	var offset = 0;
	var hours = 0;
	var losgehts = function(data) {
		var requestDuration = Date.now() - requestTime;
		jQuery('#serverDateHour').html(data.dateHour);
		
		var now = new Date();
		var serverNow = new Date();
		serverNow.setMinutes(0);
		serverNow.setSeconds(0);
		serverNow.setMilliseconds(data.milliseconds + requestDuration/2);
		
		offset = serverNow - now;
		hours = serverNow.getHours()
		refreshServerTimePartial();
	};
	if (start) {
		losgehts(start);
	}
	else {
		jQuery.ajax ({
			url: 'uhrzeit_mik.php?getTime',
			data: '',
			dataType: 'json',
			type: 'POST',
			cache: false,
			success: function(data) {
				losgehts(data);
			},
			error: function() {
			}
		});
	}
	function refreshServerTimePartial(){
		var now = new Date();
		now.setTime(now.getTime() + offset);
		var m = now.getMinutes();
		var s = now.getSeconds();
		if (m < 10) {m='0'+m}
		if (s < 10) {s='0'+s}
		jQuery('#serverRest').html(m+':'+s);
		
		if (now.getHours() !== hours){
			refreshServerTime();
		}
		else {
			window.setTimeout(
				refreshServerTimePartial,
				1001 - now.getMilliseconds()
			);
		}
	}
}
</script>
</head>
<body>
	Server: <span id="server"><span id="serverDateHour"></span><span id="serverRest"></span></span>
<script>
refreshServerTime(jQuery.parseJSON('$out'));
</script>
</body>
</html>
EOT;
}
?>

Live: Fenstertitel

Korbinian, habe ich das richtig übersetzt?
 
Sieht gut aus.

Aber dadurch, dass du den ersten HTTP-Request weggelassen hast, hast du keine Information über die Latenzzeit zwischen Server und Client. Das sind zwar nicht besonders viel, aber es kann gerne mal eine halbe Sekunde sein (v.A. wenn vorher noch eine Bibliothek geladen werden muss) - bei dir sind das jetzt insgesamt ca. 200ms

PS: in PHP Heredocs zu verwenden finde ich nach wie vor grausam, unnötig und schlecht lesbar.
PPS: diese Zeile ist unnötig kompliziert:
Code:
refreshServerTime(jQuery.parseJSON('$out'));
- $out ist ja schon JSON und kann deswegen direkt ins JS geschrieben werden:
Code:
refreshServerTime($out);
 
Aber die Latenzzeit ist doch nur wichtig, dass der Request rechtzeitig vor der nächsten vollen Stunde gemacht wird, oder?

Zweite Frage: Wenn ich die Sekunden nicht haben möchte, reicht es dann, unten beim window.setTimeout einfach 60001 - now.getSeconds() zu schreiben und natürlich die Ausgabe zu ändern?
 
Die Latenzzeit is wichtig, damit die Uhr richtig geht, da du ja beim Server die Zeit abfragst, dann die Daten an den Client schickst, dort das HTML (teilweise) aufbaust und dann erst das JS startest. Dann ist aber schon etwas Zeit vergangen. Kann in Extremfällen sogar mal in den Sekundenbereich kommen - wobei die Abschätzung, dass die Hälfte der Kommunikationszeit dann auf die Serverzeit draufaddiert werden muss, dann auch nicht mehr beosnders genau ist.

Der nächste Request sollte in der Sekunde nach der nächsten vollen Stunde gemacht werden.

60001 klingt theoretisch gut, aber da würde ich eventuell trotzdem alle 10 Sekunden aktualisieren.

EDIT: hab' gerade gesehen, dass das nicht besonders toll aussieht beim Stundenwechsel... ich würde da jetzt kurzzeitig ein "resync" oder sowas anzeigen.
 
Zuletzt bearbeitet:
Könnten wir vielleicht noch eine Version erstellen, die die von kkapsner zuletzt genannten Aspekte berücksichtigt und mit reinem JS (ohne jQuery) läuft?
 
Bei mir ist das aktuell so in Betrieb:

HTML:
function uhr(data){
	var requestTime = Date.now();
	var offset = 0;
	var hours = 0;
	var tag = new Array('Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag');
	var losgehts = function(data) {
		var requestDuration = Date.now() - requestTime;
		jQuery('#maindatumzeit_datum').html(data.dateHour);
		var now = new Date();
		var serverNow = new Date();
		serverNow.setMinutes(0);
		serverNow.setSeconds(0);
		serverNow.setMilliseconds(data.milliseconds + requestDuration/2);
		jQuery('#maindatumzeit_tag').html(tag[serverNow.getDay()]);
		offset = serverNow - now;
		hours = serverNow.getHours()
		refreshServerTimePartial();
	};
	if (!data) {
		requestTime = Date.now();
		jQuery.ajax ({
			url: '/pfad/script.ext',
			data: 'deineparameter',
			dataType: 'json',
			type: 'POST',
			cache: false,
			success: function(dataneu) { losgehts(dataneu); },
			error: function() { refreshServerTimePartial(); }
		});
	}
	else { losgehts(data); }
	function refreshServerTimePartial(){
		var now = new Date();
		now.setTime(now.getTime() + offset);
		var h = now.getHours();
		var m = now.getMinutes();
		if (h < 10) {h='0'+h}
		if (m < 10) {m='0'+m}
		jQuery('#maindatumzeit_zeit').html(h+':'+m);
		if (h != hours) { uhr(); }
		else {window.setTimeout(refreshServerTimePartial,(60-now.getSeconds())*1000);}
	}
}

Aufruf:
HTML:
<script>uhr({"dateHour":"08.04.2014","milliseconds":346421.761989594});</script>
Die zwei Variablenwerte werden dynamisch vom Serverscript eingesetzt.

Du brauchst also nur den Ajax Request und die jQuery('selector').html()'s ersetzen. Und natürlich die ID's.
 
Zuletzt bearbeitet:
Ganz wichtg, falls jemand diesen Code hier nutzt:
In der function refreshServerTimePartial muss in der vorletzten Zeile bei dem Vergleich aus !== ein != gemacht werden!!!
Sonst führen einziffrige Uhrzeiten (0 bis 9 Uhr) nämlich dazu, dass der Vergleich scheitert und die Uhr lädt ununterbrochen vom Server. Beispiel 7 Uhr: 07 !== 7 ist natürlich false!
Ursache: Das zwei Zeilen drüber gemachte h='0'+h ;)

Ich habs in Beitrag #28 schon korrigiert.

Boah hat mich das jetzt wochenlang gewurmt. Das Blöde ist, immer wenn ich die Debug Ausgaben gerade so halb drin hatte war es bereits 10 Uhr und der Fehler trat nicht mehr auf
*KLATSCH BUMM BÄNG* :) :mad: :p :icon6:

Und wenn man dann nur in einem Fenster das Projekt offen hat merkt man das ja auch nicht unbedingt sofort.
Heute hatte ich Glück, denn ich hab es gemerkt (da in 4 Tabs offen) und es war früh genug, also weit vor 10 Uhr :)
 
Oh nein. Das ist die falsche Stelle. Damit beseitigst du nur das Symptom. Das grundelegende Problem ist, dass Darstellung und Datenverwaltung vermischt sind bzw. in der falschen Reihenfolge aufgerufen werden.

Code:
	function refreshServerTimePartial(){
		var now = new Date();
		now.setTime(now.getTime() + offset);
		var h = now.getHours();
		var m = now.getMinutes();
		if (h !== hours) { uhr(); }
		else {window.setTimeout(refreshServerTimePartial,(60-now.getSeconds())*1000);}
		if (h < 10) {h='0'+h}
		if (m < 10) {m='0'+m}
		jQuery('#maindatumzeit_zeit').html(h+':'+m);
	}

PS: hab' gerade auch erst gesehen, dass du den Array-Konstruktor verwendest... tztztz...
 
Hallo zusammen,

auch ich greife jetzt mal an, da ich zwei Dinge gesehen habe, die ich für "unrund" halte. Gut und wichtig wie richtig: Request pro Sekunde ist ein Todbringer ... die Lösung ist jetzt eigentlich ganz smart und schont Ressourcen des Servers. Und trotzdem mische ich mich ein - verflixxt!

Ich habe in der mikdoe'schen Lösung zwei Dinge gesehen, die ich als "unperformant" einstufe - sorry, ist also nicht böse gemeint, aber wir haben auch die Ressource "Client", mit der man "direkt schonend" umgehen soll und muss. Grund meines Vetos ist nur die Art, wie der Quellcode geschrieben ist:

Code:
var requestTime = Date.now(),
		offset = 0,
		hours = 0,
		tag = ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
		losgehts = function(data){ ... usw.

Dass nicht jede Variable ein "var" braucht, wenn diese direkt aufeinander folgen, ist jetzt mal nur sekundär (verkleinert nur die auszuliefernde JS-Datei), ist aber Teil meines "Gemeckers". Aber das Array sollte nicht (!) mit "new Array" gebaut werden, sondern lediglich durch den Einsatz von "[]". Man sollte auch - aus Performance-Gründen - nicht "new Object" nehmen, sondern "{}" (nur mal als Vergleich). Sinn und Zweck sind schlichtweg Performance-Gründe im Client, da die Browser für "new Array" länger benötigen als für "[]" - obwohl das Ergebnis identisch ist. Vergleichbar wäre dies mit "array_push" statt "array[] = Wert", wenn einfach nur ein Wert angehängt werden soll (in PHP).

Lasst mich noch ein Stück weiter ausholen:
Statt "new RegExp()" sollte man bekanntlich (fiktiv) "/[a-z]/g" nehmen ...
... statt "new String" eben nur "string = ''" (gilt auch für new Number, new Boolean; also direkt zuweisen - nicht so bilden lassen!) ...
und natürlich - findet man häufiger - nicht "throw new Error('gnarf')" sondern "throw { name: "Error", message: "gnarf"};" (oder: "throw Error('gnarf');")

Fazit: Finger weg von den Build-In-Konstruktoren, da diese eine Web-App bremsen (= Flaschenhals)!

Beste Grüße


EDIT: Verlifxxt ... hatte noch was vergessen! Und zwar: setTimeout() in einem Browser wird nicht nach exakt "1000" ausgeführt! Es ist egal, welcher Browser - es ist ein Nährwert, mehr nicht!
 
und natürlich - findet man häufiger - nicht "throw new Error('gnarf')" sondern "throw { name: "Error", message: "gnarf"};" (oder: "throw Error('gnarf');")
Wenn du ein generisches Objekt wirfst ist das aber was anderes als wenn du ein Error wirfst. Das kann man nicht einfach so austauschen.

Fazit: Finger weg von den Build-In-Konstruktoren, da diese eine Web-App bremsen (= Flaschenhals)!
Auch das ist so nicht korrekt. So muss man manchmal den RegExp-Konstruktor verwende,n wenn man eine RegExp dynamisch aufbauen will und ein Date-Objekt kann man nur mit dem Konstruktor erzeugen.

Aber bie Array, Object, Number, String und Boolean gebe ich dir recht, dass man davon die Hände lassen sollte. Aber nicht aus dem Grund, dass es langsamer ist (was es laut meinen Tests auch nicht ist), sondern weil es unerwartetes Verhalten zeigt.

EDIT: Verlifxxt ... hatte noch was vergessen! Und zwar: setTimeout() in einem Browser wird nicht nach exakt "1000" ausgeführt!
Das ist uns klar. Was willst du uns damit sagen? Deswegen wir die Zeit ja auch über ein Date-Objekt berechnet... und hier ist es ja auch egal, wenn das Timeout mal etwas später kommt (früher kommt es ja nie).
 
Hallo erneut,

das ist keine These von mir, sondern von dem Herrn Stefanov, der seine Bücher bei O'Reilly (= "JavaScript Patterns") rausbringt. Da ich das kürzlich aufgesaugt habe, war es noch lebhaft im Gedächtnis. Ich schlage gern nach und lege die Seitenangabe vor. Wenn ich also dem Material von O'Reilly weniger Glauben solle, aber dafür bspw. Foren ... hmmm ... da komme ich in ein Dilemma. ;)

"den" kann auch anders gelesen werden - in Form von "den genannten" bzw. "diesen". Ich habe also nicht global von Build-In-Konstruktoren geschrieben, sondern in einem Fazit von denEN, die genannt sind. ;)

Ein dynamischer RegExp? Hatte ich noch nicht - würde mir nicht einmal ein (Praxis-)Beispiel für einfallen ... und "new Date" war in meiner Auflistung übrigens gar nicht bei. ;)

Was die "1000" anbelangt, hatte ich in diesem Thread - ich meine es war eine Frage vom mikdoe - gelesen, inwiefern diese Angabe "exakt" wäre. Eine Antwort scheine ich dann offenbar überlesen zu haben - dadurch hatte ich aber den Eindruck, dass die Frage nicht beantwortet wurde und erwähnte dies im Gesamtzusammenhang.

Du spielst auf Error(), SyntaxError() und TypeError() an? Ich spreche aber von eigenen Fehlermeldungen als Beispiel und kann - mittels "throw" - meine Fehlermeldungen derart aufbohren, dass ich in der Tat das alles zeigen kann, was wirklich benötigt wird (bei "catch"). Habe mir jetzt mal das Buch geschnappt - das mit dem "throw" steht als Empfehlung auf Seite 53/54. Das Beispiel im Buch sieht so hierfür aus:

Code:
try {
  throw {
	name: "MyErrorType", // custom error type
	message: "oops", 
	extra: "This was rather embarrassing",
	remedy: genericErrorHanler // who should handle it
  };
} catch(e){
	// inform user ...
	alert(e.message);
	
	e.remedy(); // calls genericErrorHandler()
}

Und auch das hier hatte ich vor längerer Zeit gelesen - das ist bei Stackoverflow bzgl. "Flaschenhals" (und nicht "unerwartetes Verhalten"). Sei bitte nicht böse, aber "Flaschenhals" trifft es wirklich. :D

Gute Nacht.
 
Aber bie Array, Object, Number, String und Boolean gebe ich dir recht, dass man davon die Hände lassen sollte. Aber nicht aus dem Grund, dass es langsamer ist (was es laut meinen Tests auch nicht ist), sondern weil es unerwartetes Verhalten zeigt.
welches unerwartete verhalten?
ja, langsammer ist es, aber das ist nicht erwähnenswert
ob man deswegen ständig darauf hinweisen muss die literalschreibweise zu nutzen, ich weiss ja nicht...

Sei bitte nicht böse, aber "Flaschenhals" trifft es wirklich.
Ähm... Flaschenhals? Das bezeichnest du als Flaschenhals?
 
Hi hesst,

ja, tue ich (und in Anführungszeichen), da ich das nicht als Ganzes betrachte, sondern immer nur als Teil des eigentlichen Gesamtprojektes. Dort gibt es auch noch (wieder) viele andere Teile, auf die man sehenden Auges direkt bei Produktion optimieren kann.

Und ganz rudimentär wird eine Datei sogar "kleiner" (auch in Anführungszeichen, damit wir hier nicht von rauhen Mengen sprechen), wenn man die Literalschreibweise verwendet. Und kleine(re) Dateien? Da war ja auch noch was ...

Ich meine es wirklich gar nicht böse, aber Optimierungen gehen so ziemlich überall! Und gerade im Client (noch eher beim Mobilgeräthype) sind performante Handlings einfach ein Muss.

Viele Grüße
 
ja, tue ich (und in Anführungszeichen), da ich das nicht als Ganzes betrachte, sondern immer nur als Teil des eigentlichen Gesamtprojektes. Dort gibt es auch noch (wieder) viele andere Teile, auf die man sehenden Auges direkt bei Produktion optimieren kann.
kein mensch optimiert an stellen, die niemals relevant werden können.
wenn man 50 Millionen arrays pro sekunde mit new Array anlegen kann, spielt es keine rolle, wenn man mit literalschreibweise 90 Millionen anlegen könnte. das spielt erst dann eine rolle, wenn man in die nähe dieser größenordnung kommt bzw. sie überschreitet. dann ist es ein spezialfall der optimiert werden muss. und nur in spezialfällen macht optimierung sinn. optimierung auf gut glück bei nicht relevantem code ist sinnlos. optimiert wird, an stellen die problematisch SIND.

Und ganz rudimentär wird eine Datei sogar "kleiner" (auch in Anführungszeichen, damit wir hier nicht von rauhen Mengen sprechen), wenn man die Literalschreibweise verwendet. Und kleine(re) Dateien? Da war ja auch noch was ...
wenn man meint, übertragungszeit einsparen zu müssen, jagd man das durch einen minimizer, da spielen die paar byte keine rolle. sinnvoller wäre es die anzahl der requests zu minimieren, als die größe des codes.

Ich meine es wirklich gar nicht böse,
habe ich nicht behauptet, und ich meine es auch nicht böse, aber so

aber Optimierungen gehen so ziemlich überall!
optimiert man nicht
 
Zurück
Oben