Forum: Mikrocontroller und Digitale Elektronik Prüfen ob Tag innerhalb einer Tages-range liegt


von Mr Bean (Gast)


Lesenswert?

Hallo

Ich stehe vor der Aufgabe zu prüfen, ob ein bestimmter Tag innerhalb 
einer Datums-range liegt.

Also z.B. der 10. zwischen dem 5. und dem 15. .
Dieser Fall ist recht einfach und wird in meinem Algorithmus derzeit mit
1
if( (day >= date_from.day) && (day <= date_to.day) )
abgearbeitet. Allerdings schlägt diese Abfrage natürlich beim 
Monatswechsel also z.b. date_from.day=31 und date_to.day=1 fehl. Obwohl 
der Tag (31.) innerhalb des Bereichs liegt.
Habt ihr mir eine gute Idee, wie ich diesen Fall handeln kann?

Gruß,
Bean

von Falk B. (falk)


Lesenswert?

@ Mr Bean (Gast)

>Ich stehe vor der Aufgabe zu prüfen, ob ein bestimmter Tag innerhalb
>einer Datums-range liegt.

Du meinst einen Date-bereich?

>Habt ihr mir eine gute Idee, wie ich diesen Fall handeln kann?

Man muss den Tag in den Tag des Jahres umrechnen, dann hat man einen 
einheitlichen Zahlenbereich. Oder man muss in Einzelprüfungen den Monat 
mit berücksichtigen.

von Cube_S (Gast)


Lesenswert?

hier ein Code-Ausschnitt wie ich hin verwende:
1
int k = Month > 2 ? Year : Year - 1;
2
int f = Year * 365 + (k + 4) / 4 - k / 100 + k / 400 + (Month - 1) * 30 + (Month + Month / 8) / 2 + (Month > 2 ? Day - 2 : Day) - 1;
f ist dann ein einfacher Zähler für Tage der sich auch leicht 
vergleichen lässt.

von Florian (Gast)


Lesenswert?

Du nennst selbst das Stichwort: Fall.

Der übliche Weg, den Programmierer dann gehen, ist, sich die 
verschiedenen Fälle zu überlegen. In Deinem Fall (hi hi) gibt es nur 
zwei mögliche Fälle. Du hast sie genannt.

Das ist nicht einfach die Lösung, aber Du kommst selbst darauf, wenn Du 
ein wenig darüber nachdenkst, meine ich.

Allerdings gibt es ein anderes Problem dabei, das Du nicht genannt hast. 
Entweder spielt es in Deinem Fall keine Rolle oder Du hast nicht daran 
gedacht. Die Monate haben verschiedene Länge. Und im Fall des Februars 
hängt die Länge noch von der Jahreszahl ab.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Normiere Deine Datumsangaben z.B. auf x. Tag im Jahr und vergleiche 
dann.

Wenn jahresübergreifende Daten vorkommen (zwischen 25.12.16 und 3.1.17), 
dann musst Du auf Tag seit einem ausreichend lange in der Vergangenheit 
liegenden Datum normieren, und dann vergleichen.

Für Datumsberechnungen lohnt es sich immer, eine Normierungs- und 
"Entnormierungs"-Funktion zu haben, also einer, die aus einem Datum eine 
Tagesnummer und aus einer Tagesnummer wieder ein Datum erzeugt.

Damit lässt sich auch leicht die Aufgabe "Nenne mir das Datum in 41 
Tagen" lösen.

von Guest (Gast)


Lesenswert?

Du schnappst dir die Routinen zur Umrechnung von einem Datum in einen 
Timecode (Sekunden seit xxx), dann ist der Vergleich trivial.

von Peter D. (peda)


Lesenswert?

Alles in Tagen seit dem 01.01.1980 umrechnen lassen und dann 
vergleichen.

von Florian (Gast)


Lesenswert?

Die Sache mit den Normierungsfunktionen (z.B. auf die Anzahl Tage seit 
dem 1.1.1970 oder so ähnlich) ist natürlich die beste Möglichkeit. Damit 
ist alles berücksichtigt, was normalerweise auftritt.

von Mr Bean (Gast)


Lesenswert?

