Forum: Mikrocontroller und Digitale Elektronik Funktion: Berechnung WeekOfDay


von Zo R. (hsch1978)


Lesenswert?

Guten Morgen,

für meine Anwendung benötige ich eine Funktion, mit dieser ich den 
genauen Wochentag ermitteln kann. Als Eingabe wird ein string übergeben:
1
char *Date = "2023-03-20";

Im Netz habe ich schon die ein oder andere Funktion gefunden und auch 
getstet. Leider waren diese Funktionen nicht korrekt, was die Ausgabe 
betraf.
Gibt es eventuell hier im FOrum jemand der bereits so eine Funktion 
schon entwickelt hat?

von Εrnst B. (ernst)


Lesenswert?

Welche Umgebung?

Eine Lösung für einen Windows-PC wird anders aussehen als eine für einen 
ARM oder einen AVR.

von Zo R. (hsch1978)


Lesenswert?

Oh sorrry das habe ich vergessen zu sagen.
Es handelt sich hierbei um einem ARM Controller.

von Johnny B. (johnnyb)


Lesenswert?

He S. schrieb:
> Im Netz habe ich schon die ein oder andere Funktion gefunden und auch
> getstet. Leider waren diese Funktionen nicht korrekt, was die Ausgabe
> betraf.

Wenns bewährt und bereits getestet sein soll, dann kann man mal in den 
Source-Code eines quelloffenen Linux-Betriebssystems reinschauen.

von Dirk B. (dirkb2)


Lesenswert?

Mit den Funktionen aus <time.h> und etwas drum herum kann man das 
machen.
Jahr, Monat, Tag mit sscanf aus dem String lesen.

struct tm füllen, in Epoch umwandeln mit mktime() und damit über 
localtime() eine (jetzt richtig gefüllte) struct tm beschreiben.

Im Member tm_wday steht dann der Wochentag (Sonntag = 0)

Wenn man nur den Text braucht, geht dann auch gleich strftime()

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Quick&Dirty. Verlässt sich voll auf ein gültiges, ISO 8601-Formatiertes 
Datum im input.
1
#include <stdint.h>
2
#include <time.h>
3
#include <string.h>
4
#include <stdlib.h>
5
6
int dow(const char * date) {
7
    struct tm myTime;
8
    memset (&myTime, '\0', sizeof (myTime));
9
    myTime.tm_year = atoi(date);
10
    myTime.tm_mon = atoi(date+5)-1;
11
    myTime.tm_mday = atoi(date+8);
12
    mktime(&myTime);
13
    return myTime.tm_wday;
14
}


Statt den drei "atoi"-Zeilen geht evtl. auch ein
     strptime(date, "%F", myTime);

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

1
#include <stdio.h>
2
#include <time.h>
3
4
int main (){
5
  struct tm timeinfo = *localtime(&(time_t){time(0)});
6
  printf("%.2s\n", &"SoMoDiMiFrSa"[timeinfo.tm_wday*2]);
7
  return 0;
8
}

Die Funktionen kannst du dir in musl libc nachsehen, wie die 
implementiert sind: https://git.musl-libc.org/cgit/musl/tree/src/time/

Die Aktuellen Zeitzohnendaten kriegt man bei IANA: 
https://www.iana.org/time-zones

Aber vielleicht reicht ja schon der Teil: 
http://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c

von Rainer W. (rawi)


Lesenswert?

He S. schrieb:
> Im Netz habe ich schon die ein oder andere Funktion gefunden und auch
> getstet. Leider waren diese Funktionen nicht korrekt, was die Ausgabe
> betraf.

Für welchen Zeitraum brauchst du die Funktion.
Muss der Übergang von julianschen auf gregorianischen Kalender mit 
abgedeckt werden?

von Zo R. (hsch1978)


Lesenswert?

Danke für das Beispiel. Ich habe mal die Funktion getestet. Ich erhalte 
nicht den richtigen Wochentag. Ich habe die Funktion auch mal genauer 
mit dem Debugger angeschaut. Der Monat ist immer Monath-1? Warum 
eigentlich?

Beitrag #7380161 wurde vom Autor gelöscht.
von Zo R. (hsch1978)


Lesenswert?

Wenn ich "2023-03-12" eingebe erhalte ich den Wert 1 zurück. Ist aber 
falsch müsste 7 sein => Sonntag

von Εrnst B. (ernst)


Lesenswert?

Mein Fehler: "Year minus 1900" fehlt im Code.
Da steht aber auch die Erklärung für "Monat-1".
1
       struct tm {
2
           int         tm_sec;    /* Seconds          [0, 60] */
3
           int         tm_min;    /* Minutes          [0, 59] */
4
           int         tm_hour;   /* Hour             [0, 23] */
5
           int         tm_mday;   /* Day of the month [1, 31] */
6
           int         tm_mon;    /* Month            [0, 11]  (January = 0) */
7
           int         tm_year;   /* Year minus 1900 */
8
           int         tm_wday;   /* Day of the week  [0, 6]   (Sunday = 0) */
9
           int         tm_yday;   /* Day of the year  [0, 365] (Jan/01 = 0) */
10
           int         tm_isdst;  /* Daylight savings flag */
11
12
           long        tm_gmtoff; /* Seconds East of UTC */
13
           const char *tm_zone;   /* Timezone abbreviation */
14
       };

Sonntag wäre 0, nicht 7.

: Bearbeitet durch User
von Zo R. (hsch1978)


Lesenswert?

Nun stimmt es. Ich habe in der Funktion noch year - 1900 berechnet.
1
int dow(const char * date)
2
{
3
4
    struct tm myTime;
5
6
    memset (&myTime, '\0', sizeof (myTime));
7
8
    myTime.tm_year = atoi(date)-1900;
9
    myTime.tm_mon = atoi(date+5)-1;
10
    myTime.tm_mday = atoi(date+8);
11
12
    mktime(&myTime);
13
14
    return myTime.tm_wday;
15
16
}

von Christian M. (christian_m280)


Lesenswert?

He S. schrieb:
> WeekOfDay

oder doch etwa DayOfWeek...?

Gruss Chregu

von Rainer W. (rawi)


Lesenswert?

He S. schrieb:
> Wenn ich "2023-03-12" eingebe erhalte ich den Wert 1 zurück. Ist aber
> falsch müsste 7 sein => Sonntag

Vielleicht kannst du damit leben, dass Sonntag als 1 definiert ist.

Alternativ könntest du immer 5 dazuzählen und dann mit modulo(x,7)+1 auf 
deine Darstellung umrechnen.

von Christian M. (christian_m280)


Lesenswert?

He S. schrieb:
> erhalte ich den Wert 1 zurück. Ist aber falsch müsste 7 sein => Sonntag

Der Sonntag IST der 1. Tag in der Woche!
(Daniel 7,25)

Gruss Chregu

von Rainer W. (rawi)


Lesenswert?

Christian M. schrieb:
> Der Sonntag IST der 1. Tag in der Woche!
> (Daniel 7,25)

Und Samstag war der sechste Tag (1.Mose 1:31) und Sonntag wurde 
ausgeruht ...

von Mikro 7. (mikro77)


Lesenswert?

Christian M. schrieb:
> He S. schrieb:
>> erhalte ich den Wert 1 zurück. Ist aber falsch müsste 7 sein => Sonntag
>
> Der Sonntag IST der 1. Tag in der Woche!
> (Daniel 7,25)

https://www.timeanddate.com/calendar/days/first-day-of-the-week.html

von Zo R. (hsch1978)


Lesenswert?

Danke nochmals für eure Untersützung.

Ich habe allerdings noch ein Problem mit einer Konvertierung:
Ich möchte das Datum in einen string umwandeln für die weitere 
Verarbeitung.
Dazu nutze ich die Funktion strftime:

Datum Zeit:
1
sec = 30
2
min = 59
3
hour = 9
4
month_day = 27
5
month = 3
6
year = 123
7
day_of_week = 4
8
day_of_year 117
9
time_zone = 100663296
Nun soll mit Hilfe der Funktion strftime diese obigen Werete in ein 
string umgewandlet werden:
1
char buffer[10];
2
strftime (buffer, 10, "%Y:%m:%d", &DateTime);

Im Buffer steht dann folgendes:
1
buffer[0]  char  50 '2'  0x20000780  
2
buffer[1]  char  48 '0'  0x20000781  
3
buffer[2]  char  50 '2'  0x20000782  
4
buffer[3]  char  51 '3'  0x20000783  
5
buffer[4]  char  58 ':'  0x20000784  
6
buffer[5]  char  48 '0'  0x20000785  
7
buffer[6]  char  52 '4'  0x20000786  
8
buffer[7]  char  58 ':'  0x20000787  
9
buffer[8]  char  50 '2'  0x20000788  
10
buffer[9]  char  55 '7'  0x20000789
Das stimmt aber nicht. Der zweite Wert sollte nicht 4 sein sondern 3.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

He S. schrieb:
> Das stimmt aber nicht. Der zweite Wert sollte nicht 4 sein sondern 3.

Nochmal:

Εrnst B. schrieb:
1
 struct tm {
2
            int         tm_sec;    /* Seconds          [0, 60] */
3
            int         tm_min;    /* Minutes          [0, 59] */
4
            int         tm_hour;   /* Hour             [0, 23] */
5
            int         tm_mday;   /* Day of the month [1, 31] */
6
            int         tm_mon;    /* Month            [0, 11]  (January  = 0) */
7
            int         tm_year;   /* Year minus 1900 */
8
            int         tm_wday;   /* Day of the week  [0, 6]   (Sunday =  0) */
9
            int         tm_yday;   /* Day of the year  [0, 365] (Jan/01 =  0) */
10
            int         tm_isdst;  /* Daylight savings flag */
11
            long        tm_gmtoff; /* Seconds East of UTC */
12
            const char *tm_zone;   /* Timezone abbreviation */
13
        };

Überleg dir, was der Kommentar "January  = 0" für die Monate Februar, 
März, April... aussagt.

von Zo R. (hsch1978)


Lesenswert?

Verdammt das habe ich völlig übersehen. Danke dir.

von ●DesIntegrator ●. (Firma: FULL PALATINSK) (desinfector) Benutzerseite


Lesenswert?

He S. schrieb:

> 2023-03-20

= 2000

von Ron-Hardy G. (ron-hardy)


Lesenswert?

Εrnst B. schrieb:
> struct tm {
>             int         tm_sec;    /* Seconds          [0, 60] */

wieso 60 ?

von Harald K. (kirnbichler)


Lesenswert?

Ron-Hardy G. schrieb:
> wieso 60 ?

Schaltsekunde?

von Ron-Hardy G. (ron-hardy)


Lesenswert?

Harald K. schrieb:
> Schaltsekunde?

ok, danke

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

Ich habe mal die Gauss'sche Osterformel benutzt. Ein altes Foto vom 
Ostersonntag, ohne Jahresangabe. Ein kleines Basic-Programm lieferte mir 
die Hauptverdächtigen (Aus der Mode der abgebildeten Kleidung ließ es 
sich etwa abschätzen).
https://de.wikipedia.org/wiki/Gau%C3%9Fsche_Osterformel
da spielt noch der Vollmondtermin eine Rolle, das macht es etwas 
komplizierter.

von Andras H. (kyrk)


Lesenswert?

void rtc_update_dofw_and_week(TimeDate *ptr) {
  if (ptr != NULL) {
    unsigned int x_year = 1900;
    unsigned int y_month = 1;

    if (ptr->year >= 2000) {
      ptr->dofw_calendar = 5;//first day of year 2000 Szombat
      for (x_year = 2000; x_year < ptr->year; x_year++) {
        for (y_month = 1; y_month <= 12; y_month ++) {
          ptr->dofw_calendar += rtc_get_day_in_month(x_year, y_month);
          ptr->dofw_calendar %= 7;
        }
      }
    }
    {
      unsigned char i = 0;
      ptr->week_calendar = 0;
      if (ptr->dofw_calendar < 3) {
        ptr->week_calendar ++;
      }

      for (i = 1; i < ptr->month; i++) {
        ptr->dofw_calendar += rtc_get_day_in_month(ptr->year, i) % 7;
        ptr->week_calendar += rtc_get_day_in_month(ptr->year, i) / 7;
      }
      ptr->dofw_calendar += (ptr->day - 1) % 7;
      ptr->week_calendar += (ptr->day - 1) / 7;
      ptr->week_calendar += ptr->dofw_calendar / 7;
      ptr->dofw_calendar = ptr->dofw_calendar % 7;
    }
  }
}

unsigned char rtc_is_leap_year(unsigned int year) {
  unsigned char result = 0;
  if ((year % 400) == 0) {
    result = 1;
  } else {
    if ((year % 100) == 0) {
      result = 0;
    } else {
      if ((year % 4) == 0) {
        result = 1;
      } else {
        result = 0;
      }
    }
  }
  return result;
}

unsigned char rtc_get_day_in_month(unsigned int year, unsigned int 
month) {
  unsigned char result = 1;
  if ((month >= MONTH_MIN) && (month <= MONTH_MAX)) {
    if ((month == 2) && (rtc_is_leap_year(year) == 1)) {
      result = day_in_month[MONTH_LEAP];
    } else {
      result = day_in_month[month];
    }
  }
  return result;
}

von Dirk B. (dirkb2)


Lesenswert?

He S. schrieb:
> char buffer[10];
> strftime (buffer, 10, "%Y:%m:%d", &DateTime);

10 ist zu kurz für einen C-String.
Da ist kein Platz für die '\0'

von Rainer W. (rawi)


Lesenswert?

He S. schrieb:
> time_zone = 100663296

Warum diese kryptische Zahl und nicht einfach 0x6000000?

von Michi S. (mista_s)


Lesenswert?

He S. schrieb:
> Das stimmt aber nicht. Der zweite Wert sollte nicht 4 sein
> sondern 3.

He S. schrieb:
> day_of_year 117

Der 117. Tag des Jahres paßt aber auch besser in den April, als in den 
März.

von Joe L. (joelisa)


Lesenswert?

Εrnst B. schrieb:
> He S. schrieb:
>> Das stimmt aber nicht. Der zweite Wert sollte nicht 4 sein sondern 3.
>
> Nochmal:
>
> Εrnst B. schrieb: struct tm {
>             int         tm_sec;    /* Seconds          [0, 60] */
>             int         tm_min;    /* Minutes          [0, 59] */
>             int         tm_hour;   /* Hour             [0, 23] */
>             int         tm_mday;   /* Day of the month [1, 31] */
>             ....
>         };
>

Kann nicht mal wer diese Info in ein Youtube-Video packen? Entsprechend 
dem Niveau der "Programmierer" hier - denn schnödes Lesen ist ja so was 
von altbacken ...

von Daniel A. (daniel-a)


Lesenswert?

Joe L. schrieb:
> denn schnödes Lesen ist ja so was von altbacken ...

Dann lass es dir halt vorlesen. (Gibt ja programme dafür)

: Bearbeitet durch User
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.