none
Zeitserver RRS feed

  • Frage

  • Ich habe eine kleine App geschrieben, die über TCP die aktuelle Zeit von einigen Zeitservern abholt und die Rechneruhren stellt (brauchen wir kommerziell). Nach der Umstellung der Sommerzeit bin ich etwas durcheinander. Bei time.ien.it und ntp0.freenet.de hatte ich im Sommer von der zurückgelieferten Zeit 2 Stunden abziehen müssen. Nach der Zeitumstellung muss ich jetzt 1 Stunde abziehen. Wenn die genannten Server die Zeit von Greenwich liefern, dann müsste es doch eigentlich gerade umgekehrt sein. Angenommen Greenwich-Zeit ist im Sommer 12 Uhr, dann war es hier 14 Uhr (+2). Wenn jetzt im November in London 12 Uhr ist, dann ist es hier 13 Uhr (+1). Oder senden die Server eine Kennung mit, die die Differenz automatisch nach der Zone des anfragenden Clients berücksichtigt? Auf unseren Rechnern wird die Zeit über SetSystemTime in der Kernel32.dll gesetzt. Findet hier evt. auch eine Wandlung statt? Fragen über Fragen. Kennt sich jemand damit aus?

    Danke. Grüße

    Norbert

    Hier der Code:

    Imports System.IO
    Imports System.Text
    Imports System.Net
    Imports System.Net.Sockets
    
    Public Class Form1
    
        Private pi As New ProcessIcon
    
        Public Structure SYSTEMTIME
            Public wYear As UInt16
            Public wMonth As UInt16
            Public wDayOfWeek As UInt16
            Public wDay As UInt16
            Public wHour As UInt16
            Public wMinute As UInt16
            Public wSecond As UInt16
            Public wMilliseconds As UInt16
        End Structure
    
        Declare Function GetSystemTime Lib "Kernel32.dll" (ByRef lpSystemTime As SYSTEMTIME) As UInt32
        Declare Function SetSystemTime Lib "Kernel32.dll" (ByRef lpSystemTime As SYSTEMTIME) As UInt32
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
            Dim success As Boolean = False
            Dim sListe As ArrayList = ServerListe()
            For Each arr() As Object In sListe
                If GetTimeFromServer(CStr(arr(0)), CInt(arr(1)), CInt(arr(2))) Then
                    success = True
                    Exit For
                End If
            Next
            If success Then
                Me.WindowState = FormWindowState.Normal
                Application.DoEvents()
                Threading.Thread.Sleep(2000)
                pi.Dispose()
                Me.Close()
            Else
                pi.Display(New Icon("Icon.ico"))
                pi.ShowInfo()
            End If
        End Sub
    
        Private Function GetTimeFromServer(server As String, diff As Integer, port As Integer) As Boolean
            Dim tcpc As New TcpClient()
            Dim response As String = ""
            Try
                If tcpc.ConnectAsync(server, port).Wait(3000) Then
                    Using reader = New StreamReader(tcpc.GetStream)
                        response = reader.ReadToEnd
                    End Using
                End If
                If String.IsNullOrEmpty(response) Then
                    tcpc.Close()
                    Return False
                End If
            Catch ex As Exception
                tcpc.Close()
                Return False
            End Try
    
            Dim dt As Date = FormatierenDatum(response)
            If dt = Nothing Then
                tcpc.Close()
                Return False
            End If
            tcpc.Close()
    
            Dim st As New SYSTEMTIME
            dt = dt.AddHours(diff)
            st.wYear = Convert.ToUInt16(dt.Year)
            st.wMonth = Convert.ToUInt16(dt.Month)
            st.wDay = Convert.ToUInt16(dt.Day)
            st.wHour = Convert.ToUInt16(dt.Hour)
            st.wMinute = Convert.ToUInt16(dt.Minute)
            st.wSecond = Convert.ToUInt16(dt.Second)
            st.wMilliseconds = Convert.ToUInt16(dt.Millisecond)
    
            Try
                SetSystemTime(st)
            Catch ex As Exception
                Return False
            End Try
    
            Label2.Text = server
            Return True
        End Function
    
        Private Function ServerListe() As ArrayList
            Dim l As New ArrayList
            l.Add(New Object() {"time.ien.it", -1, 13})
            l.Add(New Object() {"ntp0.freenet.de", -1, 13})
            l.Add(New Object() {"ntp1.freenet.de", -1, 13})
            l.Add(New Object() {"utcnist.colorado.edu", 0, 13})
            l.Add(New Object() {"time.nist.gov", 0, 13})
            l.Add(New Object() {"time-a.nist.gov", 0, 13})
            l.Add(New Object() {"time-b.nist.gov", 0, 13})
            l.Add(New Object() {"ptbtime1.ptb.de", 0, 13})
            l.Add(New Object() {"ptbtime2.ptb.de", 0, 13})
            l.Add(New Object() {"ptbtime3.ptb.de", 0, 13})
    
            'nur UDP
            'l.Add(New Object() {"ntp1.t-online.de", 0, 13})
            'l.Add(New Object() {"ntps1-0.cs.tu-berlin.de", 0, 13})
            'l.Add(New Object() {"ntps1-1.cs.tu-berlin.de", 0, 13})
            'l.Add(New Object() {"ntp0.ewetel.de", 0, 13})
            'l.Add(New Object() {"ntp1.ewetel.de", 0, 13})
            'l.Add(New Object() {"ntp0.fau.de", 0, 13})
            'l.Add(New Object() {"ntp1.fau.de", 0, 13})
            'l.Add(New Object() {"ntp2.fau.de", 0, 13})
            'l.Add(New Object() {"ntp3.fau.de", 0, 13})
            'l.Add(New Object() {"ntp.fujitsu.com", 0, 13})
            'l.Add(New Object() {"nist1.datum.com", 0, 13})
            'l.Add(New Object() {"ntp.cs.mu.oz.au", 0, 13})
            'l.Add(New Object() {"tock.usno.navy.mi", 0, 13})
            'l.Add(New Object() {"tick.usno.navy.mil", 0, 13})
            'l.Add(New Object() {"swisstime.ethz.ch", 0, 13})
            'l.Add(New Object() {"ntp-cup.external.hp.com", 0, 13})
    
            Return l
        End Function
    
        Private Function FormatierenDatum(s As String) As Date
            Dim d As Date = Nothing
            If Date.TryParse(s.Substring(0, 20), Now) Then
                d = CDate(s.Substring(0, 20))
            ElseIf Date.TryParse(s.Substring(8, 3) & s.Substring(4, 4) & s.Substring(20, 4) & s.Substring(10, 9), Now) Then
                d = CDate(s.Substring(8, 3) & s.Substring(4, 4) & s.Substring(20, 4) & s.Substring(10, 9))
            ElseIf s.Split(" "c).Length >= 6 AndAlso s.Split(" "c)(5) = "0" Then
                Dim tokens() As String = s.Split(" "c)
                Dim dateParts() As String = tokens(1).Split("-"c)
                Dim timeparts() As String = tokens(2).Split(":"c)
    
                d = New DateTime(Convert.ToInt32(dateParts(0)) + 2000, Convert.ToInt32(dateParts(1)),
                                      Convert.ToInt32(dateParts(2)), Convert.ToInt32(timeparts(0)),
                                      Convert.ToInt32(timeparts(1)), Convert.ToInt32(timeparts(2)))
            End If
            Return d
        End Function
    
        Private Sub Exit_Click(sender As Object, e As EventArgs)
            Application.[Exit]()
        End Sub
    End Class
    Module Main
        Sub Main()
            Dim _process() As Process
            _process = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName)
    
            If _process.Length > 1 Then
                Application.ExitThread()
            Else
                Application.EnableVisualStyles()
                Application.SetCompatibleTextRenderingDefault(False)
                Application.Run(New Form1)
            End If
        End Sub
    End Module
    



    • Bearbeitet norbert3 Samstag, 18. November 2017 12:24
    Samstag, 18. November 2017 12:21