Ok, vielen Dank für eure Antworten.
die mktime() Funktionen aus time.c wollte ich mir egentlich sparen. Da 
diese ja doch etwas Resourcen kosten. Aber habe mir schon fast gedacht, 
dass es am Ende doch darauf raus läuft, wenn man sicher alle Fälle 
berücksichtigen möchte.

Danke

Gruß,
Bean

von Jonas B. (jibi)


Lesenswert?

>Die Sache mit den Normierungsfunktionen (z.B. auf die Anzahl Tage seit
>dem 1.1.1970 oder so ähnlich) ist natürlich die beste Möglichkeit. Damit
>ist alles berücksichtigt, was normalerweise auftritt.

Ja bis ein Datum vor dem 1.1.1970 umzurechnen ist.

Gruß J

von Cube_S (Gast)


Lesenswert?

> Ja bis ein Datum vor dem 1.1.1970 umzurechnen ist.

Kein Problem. Siehe obige Formel.

von Florian (Gast)


Lesenswert?

Jonas B. schrieb:
>>Die Sache mit den Normierungsfunktionen (z.B. auf die Anzahl Tage seit
>>dem 1.1.1970 oder so ähnlich) ist natürlich die beste Möglichkeit. Damit
>>ist alles berücksichtigt, was normalerweise auftritt.
>
> Ja bis ein Datum vor dem 1.1.1970 umzurechnen ist.
>
> Gruß J

Das versteht sich, meiner Meinung nach, ja von selbst und ist keiner 
Erwähnung wert.

von Cube_S (Gast)


Lesenswert?

> Das versteht sich, meiner Meinung nach, ja von selbst und ist keiner
> Erwähnung wert.

So selbstverständlich ist das nicht. Wenn's richtig gemacht ist, könnte 
man auch mit negativen Zahlen rechnen.

von Florian (Gast)


Lesenswert?

Es ist immer ein Problem, wie tief man bei solchen Fragen und den 
Antworten darauf geht. Bei der obigen Formel ist ihr Geltungsbereich 
nicht angegeben. Was wäre dann mit Daten vor dem 15.10.1582?

Die Behauptung - "bis man dan Daten vor 1970 berechnen muss" - an sich 
ist wertlos, wenn man dann nicht auf das aufgeworfene Problem eingeht.

Der TO hat vermutlich gar nicht die Vorkenntnisse um das einzuschätzen. 
Und bevor er mktime erwähnt hat, wusste man gar nicht, ob sein Problem 
nicht erst einmal nur darin liegt einen Vergleich für - im weitesten 
Sinne - Modulo-Bereiche zu formulieren.

> Wenn man es richtig macht ...

Was ist richtig, was ist falsch? Das hängt von dem Kenntnisstand ab.

Aber wenn ich eine Funktion postuliere, die eine Berechnung für Werte 
seit dem 1.1.1970 berechnet dann ist klar, dass sie nicht 
notwendigerweise für Werte davor gültige Resultate ergibt.

Der Satz fängt darüber hinaus falsch an. Die Voraussetzung war ja 
gerade, dass der Definitionsbereich damit anfängt. Damit ist die 
Funktion ihrer Definition nach "richtig" und es gibt kein "richtiger".

Es ist wirklich ärgerlich, dass hier alles und jedes relativiert wird. 
Ohne Erklärung, völlig unnötig, unvollständig und falsch.

von Karl (Gast)


Lesenswert?

Florian schrieb:
> Die Sache mit den Normierungsfunktionen (z.B. auf die Anzahl Tage seit
> dem 1.1.1970 oder so ähnlich) ist natürlich die beste Möglichkeit. Damit
> ist alles berücksichtigt, was normalerweise auftritt.

Wenn er eine 32-bit Variable nimmt, dann wird es ab dem 9. Januar 2038 
3:14:08 Uhr auch Probleme geben.

Was ist so schwer daran das ganze mit einer if Abfrage mit ein paar and 
zu klären:

if (((day   >= date_from.day)   && (day   <= date_to.day  )) &&
    ((month >= date_from.month) && (month <= date_to.month)) &&
    ((year  >= date_from.year)  && (year  <= date_to.year )))

von Der Andere (Gast)


Lesenswert?

Karl schrieb:
> if (((day   >= date_from.day)   && (day   <= date_to.day  )) &&
>     ((month >= date_from.month) && (month <= date_to.month)) &&
>     ((year  >= date_from.year)  && (year  <= date_to.year )))

