Datum aus NTP-Datenpaket berechnen

Hallo zusammen,

ich habe ein Programm geschrieben, welches einen NTP-Zeitserver abfragt und aus dem empfangenen Datenpaket Datum und Uhrzeit berechnet und diese auf dem seriellen Monitor ausgibt.

Das funktioniert soweit auch alles ganz gut, allerdings ist der Tag des Datums falsch. Es ist immer ein Tag zu wenig. Wenn z.B. das Datum der 14.12.2018 ist, dann gibt das Programm "13.12.2018" aus.

Die Berechnung erfolgt direkt aus der NTP-Zeit (Sekunden seit 01.01.1900) und diese Zeit wird auch korrekt vom Zeitserver empfangen (ich habe mir den Zeitstempel anzeigen lassen, daraus die UNIX-Zeit bestimmt und mir anschließend daraus online das Datum und die Uhrzeit berechnen lassen -> Zeitstempel ist korrekt).

Der Code, der das Datum berechnet ist folgender:

...

// Variablen
uint32_t timeInSeconds ;
uint8_t month, dayMonth;
uint16_t year;

// NTP-Zeitserver abfragen
// Gibt die Anzahl der Sekunden zurück, die seit dem 01.01.1900 vergangen sind
timeInSeconds = RequestNTP();

// Eine Stunde aufaddieren, da Deutschland = UTC+1h ist
timeInSeconds += 3600;

// Jahr berechnen
// Sekunden durchschnittliches Jahr (365,25 Tage): 31557600UL
// Anzahl von 4-Jahresblöcken berechnen, die seit 1900 vergangen sind
uint8_t blocks = (timeInSeconds / (4 * 31557600UL));
// Vorläufige Jahreszahl berechnen
year = START_YEAR_NTC + (4 * blocks);   
// Anzahl der verbleibenden Sekunden berechnen
// (Sekunden seit 1900 - Sekunden aller 4-Jahresblöcke)
uint32_t remainingSeconds = (timeInSeconds - (4 * blocks * 31557600UL));
// Bis zu 3 Jahre auf die vorläufige Jahreszahl addieren
// Nach der obigen Berechnung können max. 3 Jahre dazu kommen, da anonsten "leastSeconds" 0 wäre
for (uint8_t i=0;i<3;i++)
{
  // Kein Schaltjahr (Sekunden normales Jahr: 31536000UL)
  if (year % 4)
  {
    if (remainingSeconds >= 31536000UL)
    {
      year++;
      remainingSeconds -= 31536000UL;
    }  
  }
  // Schaltjahr (Sekunden Schaltjahr: 31622400UL)
  else
  {
    if (remainingSeconds >= 31622400UL)
    {
      year++;
      remainingSeconds -= 31622400UL;
    }
  }
}

// Monat berechnen
month = 1;
// Februar mit 28 Tagen = 2419200UL s
// Februar mit 29 Tagen = 2505600UL s
// 30-Tage Monat = 2592000UL s
// 31-Tage Monat = 2678400UL s
while (remainingSeconds >= 2419200UL)
{
  // Monate mit 31 Tagen 
  // (außer Dezember, da ansonsten Jahreswechsel stattfinden würde)
  if ((month == 1)
      || (month == 3)
      || (month == 5)
      || (month == 7)
      || (month == 8)
      || (month == 10))
  {
    if (remainingSeconds >= 2678400UL)
    {
      month++;
      remainingSeconds -= 2678400UL; 
    }   
  }
  // Monate mit 30 Tagen
  else if ((month == 4)
           || (month == 6)
           || (month == 9)
           || (month == 11))
  {
    if (remainingSeconds >= 2592000UL)
    {
      month++;
      remainingSeconds -= 2592000UL; 
    }   
  }
  // Februar im Nicht-Schaltjahr
  else if ((month == 2) 
           && (year % 4))
  {
    if (remainingSeconds >= 2419200UL)
    {
      month++;
      remainingSeconds -= 2419200UL; 
    }         
  }
  // Februar im Schaltjahr
  else if ((month == 2) 
           && !(year % 4))
  {
    if (remainingSeconds >= 2505600UL)
    {
      month++;
      remainingSeconds -= 2505600UL; 
    }         
  }
}