Antworten

  • Hi Norbert,
    ich habe mal die Zeiten gemessen. Meist bekomme ich die Zeit unter 200 mst. Manchmal dauert es auch etwas mehr als 1 Sekunde bis die Zeit ankommt. Nachfolgend meine Demo. Dass das Programm erst starten darf, wenn die Verbindung zum Server steht, finde ich, ist ein schlechtes Design. Das Programm sollte schon starten, bedienbar sein und bestimmte Funktionen erst erlauben, wenn die Zeit geholt wurde und die Verbindung zum Server steht. Da kann man viel asynchron ausführen, um die Abarbeitung zu beschleunigen.

    Imports System.ComponentModel
    Imports System.Net.Sockets
    Imports System.Runtime.CompilerServices
    
    Module Module11
      Private sw As New Stopwatch
      Sub Main()
        sw.Start()
        Try
          Dim c As New UtcTime
          AddHandler c.PropertyChanged, AddressOf c_PropChanged
          c.Execute()
        Catch ex As Exception
          Console.Write(ex.ToString)
        End Try
        Console.WriteLine("weiter mit Taste")
        Console.ReadKey()
      End Sub
    
      Private Sub c_PropChanged(sender As Object, e As PropertyChangedEventArgs)
        sw.Stop()
        Dim zeit = CType(sender, UtcTime).DateString
        Console.WriteLine($"Zeit: {zeit} nach {sw.ElapsedMilliseconds} ms")
      End Sub
    
      Friend Class UtcTime
        Implements INotifyPropertyChanged
    
        Private timeServers() As String = New String() {
          "ntp1.fau.de",
          "ntps1-0.cs.tu-berlin.de",
          "ntp1.lrz-muenchen.de",
          "ntps1-1.cs.tu-berlin.de",
          "ntps1-1.uni-erlangen.de",
          "ptbtime2.ptb.de",
          "time.nist.gov",
          "time-a.nist.gov",
          "time-b.nist.gov",
          "time-c.nist.gov",
          "time-d.nist.gov"
        }
    
        Private rnd As New Random
    
        Friend Property DateString As String
    
        Friend Async Sub Execute()
          DateString = (Await GetTime()).ToLongTimeString
          OnPropertyChanged(NameOf(DateString))
        End Sub
    
        Private Async Function GetTime() As Task(Of DateTime)
          Return Await Task(Of DateTime).Run(Function() As DateTime
                                               Dim port = 37
                                               Dim serverResponse = String.Empty
                                               For Each server As String In timeServers.OrderBy(Function()
                                                                                                  Return rnd.NextDouble()
                                                                                                End Function).Take(10)
                                                 Try
                                                   Debug.WriteLine(server)
                                                   Dim tcpc As New TcpClient()
                                                   If tcpc.ConnectAsync(server, port).Wait(1000) Then
                                                     Using ns As NetworkStream = tcpc.GetStream
                                                       Dim timeBuffer(3) As Byte
                                                       ns.Read(timeBuffer, 0, timeBuffer.Length)
                                                       ' Byte-Reihenfolge ggf. umkehren
                                                       If BitConverter.IsLittleEndian Then Array.Reverse(timeBuffer)
                                                       Return (New DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddSeconds(BitConverter.ToUInt32(timeBuffer, 0)).ToLocalTime()
                                                     End Using
                                                   End If
                                                 Catch
                                                 End Try
                                               Next
                                               Return Nothing
                                             End Function)
        End Function
    
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
        Friend Sub OnPropertyChanged(<CallerMemberName> Optional propName As String = "")
          RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
        End Sub
    
      End Class
    
    End Module


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Sonntag, 19. November 2017 08:22

Alle Antworten

  • Hi Norbert,
    einfacher ist es UTC zu holen und dann entsprechend den lokalen Einstellungen die aktuelle Zeit in der Zeitzone zu berechnen, z.B. so:

    Imports System.Net.Sockets
    
    Module Module11
      Sub Main()
        Try
          Dim c As New Zeit
          Console.WriteLine(c.Execute().ToLongTimeString)
        Catch ex As Exception
          Console.Write(ex.ToString)
        End Try
        Console.Write("weiter mit Taste")
        Console.ReadKey()
      End Sub
    
      Class Zeit
        Friend Function Execute() As DateTime
          Dim serverName = "time.nist.gov"
          Dim port = 37
          Dim serverResponse = String.Empty
          Dim tcpc As New TcpClient()
          If tcpc.ConnectAsync(serverName, port).Wait(3000) Then
            Using ns As NetworkStream = tcpc.GetStream
              Dim timeBuffer(3) As Byte
              ns.Read(timeBuffer, 0, timeBuffer.Length)
              ' Byte-Reihenfolge ggf. umkehren
              If BitConverter.IsLittleEndian Then Array.Reverse(timeBuffer)
              Return (New DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddSeconds(BitConverter.ToUInt32(timeBuffer, 0)).ToLocalTime()
            End Using
          End If
          Return Nothing
        End Function
      End Class
    
    End Module


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks


    Samstag, 18. November 2017 15:56
  • Danke, Du unermüdlicher Helfer!!! Wo nimmst Du bloss die Zeit dafür her? Ich kann gar nicht mehr zählen, wie oft mir Deine Tipps schon weitergeholfen haben. Am liebsten würde ich Deinen Code gleich jetzt ausprobieren, aber das muss bis morgen warten. Ist dann wirklich schon die lokale Zeit drin (auch Sommer- und Winterzeit)? Und vor allem (woran ich lange gedoktert habe): wartet die App wirklich nicht länger als 3 Sekunden, wenn der Server nicht antwortet bzw. nicht erreichbar ist? Es ist für uns fatal, wenn erst 1 Minute vergehen muss, bevor wir weiterarbeiten können. Wie schon oben beschrieben, kommt bei uns alles ins Stocken, wenn unsere Clients beim Starten des Systems nicht exakt die richtige Zeit haben.

    Herzlichst Norbert

    Samstag, 18. November 2017 16:11
  • Hi Norbert,
    als Rentner hat man genügend Zeit und die Enkel sind übers Wochenende weggefahren.

    Der Server bringt die Sekunden seit dem 1.1.1900 0 Uhr für London (GMT). Über die Zeitzonen-Einstellung des aktuellen Rechners, auf dem das Programm läuft, wird die Zeitverschiebung sommers und winters berücksichtigt. Wenn Ihr also überall mit GMT arbeitet, dann zeigen alle Rechner weltweit die richtige lokale Zeit an (entsprechend ihrer lokalen Einstellungen).

    Wenn das Warten ungünstig ist, dann den Abruf auch asynchron gestalten, so dass die Oberfläche bedienbar bleibt.

    Falls ein Time Server mal nicht zur Verfügung steht, dann muss man halt 3 Sekunden warten und den nächsten auf einer Liste nutzen oder auch einen europäischen Zeitserver. Die Auswahl kann man eine zufällig machen.


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Samstag, 18. November 2017 16:30
  • Danke Peter! Bin auch Rentner und habe nie Zeit ;-)

    >>Wenn das Warten ungünstig ist, dann den Abruf auch asynchron gestalten, so dass die Oberfläche bedienbar bleibt.

    Geht nicht. Unser Praxisprogramm darf erst starten, wenn es Verbindung zum Kassen-Server hat. Und der wirft uns ab, wenn die Zeit nicht sekundengenau stimmt. Bei erfolgreicher Konnektion laufen dann noch viele Prozesse hintendran ab, die alle in meiner App gecodet sind.

    >>dann muss man halt 3 Sekunden warten

    3 Sekunden sind Okey, aber nicht 1 Minute.

    Ich bin vor Ehrfurcht nahezu erstarrt, wie Du in wenigen Minuten den Code raushaust. Ich habe an diesem kleinen Prog fast eine Woche gesessen ...

    Viele Grüße Norbert

    Samstag, 18. November 2017 16:45
  • Warum übermittelt nicht der Klassen-Server die Zeit an die Clients. Damit hätten alle die selbe Zeit unabhängig davon ob sie "richtig" ist

    Gruß Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Cross Platform Canvas for UWP, Android, iOS

    UWP Community Toolkit Sample App

    Alle Größenangaben in UWP müssen durch 4 teilbar sein

    Samstag, 18. November 2017 17:30
  • >>Warum übermittelt nicht der Klassen-Server die Zeit an die Clients. Damit hätten alle die selbe Zeit unabhängig davon ob sie "richtig" ist

    Nicht "Klassenserver" sondern "Kassenserver". Das ist ein (sind) Server der Krankenkassen im "Sicheren Netz der KVen" bzw. der AOK. Da trifft Deine Idee auf taube Ohren. Ich möchte eigentlich auch nicht, dass die auf meinen Rechnern etwas einstellen.

    Grüße Norbert

    Samstag, 18. November 2017 18:43
  • da habe ich mich verlesen, bin aber auch erst vorhin aufgestanden :)

    Es gibt eine cross platform NTP client library die auch unter UWP verfügbar ist. Hier werden auch einige NTP Server genutzt Link

     

    Gruß Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Cross Platform Canvas for UWP, Android, iOS

    UWP Community Toolkit Sample App

    Alle Größenangaben in UWP müssen durch 4 teilbar sein

    Samstag, 18. November 2017 19:15
  • Hi Norbert,
    ich habe mal die Zeiten gemessen. Meist bekomme ich die Zeit unter 200 mst. Manchmal dauert es auch etwas mehr als 1 Sekunde bis die Zeit ankommt. Nachfolgend meine Demo. Dass das Programm erst starten darf, wenn die Verbindung zum Server steht, finde ich, ist ein schlechtes Design. Das Programm sollte schon starten, bedienbar sein und bestimmte Funktionen erst erlauben, wenn die Zeit geholt wurde und die Verbindung zum Server steht. Da kann man viel asynchron ausführen, um die Abarbeitung zu beschleunigen.

    Imports System.ComponentModel
    Imports System.Net.Sockets
    Imports System.Runtime.CompilerServices
    
    Module Module11
      Private sw As New Stopwatch
      Sub Main()
        sw.Start()
        Try
          Dim c As New UtcTime
          AddHandler c.PropertyChanged, AddressOf c_PropChanged
          c.Execute()
        Catch ex As Exception
          Console.Write(ex.ToString)
        End Try
        Console.WriteLine("weiter mit Taste")
        Console.ReadKey()
      End Sub
    
      Private Sub c_PropChanged(sender As Object, e As PropertyChangedEventArgs)
        sw.Stop()
        Dim zeit = CType(sender, UtcTime).DateString
        Console.WriteLine($"Zeit: {zeit} nach {sw.ElapsedMilliseconds} ms")
      End Sub
    
      Friend Class UtcTime
        Implements INotifyPropertyChanged
    
        Private timeServers() As String = New String() {
          "ntp1.fau.de",
          "ntps1-0.cs.tu-berlin.de",
          "ntp1.lrz-muenchen.de",
          "ntps1-1.cs.tu-berlin.de",
          "ntps1-1.uni-erlangen.de",
          "ptbtime2.ptb.de",
          "time.nist.gov",
          "time-a.nist.gov",
          "time-b.nist.gov",
          "time-c.nist.gov",
          "time-d.nist.gov"
        }
    
        Private rnd As New Random
    
        Friend Property DateString As String
    
        Friend Async Sub Execute()
          DateString = (Await GetTime()).ToLongTimeString
          OnPropertyChanged(NameOf(DateString))
        End Sub
    
        Private Async Function GetTime() As Task(Of DateTime)
          Return Await Task(Of DateTime).Run(Function() As DateTime
                                               Dim port = 37
                                               Dim serverResponse = String.Empty
                                               For Each server As String In timeServers.OrderBy(Function()
                                                                                                  Return rnd.NextDouble()
                                                                                                End Function).Take(10)
                                                 Try
                                                   Debug.WriteLine(server)
                                                   Dim tcpc As New TcpClient()
                                                   If tcpc.ConnectAsync(server, port).Wait(1000) Then
                                                     Using ns As NetworkStream = tcpc.GetStream
                                                       Dim timeBuffer(3) As Byte
                                                       ns.Read(timeBuffer, 0, timeBuffer.Length)
                                                       ' Byte-Reihenfolge ggf. umkehren
                                                       If BitConverter.IsLittleEndian Then Array.Reverse(timeBuffer)
                                                       Return (New DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddSeconds(BitConverter.ToUInt32(timeBuffer, 0)).ToLocalTime()
                                                     End Using
                                                   End If
                                                 Catch
                                                 End Try
                                               Next
                                               Return Nothing
                                             End Function)
        End Function
    
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
        Friend Sub OnPropertyChanged(<CallerMemberName> Optional propName As String = "")
          RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
        End Sub
    
      End Class
    
    End Module


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Sonntag, 19. November 2017 08:22
  • Guiten Morgen,

    vielen Dank, dass Du Dich nochmal damit beschäftigt hast.

    >>Dass das Programm erst starten darf, wenn die Verbindung zum Server steht, finde ich, ist ein schlechtes Design.

    Ja, hast sicher recht. Da muss ich nochmal drüber nachdenken. Ist natürlich asynchron viel eleganter mit dem EventHandler.

    Schönen Sonntag

    Norbert

    Sonntag, 19. November 2017 08:56