Unix-Zeitstempel aus Echtzeit errechnen

Hallochen.
Ich, mal wieder, am verzweifeln...
Für einige Berechnungen der Uhr benötige ich den Unix-Zeitstempel.
Da ich aber für die RTC keine Bibliothek benutze, hab ich den nicht.
Also habe ich den Code aus der Wikipedia mal angepasst:

//**************************** Unix-Zeitstempel berechnen **********************************************
void unixzeit()
{
  UhrLesen(jahre,monate,tage,stunden,minuten,sekunden);            // aktuelles Datum und Zeit holen
  
  /** Konvertiert gegliederte UTC-Angaben in Unix-Zeit.
 * Parameter und ihre Werte-Bereiche:
 * - jahr [1970..2038]
 * - monat [1..12]
 * - tag [1..31]
 * - stunde [0..23]
 * - minute [0..59]
 * - sekunde [0..59]
 */
  const short tage_seit_jahresanfang[12] = /* Tage seit Jahresanfang ohne Tage des aktuellen Monats und ohne Schalttag */
    {0,31,59,90,120,151,181,212,243,273,304,334};
 
  long unix_zeit;
  long jahr=jahre-1970;
  int schaltjahre=((jahre-1)-1968)/4 - ((jahre-1)-1900)/100 + ((jahre-1)-1600)/400;
 
  unix_zeit=sekunden + 60*minuten + 60*60*stunden +
    (tage_seit_jahresanfang[monate-1]+tage-1)*60*60*24 +
    (jahr*365+schaltjahre)*60*60*24;
 
  if ( (monate>2) && (jahre%4==0 && (jahre%100!=0 || jahre%400==0)) )
    unix_zeit+=60*60*24; /* +Schalttag wenn jahr Schaltjahr ist */
 
  Serial.begin(9600);
  Serial.println(unix_zeit);
  
}

Das Ergebnis ist nahe dran, aber nicht nahe genug. Es liegt grob elf Monate in der Vergangenheit.
Hat einer der Experten hier eine Ahnung, warum?

Die RTC liefert korrekt aktuelles Datum und Uhrzeit-an der liegts also nicht.

Benutze doch einfach die Time-Library, auf die die Arduino-Referenz verweist?

Dort steht :

makeTime(tm);

Convert normal date & time to a time_t number. The time_t number is returned. The tm input is a TimeElements variable type, which has these fields:

tm.Second  Seconds   0 to 59
tm.Minute  Minutes   0 to 59
tm.Hour    Hours     0 to 23
tm.Wday    Week Day  0 to 6  (not needed for mktime)
tm.Day     Day       1 to 31
tm.Month   Month     1 to 12
tm.Year    Year      0 to 99 (offset from 1970)

Wobei time_t dann im Unix-Format ist.

Liebe Grüße

Dirk

Statt selber rechnen, lieber rechnen lassen :smiley:

Das habe ich zum Stellen der RTC genutzt. Bei einem batteriegepufferten DS3231 Modul ist das ja nicht allzu oft notwendig.

Gruß Gerald

Nein, ich will keine Lib benutzen- die ganze Uhr läuft bisher ohne sie und so solls auch bleiben.
Dass ich die Zeit bequem online errechnen lassen kann, weiss ich-aber das nutzt mir nix.
Ich brauch sie im Arduino, damit die Uhr mir beispielsweise die Mondphasen errechnen kann.

Rabenauge:
Nein, ich will keine Lib benutzen- die ganze Uhr läuft bisher ohne sie und so solls auch bleiben.

OK, ich habe da mal was vorbereitet, mit einigen Tricksereien und Optimierungen:

unsigned long unixTime(int year, int month, int day, int hour, int minute, int second)
{
  month = (month + 9) % 12;
  year = year - month/10;
  return (365L*year + year/4 - year/100 + year/400 + (month*306 + 5)/10 + ( day - 1 )-719468L) * 86400L + hour*3600L + minute*60 + second;
}

Probier's mal aus!

Ist nicht schlecht, so als Dreizeiler?
Viel kürzer geht es wahrscheinlich nicht.

Und noch ein Nachtrag zum Wikipedia-Code, den ich mir hier herausgesucht habe:

Der Code ist völlig korrekt und eigentlich muss man nur bedenken, dass dieser Code für ein handelsübliches 32- oder 64-Bit Betriebssystem geschrieben ist und nicht für 8-Bit AVR-Mikrocontroller.

Im Wesentlichen wäre zu beachten, dass ein "int" nicht 16 Bit hat wie auf dem AVR-Controller, sondern 32-Bit, und deshalb müßten im Code einige Variablen zur Berechnung auf größere Zahlentypen gebracht werden. Insbesondere bei den konstanten Ausdrücken "606024" ist das Ergebnis regelmäßig größer als es mit einem AVR 16-Bit int darstellbar ist, so dass ich daraus eine "long" Konstante mache und schreibe "60L6024".