Dann check mal was bei deinem tollen Konstrukt rauskommt wenn:
Das Datum der 20.4.2016 ist und
date_from der 28.02.2016
date_to der 4.5.2016

von Mikro 7. (mikro77)


Lesenswert?

Sollte eine einfache Prüfung von Anfang und Ende sein. Also (hopefully) 
sowas in der Art:
1
// verify range start
2
if (year < start.year) 
3
  return false ;
4
if (year == start.year) {
5
  if (month < start.month)
6
    return false ;
7
  if (month == start.month) {
8
    if (day < start.day)
9
      return false ;
10
  }
11
}
12
// verify range end
13
if (end.year < year) 
14
  return false ;
15
if (end.year == year) {
16
  if (end.month < month)
17
    return false ;
18
  if (end.month == month) {
19
    if (end.day < day)
20
      return false ;
21
  }
22
}
23
return true ;

Ein normalisiertes integer Format ist allerdings praktischer. Also 
Sekunden seit..., ich nehme gern die Julian Day Number 
(https://en.wikipedia.org/wiki/Julian_day).

von Cube_S (Gast)


Lesenswert?

> Wenn er eine 32-bit Variable nimmt, dann wird es ab dem 9. Januar 2038
> 3:14:08 Uhr auch Probleme geben.

Nicht mit obiger Formel, denn die berücksichtigt nur ganze Tage und hat 
mit 32-Bit kein Problem. Und es sind nur 2 Zeilen

von Karl (Gast)


Lesenswert?

Der Andere schrieb:
> Das Datum der 20.04.2016 ist und
> date_from der 28.02.2016
> date_to der   04.05.2016

O.K., dann falt so:

if (year >= year.from) &&
   (month > from.month || (month = from.month && day >= from.day))

...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Noch eine Alternative:

Beide Daten in einen String der Form "YYYYMMDD" umrechnen und beide 
Strings einfach strcmp() übergeben.

Return-Werte:

  -1 Datum1 kleiner Datum2
   0 Datum1 gleich  Datum2
   1 Datum1 größer  Datum2

von S.Siebenhaar (Gast)


Lesenswert?

Such mal mit dem Stichwort:
"Julianisches Datum" bzw.
"Julian day" für Ausländer.
Dafür gibt es fertige und wahrscheinlich geprüfte Funktionen im Netz.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Beide Daten in einen String der Form "YYYYMMDD" umrechnen und beide
> Strings einfach strcmp() übergeben.

Beispiel:
1
  char tim1[9];
2
  char tim2[9];
3
  int  diff;
4
5
  sprintf (tim1, "%04d%02d%02d", start.year, start.month, start.day);
6
  sprintf (tim2, "%04d%02d%02d", end.year, end.month, end.day);
7
8
  diff = strcmp (tim1, tim2);
9
10
  if (diff < 0)
11
  {
12
      // kleiner
13
  }
14
  else if (diff > 0)
15
  {
16
      // größer
17
  }
18
  else
19
  {
20
      // gleich
21
  }


P.S.

Das war falsch:

Return-Werte:

>  -1 Datum1 kleiner Datum2
>   0 Datum1 gleich  Datum2
>   1 Datum1 größer  Datum2

Richtig ist:

  negativ  Datum1 kleiner Datum2
  0        Datum1 gleich  Datum2
  positiv  Datum1 größer  Datum2

von Thomas E. (picalic)


Lesenswert?

Ich glaube, Ihr denkt alle viel zu kompliziert!
Wenn die Aufgabe wirklich nur das ist, wie im Eingangspost beschrieben, 
kann man die Datum-Zahlen auch einfach in numerische Variablen schieben 
(Jahr auf die höchstwertigen Bits, Tage auf die niederwertigen) und die 
Zahlen mit einem stinknormalen, numerischen Vergleichsoperator 
verarbeiten.

von S.Siebenhaar (Gast)


Lesenswert?

@ Thomas Elger
Bei dem von mir vorgeschlagenen Julianischen Tag kommt eine "lineare" 
Ganzzahl zum Tragen, die sich einfachen Vergleichen unterziehen lässt.

von Thomas E. (picalic)


Lesenswert?

S.Siebenhaar schrieb:
> Bei dem von mir vorgeschlagenen Julianischen...

Ok, wären wir hier in einem EDV-Forum, fände ich die Lösung auch gut, 
aber wir sind ja im Mikrocontroller-Forum und da würde ich eine simplere 
und effektivere Lösung, ohne komplizierte Umrechnung von D-M-Y in eine 
Zahl bevorzugen.

von Mikro 7. (mikro77)


Lesenswert?

Thomas E. schrieb:
> Ich glaube, Ihr denkt alle viel zu kompliziert!

Stimmt! Bei dem Vergleich spielt es ja keine Rolle ob das Skalar 
durchgehend gültige Werte aufweist, oder nicht. Statt Bits würde ich 
aber mit den Wertebereichen (day=0..30,month=0..11) skalieren, also:
1
make_scalar(year,month,day) { return (day-1) + 31 * ( (month-1) + 12 * year ) ; }

Das liest sich einfach und ist damit quasi selbst dokumentierend. Man 
muss lediglich den Fall des Überlaufs ausschließen.

von Cube_S (Gast)


Lesenswert?

Und wenn Du aus der 31 eine 32 sowie der 12 eine 16 machst wäre das die 
Shift-Variante von Thomas E.

von S.Siebenhaar (Gast)


Lesenswert?

@ Thomas Elger
Wenn ich, aus welchen Gründen auch immer, die time-lib nicht einbinden 
möchte, der TO hat seine Zielplattform ja noch nicht verraten, würde ich 
mir die "übliche" Formel einfach mal ansehen.

Wird diese nicht 10000 mal pro Sekunde durchlaufen, so sehe ich auch auf 
schwachbrüstigen µP's kein Problem.

Im Übrigen hast Du ja allen kundgetan, was Du nicht machen würdest. Wie 
wäre es mal mit einem konstruktiven Vorschlag.

von Der Andere (Gast)


Lesenswert?

S.Siebenhaar schrieb:
> Im Übrigen hast Du ja allen kundgetan, was Du nicht machen würdest. Wie
> wäre es mal mit einem konstruktiven Vorschlag.

Dus schreibst um 10:45 gegen seinen Vorschlag von 10:36 und kannst dich 
um 11:13 schon nicht mehr daran erinnern?
Vieleicht mal zum Arzt gehen :-)