// Monatstag berechnen
dayMonth = 1;
dayMonth += (remainingSeconds / 86400UL);

...

Kann mir jemand sagen, wo der Fehler in der Berechnung liegt?
Vielen Dank :wink:

Schau Dir mal die Funktion breakTime in der Time-Lib an.

Gruß Tommy

AArduinoO:

...

// NTP-Zeitserver abfragen
// Gibt die Anzahl der Sekunden zurück, die seit dem 01.01.1900 vergangen sind
utcTime = RequestNTP();

// Eine Stunde aufaddieren, da Deutschland = UTC+1h ist
uctTime += 3600;
...

Fällt Dir was auf?

Gruß

Gregor

gregorss:
Fällt Dir was auf?

Gruß

Gregor

Bitte keine Nutzlosen Antworten geben.
Danke :slight_smile:

Tommy56:
Schau Dir mal die Funktion breakTime in der Time-Lib an.

Gruß Tommy

Danke für deine Antwort.
Leider ist der Code sehr schlecht kommentiert, so dass ich nicht verstehe, was da vor sich geht.

Du kannst ja mal beim Autor nachfragen, ob er ihn für Dich noch mehr kommentiert :wink:

Der Code ist auch ohne zusätzliche Kommentare verständlich, wenn man ein paar Grundlagen beherrscht.

Du brauchst auch nicht zu schreiben, dass Du die Antwort als nutzlos empfindest. Du bekommst keine andere von mir.

Gruß Tommy

Tommy56:
[...]

Du brauchst auch nicht zu schreiben, dass Du die Antwort als nutzlos empfindest. Du bekommst keine andere von mir.

Gruß Tommy

User wie "gregorss" haben in solchen Foren nichts zu suchen. Sein Beitrag hat nichts zur Lösung des geschilderten Problems beigetragen ist damit nutzlos.

Arrogante Fragesteller, wie Du, noch viel weniger.

Er hat Dich lediglich auf eine mögliche Fehlerquelle hingewiesen, weil Du für UTC- und localtime die gleiche Variable verwendet hast. Du hast es nur nicht begriffen.

Und Tschüß
Tommy

AArduinoO:
Bitte keine Nutzlosen Antworten geben.
Danke :slight_smile:

Deine Antwort ist sehr AntwortLustAbhandenKommend.
Grüße Uwe

AArduinoO:
User wie "gregorss" haben in solchen Foren nichts zu suchen. Sein Beitrag hat nichts zur Lösung des geschilderten Problems beigetragen ist damit nutzlos.

Ich sehe das anders. Ich sehe daß solche Fragesteller in solchen Foren nichts zu suchen haben.
Das schlimme an meiner Meinung ist ich bin einer der Moderatoren.

Also was soll ich denn denken?

Grüße Uwe

Tommy56:
Arrogante Fragesteller, wie Du, noch viel weniger.

Ich drücke mich klar aus und antworte auf Fragestellungen exakt, problembezogen und zielführend.
So wie sich das für ein deutschsprachiges Forum gehören sollte.

Für manche mag das arrogant wirken.

Tommy56:
[...]

Er hat Dich lediglich auf eine mögliche Fehlerquelle hingewiesen, weil Du für UTC- und localtime die gleiche Variable verwendet hast. Du hast es nur nicht begriffen.

Und Tschüß
Tommy

Das wäre lediglich dann eine Fehlerquelle, wenn wir uns zeitlich um 23:x Uhr (0 <= x <= 59) befinden würden. Dies ist aber nicht der Fall, da ich im ersten Post geschrieben habe:

" Es ist immer ein Tag zu wenig. " (man beachte das Wort "immer")

Hätte "gregorss" meinen Beitrag vernünftig gelesen und über das Problem nachgedacht, so hätte er bemerkt, dass er sich seinen Post hätte sparen können.

