# Zeit von einem Zeitserver beziehen?



## equestenebrarum (2. Mrz 2009)

Hallo zusammen!

Kann mir jemand sagen, wie ich mit Java über irgendeinen frei zugänglichen Zeitserver an eine möglichst genaue Uhrzeit komme? Ich benötige diese, um den genauen Zeitpunkt zu bestimmen, in dem ein Benutzer einen Button anklickt. Der zu erwartende Fehler gegenüber einer frisch synchronisierten Funkuhr sollte dabei maximal +/- 1/2 Sekunde betragen.

Und was würdet ihr dann als weiteres Vorgehen vorschlagen? Als Reaktion auf den Klick kann ich ja aus offensichtlichen Gründen nicht die Zeit erst über's Netz abfragen, also brauche ich wohl so etwas wie meinen eigenen Uhr-Thread? (Ich nehme nicht an, dass es möglich ist, per Java die Systemzeit neu zu setzen? Das wäre natürlich die ideale Lösung.)

Gibt es eine fertige Lösung, z. B. in Form einer Bibliothek, die ich einbinden könnte?

Beste Grüße

  equest


----------



## fjord (2. Mrz 2009)

Ich habe es selber noch nicht genutzt, aber was ich jetzt mal so auf die Schnelle gefunden habe ist http://commons.apache.org/net/. Das unterstützt das Network Time Protocol. Server dafür findest du zum Beispiel bei der Physikalisch-Technischen Bundesanstalt.

Mein Vorgehen wäre zu Beginn des Programms die Abweichung der lokalen Uhr vom Zeitserver zu bestimmen und dann beim Klick einfach die lokale Uhr unter Berücksichtigung der Abweichung zu verwenden. Das die lokale Uhr sich während dein Programm läuft um 1/2 Sekunde verstellt ist eigentlich nicht zu erwarten.


----------



## equestenebrarum (2. Mrz 2009)

Jau, ist eine gute Idee. Lieben Dank für die Links, sieht hilfreich aus. Ich werde mich wieder melden und sagen, ob's geholfen hat.


----------



## ARadauer (2. Mrz 2009)

die frage ist halt jetzt nur, wie du die systemzeit ändern kannst.
ich denke du willst dir die arbeit mit jni oder jna ersparen...
auf einem windows system würde es so funktionieren...

[HIGHLIGHT="Java"]import java.awt.AWTException;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class Test {

	public static void main(String[] args) throws FileNotFoundException, IOException, AWTException {
	   System.out.println("start");
	   GregorianCalendar cal = new GregorianCalendar();
	   cal.add(Calendar.HOUR, 1);

	   DateFormat f = new SimpleDateFormat("HH:mm:ss");
	   System.out.println("Stelle Zeit auf "+f.format(cal.getTime()));


	   Process p = Runtime.getRuntime().exec("cmd /c time "+f.format(cal.getTime()));
	   BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
	   String line =null;
	   while((line=r.readLine())!=null){
	      System.out.println(line);
	   }
	   System.out.println("fertig");
	}


}[/HIGHLIGHT]
das stellt die zeit um eine stunde nach vor


----------



## equestenebrarum (2. Mrz 2009)

Danke schön auch dir, ARadauer. Ich werde die Systemzeit aber nicht verstellen, wenn ich dadurch einen Teil Plattformunabhängig verliere. Ansonsten habe ich jetzt eine funktionierende Lösung gefunden, die zentrale Hilfsklasse habe ich angefügt.

Die kleine, aber feine Bibliothek, die alle meine Probleme gelöst hat, heißt AtomicDate und ist hier verfügbar: http://atomicdate.sourceforge.net. Manchmal kann es so einfach sein, wenn man das richtige Stichwort (in diesem Fall SNTP) bekommt.

[HIGHLIGHT="Java"]/**
 * Provides methods to retrieve current network time and local time offset.
 *
 * @param haddr The time server host address.
 */
class AtomicTime{

	/**
	 * The default time server.
	 */
	private String haddr = "ptbtime1.ptb.de";

	/**
	 * Default value for the number of attempts to be made to retrieve the network time offset.
	 */
	private int attempts = 3;

	/**
	 * The error message that will be returned if network time or offset can't be retrieved.
	 */
	private String errorMessage;

	/**
	 * Default format for times.
	 */
	final static public SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss.SSS");

	/**
	 * Default format for date and time.
	 */
	final static public SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("z yyyy-MM-dd HH:mm:ss.SSS");

	/**	
	 * @param haddr The time server host address.
	 */
	public AtomicTime(String haddr){
		this.haddr = haddr;
		this.errorMessage = "Can't receive current time from + " + haddr + ".";
	}

	/**
	 * Uses the default time server.
	 */
	public AtomicTime(){
		this.errorMessage = "Can't receive current time from + " + haddr + ".";
	}

	/**
	 * @return The local time offset relative to the network time, or Long.MIN_VALUE if it couldn't be retrieved.
	 *
	 * @param n The number of attempts to be made to retrieve the network time offset.
	 */
	public long getOffset(int n){
		long offset = Long.MIN_VALUE;

		net.sf.atomicdate.Client client = null;
		try{
			client = new net.sf.atomicdate.Client(1000);
		}
		catch(java.net.SocketException e){
			System.out.println(errorMessage);
			// e.printStackTrace();
		}

		if(client != null){
			long[] offsets = new long[n];
			boolean[] success = new boolean[n]; for(int i=0; i<n; i++){ success_ = true; }
			int cnt = 0;

			for(int i=0; i<n; i++){ // n attempts
				try{
					offsets = client.getOffset(haddr); // retrieve the network time offset from the specified SNTP server
					// System.out.println("" + i + ": " + offsets);
				}
				catch(IOException e){
					success = false;
					cnt++;
					// e.printStackTrace();
				}
			}

			int validOffsets = n - cnt;
			if(validOffsets > 0){
				offset = 0;
				for(int i=0; i<n; i++){
					if(success){
						// System.out.println("" + i + ": " + offsets);
						offset += offsets;
					}
				}
				// System.out.println("" + offset + ", " + validOffsets + ", " + ((double)offset/validOffsets));
				offset = Math.round((double)offset/validOffsets);
			}
			else{ System.out.println(errorMessage); }

			client.stopListening();
		}

		return offset;
	}

	/**
	 * @return The local time offset relative to the network time, or Long.MIN_VALUE if it couldn't be retrieved.
	 */
	public long getOffset(){
		return getOffset(attempts);
	}

	/**
	 * @return The current network time, or null if it couldn't be retrieved.
	 *
	 * @param n The maximum number of attempts to be made to retrieve network time.
	 */
	public Date getNetworkTime(int n){
		net.sf.atomicdate.Date date = null;
		int count = 0;

		while(date == null && count < n){ // n attempts
			try{
				date = new net.sf.atomicdate.Date(haddr);
				// System.out.println(dateTimeFormat.format(date));
				count++;
			}
			catch(IOException ioe){
				// ioe.printStackTrace();
			}
		}
		if(date == null){ System.out.println(errorMessage);	}

		return date;
	}
}[/HIGHLIGHT]_


----------