von Mr Bean (Gast)


Lesenswert?

Also,

vielen Dank allen die sich an der Diskusion beteiligen.
Ich habe eine für mich arbeitende Lösung gefunden. Ich berechne mit 
timestamps mit mktime(). Das ist vielleicht nicht die performanteste, 
aber ich kann mir sicher sein dass alle Fälle abgedeckt sind.
Zumindest solange ich in dem notwendigen Zahlenraum bleibe. Also >1970.

Ich verabschiede mich dann hier aus dem Thread. ;-)

Vielen Dank wie gesagt allen noch einmal. THUMBS_UP

Gruß,
Bean

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Cube_S schrieb:
> hier ein Code-Ausschnitt wie ich hin verwende:
>
1
> int k = Month > 2 ? Year : Year - 1;
2
> int f = Year * 365 + (k + 4) / 4 - k / 100 + k / 400 + (Month - 1) * 30 
3
> + (Month + Month / 8) / 2 + (Month > 2 ? Day - 2 : Day) - 1;
4
>
> f ist dann ein einfacher Zähler für Tage der sich auch leicht
> vergleichen lässt.

 Ja, sowas ähnliches habe ich auch, nur ist es für DayOfWeek, für
 einfaches Vergleichen von 2 Tagen ist es viel zu viel Rechnen.

 Wenn es um die Geschwindigkeit und Resources geht, lässt sich
 sein Problem am schnellsten mit einfachen if-vergleichen lösen.

 Etwa so:
Mikro 7. schrieb:
> Sollte eine einfache Prüfung von Anfang und Ende sein.

 Seine Lösung hat im ungünstigsten Fall 10 einfache Vergleiche, wenn
 jeder Schritt 2us braucht, sind es max. 20us. In Assembler würde man
 dafür weniger als 60Bytes brauchen (geschätzt). Und ein guter Compiler
 sollte auch nicht viel mehr brauchen.

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
Noch kein Account? Hier anmelden.