Oft werden Datum und Uhrzeit als 32 Bit Zahl der vergangenen Sekunden seit einem Stichtag dargestellt, z.B. in Filesystemen oder in RTC-Bausteinen wie dem DS1994. Damit kann aber keiner was anfangen, d.h. man muß diesen 32 Bit-Wert erst in Datum und Uhrzeit umrechnen. Anbei ein einfacher Beispielkode, der auf 8-Bit CPUs optimiert ist. Es werden also möglichst viele Variablen als 8-Bit definiert und möglichst wenig Divisionen bzw. keine Divisionen in Schleifen verwendet. Als erstes erfolgt die Berechnung der Zeit durch fortgesetzte Division mit Rest. Leider erwiesen sich da sowohl der Keil C51 als auch der AVR-GCC wenig optimierungsfreudig. Obwohl die Divisionsroutine gleichzeitig den Quotienten und den Rest ermittellt, rufen beide die Divisionsroutine zweimal auf. Der etwas schwerere Teil ist die Datumsermittlung. Da 2^32 Sekunden nur 136 Jahre umfassen, muß zuerst ein Startwert festgelegt werden, z.B. der 1.1.2000. Um nun die Jahreszahl zu ermitteln, wird in einer Schleife für jedes Jahr ermittelt, ob es ein Schaltjahr ist und dementsprechend werden 365 oder 366 Tage abgezogen. Da es nur max 136 Jahre sind und keine Division in der Schleife erforderlich ist, ist der Rechezeitaufwand minimal. Die 100- und 400-Jahres Regel wird berücksichtigt. Danach wird dann die Tagesanzahl je Monat abgezogen und die Monate hochgezählt. Übrig bleiben die Tage. Sommerzeit: Die Sommerzeit beginnt am letzten Sonntag um 2.00Uhr im März und endet zum gleichen Zeitpunkt im Oktober. Zuerst werden die Monate 1, 2, 11, 12 ausgeschlossen und dann wird auf den Umschaltzeitpunkt getestet. Vor dem Umschaltzeitpunkt wird im März abgebrochen und nach dem Umschaltzeitpunkt im Oktber. Dann wird die Stunde um 1 erhöht und eine eventuelle Weiterschaltung von Tag, Wochentag und Monat behandelt. Anbei das komplette Beispielprogramm für den Keil C51 Peter
Respekt Peter ! Das wollte ich schon immer mal sagen. Eine 100 oder 400 Jahresregel hätte ich sicher nicht berücksichtigt :) Ich bin zwar überzeugter Assembler Programmierer und kann deshalb die C-Sachen nicht übernehmen aber ich finde es echt super, dass du etliche Sourcen zur Verfügung stellst. Grüße, Alex
Hallo! Hat jemand zufälig eine routine, mit der es möglich ist, einen "besetzten" time-Typen: struct time { u8 second; u8 minute; u8 hour; u8 day; u8 month; u16 year; u8 wday; }; in Sekunden seit einem Stichtag zurückzurechnen? Beispiel: unsigned long sec2000; // u32, Sekunden seit 1.1.2000 time->year = 2005; time->month=7; time->day=15; _getsecond_(sec2000, &time); --> Im Moment sieht die Anwendung vor, dass man Minute, Stunde per Display einstellen kann. Dazu addiere/subtrahiere ich nach ensprechendem Tastendruck auf die Var. sec2000 einfach 3600 für Stunden, 60 bei Minuten usw. Aber wie mache ich es, wenn ich den Tag/Monat/Jahr in/dekrementieren will? Über einen Beitrag bin ich sehr dankbar mfG
Hallo Robert, ich habe Dein Problem noch nicht so ganz verstanden. Warum bleibst Du nicht einfach bei struct time? Aber wenn Du die struct time in einen long wandeln willst, kannst Du mktime() benutzen. Bsp. ........... struct time *tm; time_t timelong; tm->year = 105; tm->mon = 7; tm->mday = 15; timelong = mktime(tm); ........... Joline
hier hast was. musst es eventuell noch etwas umbauen für deine bedürfnisse. wohl hauptsächlich das: /* Start with 1900 a.d. */ result -= 693961L; wenn du mit 1.1.1980 bzw. 1970 arbeiten willst struct rdcf_date_and_time { unsigned char second; unsigned char minute; unsigned char hour; unsigned char day; unsigned char month; unsigned short year; }; /* Calculates seconds from date since 01.01.1900, 00:00:00 */ unsigned long SecondsFromTime (struct rdcf_date_and_time *t) { unsigned char i; unsigned long result = 0; unsigned int idx; /* days of each month */ unsigned char md[] = {31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* Make month an array index */ idx = t->month - 1; /* Leap year? adjust february */ if (t->year%400 == 0 || (t->year%4 == 0 && t->year%100 !=0)) md[1] = 29; else md[1] = 28; /* Calculate days of years before */ result = (unsigned long)t->year * 365; if (t->year >= 1) { result += (t->year+3) / 4; result -= (t->year-1) / 100; result += (t->year-1) / 400; } /* Start with 1900 a.d. */ result -= 693961L; /* Loop thru each month, adding the days */ for (i=0; i<idx; i++) result += md[i]; /* Add remaining days */ result += t->day; /* Convert to seconds, add all the other stuff */ result = (result-1) * 86400L + (unsigned long)t->hour * 3600 + (unsigned long)t->minute * 60 + t->second; return result; }
Hallo Peter, ich habe mir mal die Fkt. summertime mal angeschaut, weil ich in einer meiner Anwendungen auch eine solche Unterscheidung einbauen muss. Dafür wollte ich deine Fkt. anpassen. Nun versteh ich leider einen Punkt in der zweiten If-Abfrage nicht:
1 | if( day - wday >= 25 && (wday || hour >= 2) |
2 | if( month == 10 ) |
3 | return; |
Warum (wday || hour >=2)? Hour >=2 versteh ich ja, aber wday || hour? Ist Sonntag == 0?
"Ist Sonntag == 0?" Ja, ich habs von der PC-Funktion "get date" so übernommen. Steht auch ganz oben bei der Definition der Startzeit. Peter
Hallo Peter, toller Code! Ich steh aber momentan auf dem Schlauch. Denn ich möchte aus dem Struct, also dem Datum/Uhrzeit den Wert in Sekunden ermitteln. Deine Funktion also, nur anders herum. Hast Du da einen Tip parat? Viele Grüße Stefan
@Stefan, ein Ausschnitt aus den "zeitmanipulationen" die ich seit vielen Jahren verwende und teilweise auf AVRs portiert habe (verwendet zB PROGMEM). Nachdem das "ausgeschnitten" ist kann ich aber nicht garantieren dass es in dieser Form fehlerfrei Compiliert. Viel Erfolg Werner
Ahh jetzt sehe ich was ich rausgeschnitten hatte... In MakeTime_t am Ende noch "second + (60 * (minute + 60 * hour))" addieren. Aber der Kommentarteil "... bis ... 0:00:00" trifft es
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.