Tommy56:
weil Du für UTC- und localtime die gleiche Variable verwendet hast

Ging es nicht eher darum dass es einmal utc und einmal uct heißt?

@Serenifly: Du hast Recht. Lesefehler von mit, wobei ich keine Deklaration von uctTime in seinen Beiträgen finde, der Code also wohl nichtmal kompiliert.

Gruß Tommy

Serenifly:
Ging es nicht eher darum dass es einmal utc und einmal uct heißt?

Es ist richtig, dass ich im Kommentar einen Fehler gemacht habe.
Jedoch hat das keine Auswirkung auf den Programm-Code.

Zum besseren Verständnis habe ich die Variable in "timeInSeconds" umbenannt.

uwefed:
[...]

Also was soll ich denn denken?

Grüße Uwe

Wir leben in einem freien Land, ich kann ich will Ihnen nicht vorschreiben, was Sie denken sollen.

Tommy56:
@Serenifly: Du hast Recht. Lesefehler von mit, wobei ich keine Deklaration von uctTime in seinen Beiträgen finde, der Code also wohl nichtmal kompiliert.

Gruß Tommy

Sie wird nicht bei der Definition deklariert, jedoch ein paar Zeilen weiter:

timeInSeconds = RequestNTP();

Die Funktion gibt eine 32-Bit Variable zurück, welche den Zeitstempel der NTP-Anfrage enthält (Byte 40 bis 43).

Was ist los?
Keine fähigen User hier anwesend?

Zeigt mir, was ihr könnt!

AArduinoO:
Was ist los?
Keine fähigen User hier anwesend?

Zeigt mir, was ihr könnt!

Beruhige Dich!!!

Hi,

Über manche Dinge soll man ja einfach mal schlafen, Also wenn Du wieder runter gekommen bist erlaube mir mal ne doofe Frage:

Wozu willst Du das Rad neu erfinden, es gibt Räder die rund sind. Meisten sind die neue erfundenen Räder erst mal eckig so wie Deines.

time.h

oder als Beispiel in der IDE Beispiel/Time

da gibts ne ganze menge Vorschläge wie man Dein grudsätzliches Anliegen lösen kann.

PS: Ich sehe ja ein das Du uns testen wolltest, eigendlich hast Du keine sinvolle Antwort mehr verdient, und ich denke das sehen andere ebenso, dennoch etwas damit das mit einem Lösungsansatz geschlossen werden kann.

Heinz

AArduinoO:
Das funktioniert soweit auch alles ganz gut, allerdings ist der Tag des Datums falsch. Es ist immer ein Tag zu wenig.

Obwohl die Stimmung hier am Boden ist, werde ich mal helfen ^^

Die Aussage aus dem Quoting ist meiner Meinung nach falsch. Die Berechnung stimmt zumindest, ohne dass ich den Taschenrechner in Hand genommen habe oder den Code ausgeführt habe, für den gesamten Januar 1990. Man könnte jetzt hergehen und den Anfangswert der Variablen mal händisch auf bestimmte Werte setzen und prüfen ab wann der Algorithmus nicht mehr funktioniert.

Deshalb ein Tipp zu Selbsthilfe: Verrechnet sich der Algorithmus tatsächlich immer genau um einen Tag oder siehst du nur diesen Rechenfehler weil du dir Stunde, Minute, Sekunde nicht ausrechnest und ausgibst? Da würde man nämlich evtl. erkennen, dass der Algorithmus mit fortschreitendem timeInSeconds-Wert immer weiter abdriftet (Erstmal nur meine Vermutung).

Ich lehn mich mal aus dem Fenster und behaupte, dass der Fehler in dem Sekunden-Wert für ein mittleres Jahr liegt. Das mittlere Jahr hat nämlich nicht 365,25 Tage sondern nur ca. 365,2425 Tage. Nach gut 119 Jahren hast du also mittlerweile einen Rechenfehler von ca. 1 Tag drin.

Gruß
Jarny