Search code examples
cdayofweek

How can I make a function which calculates the weekday of a specific date in C?


Fixed: I had to do it with a pointer to call the values(I also changed the formular but I´m sure the old one would also work. The problem was the missing pointer):

sAppointment Calendar[MAXAPPOINTMENTS];

int dayOfWeek(sDate *date){
    static int jMonth[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    int d = date->Day;
    int m = date->Month;
    int y = date->Year;

    if(m < 3) y--;

    return(y + (y/4) - (y/100) + (y/400) + jMonth[m - 1] + d) % 7;
}

I am writing a C Program which has a datastructure in which the variables of a date can be saved but it does not work for the weekday. It appears an error: "expression must have integral type"

The function is build like this, including a formular to calculate the weekday:

void dayOfWeek(sDate date){
    if(date.Month > 3) date.Year = date.Year - 1;
    date.WeekDay = ((date.Day + floor (2.6 * ((date.Month + 9) % 12 + 1) - 0.2) + date.Year % 100 + floor (date.Year % 100 / 4) + floor (date.Year / 400) - 2 * floor (date.Year / 100) - 1) % 7 + 7) % 7 + 1;
}

The date datastructure contents int values which looks like this (german language):

typedef enum {So, Mo, Di, Mi, Do, Fr, Sa} eDayofTheWeek;

typedef struct{
    int Day;
    int Month;
    int Year;
    eDayofTheWeek WeekDay;
} sDate;

I tried to set most of the formular to a variable which first containts everything before the modulo operator

int w;
w = ((date.Day + floor (2.6 * ((date.Month + 9) % 12 + 1) - 0.2) + date.Year % 100 + floor (date.Year % 100 / 4) + floor (date.Year / 400) - 2 * floor (date.Year / 100) - 1);
date.Weekday =( w % 7 + 7) % 7 + 1;

This only led to the weekday value being 0. Other formulars also let the value of being 0.


Solution

  • OP's code has at least these problems:

    No update for caller

    void dayOfWeek(sDate date){ does not provide any result back to the caller ("This only led to the weekday value being 0"). Instead, pass by reference.

    void dayOfWeek(sDate *date) {
    //                   ^   
    

    % not valid with floating point

    Before applying %, convert the value to an int. Note: C does not has a modulo operator but a remainder operator.

    Wrong test

    // if(date.Month > 3)   date.Year = date.Year - 1;
    if(date.Month < 3) { date.Year = date.Year - 1;  maybe adjust Month }
    

    I was unable to fully fix OP's code.

    Consider avoiding one-liner code and instead provide more clarity.

    Below works for month and day outside the usual range.

    #define MARCH 3
    #define DaysPer400Years   (400*365LL + 97)
    #define DaysPer100Years   (100*365LL + 24)
    #define DaysPer4Years     (4*365LL + 1)
    #define DaysPer1Year      365LL
    #define DayNumber1970Jan1 719469
    #define MonthsPerYear 12
    
    long long DayNumber0(int year, int Month, int Day, long epoch) {
      long long dn = Day;
      long long y = year;
      y += Month / 12;
      Month %= 12;
      while (Month < MARCH) {
        Month += 12;
        y--;
      }
      // And then a miracle occurs. Scale by 30.59375 and add an offset.
      dn += ((Month - MARCH) * (7832 / 4) + (140 / 4)) >> (8 - 2);
      dn += (y / 400) * DaysPer400Years;
      y %= 400;
      dn += (y / 100) * DaysPer100Years;
      y %= 100;
      dn += (y / 4) * DaysPer4Years;
      y %= 4;
      dn += y * DaysPer1Year;
      return dn - epoch;
    }
    
    int WeekDay_chux(int year, int Month, int Day) {
      #define WEEK_JAN1AD1_OFFSET 5
      int dow = DayNumber0(year, Month, Day, WEEK_JAN1AD1_OFFSET) % 7;
      return (dow + 7 )%7;
    }
    

    Note that DaysPer400Years is a multiple of 7 (for the Gregorian Calendar). Thus we can take short cuts and start with year %= 400 to work with smaller numbers.