long unixzeitWikipedia(int jahr, int monat, int tag, int stunde, int minute, int sekunde)
{
  int tage_seit_jahresanfang[12] = /* Tage seit Jahresanfang ohne Tage des aktuellen Monats und ohne Schalttag */
    {0,31,59,90,120,151,181,212,243,273,304,334};
 
  long unix_zeit;
  long jahre=jahr-1970;
  int schaltjahre=((jahr-1)-1968)/4 - ((jahr-1)-1900)/100 + ((jahr-1)-1600)/400;
 
  unix_zeit=sekunde + 60*minute + 60L*60*stunde +
    (tage_seit_jahresanfang[monat-1]+tag-1)*60L*60*24 +
    (jahre*365+schaltjahre)*60L*60*24;
 
  if ( (monat>2) && (jahr%4==0 && (jahr%100!=0 || jahr%400==0)) )
    unix_zeit+=60L*60*24; /* +Schalttag wenn jahr Schaltjahr ist */
 
  return unix_zeit;
}

Und wieder biste der Held des Tages- passt!

Wird ein, zwei Tage dauern, ehe ich die Berechnungen geistig nachvollziehen kann, aber ich werds irgendwann kapieren.
Der Verdacht, dass da irgendein Wertebereich nicht ausreicht, kam mir zwar schon, aber ich ging davon aus, dass eine "long" immer gleich "long" ist-egal auf welcher Maschine- dem ist also nicht so.
Und während ich nun versuche, den Code zu verstehen (eigentlich eher den Rechenweg, der Code ist nicht kompliziert), kann ich die Uhr weiter ausbauen, recht entspannt.
Danke.

Bei dem Wikipedia Code kann man ebenso in unsigned long rechnen. Dann gehen nochmal doppelt so große Zahlen rein.

Rabenauge:
Wird ein, zwei Tage dauern, ehe ich die Berechnungen geistig nachvollziehen kann, aber ich werds irgendwann kapieren.

Das mit dem Kapieren könnte schwierig werden. Die mathematischen Grundlagen zur Datumsberechnung stammen wohl von Carl Friedrich Gauß, der ein grundlegendes Manuskript zu Datums-Tabellen und Datums-Berechnungen erstellt hatte, das erst nach seinem Tod veröffentlich wurde. Und worauf aufbauend dann viele andere Leute Modifikationen erstellt haben, und Optimierungen für die Berechnungen mit Computern statt des Ablesens aus Tabellen. Wenn Du etwas googeln möchtest, die wesentliche Grundlage ist die Berechnung der Differenz in Tagen zwischen zwei Datumsangaben. In diesem Fall also des aktuellen Datums und des 01.01.1970, an dem die UNIX-Zeitrechnung startet.

Dazu habe ich jedenfalls gegoogelt, dann die Tagesdifferenzberechnung in Code umgesetzt und die Konstante für den 01.01.1970 ("719468L") fest ausgerechnet, die Anzahl der Tage mit 86400 multipliziert und die Sekunden des laufenden Tages draufaddiert. Von ganz alleine wäre ich auch nicht auf meine Dreizeiler-Funktion gekommen.

Rabenauge:
Der Verdacht, dass da irgendein Wertebereich nicht ausreicht, kam mir zwar schon, aber ich ging davon aus, dass eine "long" immer gleich "long" ist-egal auf welcher Maschine- dem ist also nicht so.

Das wesentliche Problem sind beim Wikipedia-Code nicht mal die deklarierten Variablen, bei denen scheint es sogar für 8-Bit Controller zu passen. Das wesentliche Problem des Wikipedia-Codes scheinen die Konstanten zu sein.
606024 ==> Produkt aus drei Integer-Konstanten ==> Integer-Konstante (läuft über, da als 16-Bit int nicht darstellbar)
Wenn man nicht mindestens 32-Bit Integer auf seinem System hat, muss die Konstante "long" deklariert sein, was ich bei den Konstanten so handhabe, dass ich einfach den ersten der Faktoren als Long ("L") deklariere:
60L6024

Und schon scheint es auch mit dem Wikipedia-Code zu funktionieren, weil die Kontanten nun den tatsächlichen richtigen Wert enthalten und nicht nur die untersten 16 Bit, die in einen AVR "int" reinpassen.

Wie Serenifly schrieb, fehlt beim Wikipedia-Code auch noch eine Umstellung auf "unsigned long", wenn man das nicht macht, bekommt der Wikipedia-Code ein Jahr-2038-Problem: