16

I am trying to write a simple program in c++ that returns the day of the week for a given date.

The input format is day, month, year. I cannot get it to work with leap years. I tried subtracting one from the a variable when the input year is a leap year, but the program just ends up crashing without an error message.

I would appreciate any suggestions, but please try to remain simple, I am still a beginner. Apologies for the stupid question, and please excuse my mistakes, this is the first time I post on this site.

#include <iostream>
#include <string>
#include <vector>
#include <cmath>
using namespace std;


int d;
int m;
int y;


string weekday(int d, int m, int y){
    int LeapYears = (int) y/ 4;
    long a = (y - LeapYears)*365 + LeapYears * 366;
    if(m >= 2) a += 31;
    if(m >= 3 && (int)y/4 == y/4) a += 29;
    else if(m >= 3) a += 28;
    if(m >= 4) a += 31;
    if(m >= 5) a += 30;
    if(m >= 6) a += 31;
    if(m >= 7) a += 30;
    if(m >= 8) a += 31;
    if(m >= 9) a += 31;
    if(m >= 10) a += 30;
    if(m >= 11) a += 31;
    if(m == 12) a += 30;
    a += d;
    int b = (a - 2)  % 7;
    switch (b){
    case 1:
        return "Monday";
    case 2:
        return "Tuesday";
    case 3:
        return "Wednesday";
    case 4:
        return "Thursday";
    case 5:
        return "Friday";
    case 6:
        return "Saturday";
    case 7:
        return "Sunday";
    }
}

int main(){
    cin >> d >> m >> y;
    cout << weekday(d, m, y);
}
Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
OerllydSaethwr
  • 169
  • 1
  • 1
  • 4
  • 3
    What happens when you step through it in your debugger? – MrEricSir Nov 09 '16 at 22:42
  • There is no such thing as a simple program when it comes to dates (go ask jon skeet). That functionality already exists why reinvent it. – Paul Rooney Nov 09 '16 at 22:46
  • 1
    There's a simple formula for performing the calculation. See https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week – Dan Nagle Nov 09 '16 at 22:47
  • Search the internet for "c++ calculate day of week" to find a plethora of examples. – Thomas Matthews Nov 09 '16 at 22:52
  • `y` is already an int. Consequently the cast `(int) y/ 4` is redundant, and `(int)y/4 == y/4` is always true – StoryTeller - Unslander Monica Nov 09 '16 at 22:52
  • Here is a good collection of low-level date algorithms: http://howardhinnant.github.io/date_algorithms.html – Howard Hinnant Nov 10 '16 at 16:51
  • Thanks for all the comments, they are very much appreciated. – OerllydSaethwr Nov 10 '16 at 23:32
  • @Paul Rooney: I am not trying to reinvent a function that already exists, all I did was trying to solve a common problem that did not seem very difficult by myself. @Dan Nagle: I have seen the formula for the calculation, but as I have already mentioned, I wanted to be inventive rather than just copy something that has already been done. @StoryTeller: Thank you! What I meant to write was: `(int) (y/4) == y/4` – OerllydSaethwr Nov 10 '16 at 23:38

8 Answers8

24

First: Do not write your own function, if there already are standardized functions that can handle the same problem. Point is that you might easily make a mistake (and I can already see one in the first line of your weekday() function as it is now), whereas implementations of standardized functions have been tested thoroughly and you can be confident that they deliver the result you are expected to get.

That being said, here is a possible approach using std::localtime and std::mktime:

#include <ctime>
#include <iostream>

int main()
{
  std::tm time_in = { 0, 0, 0, // second, minute, hour
      9, 10, 2016 - 1900 }; // 1-based day, 0-based month, year since 1900

  std::time_t time_temp = std::mktime(&time_in);

  //Note: Return value of localtime is not threadsafe, because it might be
  // (and will be) reused in subsequent calls to std::localtime!
  const std::tm * time_out = std::localtime(&time_temp);

  //Sunday == 0, Monday == 1, and so on ...
  std::cout << "Today is this day of the week: " << time_out->tm_wday << "\n";
  std::cout << "(Sunday is 0, Monday is 1, and so on...)\n";

  return 0;
}
Striezel
  • 3,693
  • 7
  • 23
  • 37
  • 3
    Thank you for your suggestion, however the whole point of writing this program is to practice programming and try to get a grasp of the syntax of c++. I cannot blame you for suggesting a completely different approach though, since I did not mention this in my post. But if I ever need something like this when writing a more complex program with more experience, I will make sure to consider this method! – OerllydSaethwr Nov 10 '16 at 23:28
  • One year, ~1800 views, and not a single upvote oO. Just fixed it. @OerllydSaethwr please accept this (or another) answer by clicking the green checkmark near its score. – YSC Oct 11 '17 at 08:15
  • 1
    A solution about `std::localtime` is to make a copy of its return as soon as possible: `const std::tm time_out = *std::localtime(&time_temp);`. – YSC Oct 11 '17 at 08:17
  • @Striezel why are you using mktime? Why not use strftime directly? – aderchox Dec 11 '19 at 14:56
  • @Striezel You have also not provided tm_wday, tm_yday, tm_isdst. – aderchox Dec 11 '19 at 16:51
  • YSC: while that helps one can still get a corrupted tm – edwinc Aug 16 '20 at 21:19
  • aderchox: tm_wday and tm_yday are ignored by mktime. tm_isdst is irrelevant when it comes to dates. – edwinc Aug 16 '20 at 21:25
  • 1
    This seems to to be the only proper answer to this question among many bad answers. Thank you. – Violet Giraffe Feb 02 '23 at 13:48
7

New answer for old question because the tools they are a changing...

The C++20 spec says the following will have identical functionality to the intention of the code in the question:

#include <chrono>
#include <format>
#include <iostream>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    year_month_day dmy;
    cin >> parse("%d %m %Y", dmy);
    cout << format("{:%A}", weekday{dmy}) << '\n';
}

One can experiment today with this syntax by using this free, open-source date/time library, except that the date objects are in namespace date instead of namespace std::chrono, and the syntax of the format string is slightly altered.

#include "date/date.h"
#include <iostream>

int
main()
{
    using namespace std;
    using namespace date;
    year_month_day dmy;
    cin >> parse("%d %m %Y", dmy);
    cout << format("%A", weekday{dmy}) << '\n';
}
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 2
    Thanks for the code Howard. Unfortunately, it seems for the latest g++ and clang++ the `parse` and `format` are still not ready (with `-std=c++20`). By latest I mean g++-11 and clang++ 13. I am talking about the code part without header `"date/date.h"` : ( – aafulei Jan 06 '22 at 07:38
6

Your understanding of what constitutes a leap year is incorrect:

A leap year is every 4 years EXCEPT if it's divisible by 100, BUT even then it's still a leap year if it's divisible by 400.

A clear and concise explanation of how to calculate the "day number" (dn) can be found here.

Once you have the day number (dn), just perform a modulus 7. The result will be the day of week (dow).

Here's an example implementation (doesn't check if date is valid input):

#include <iostream>
#include <iomanip>

typedef unsigned long ul;
typedef unsigned int ui;

// ----------------------------------------------------------------------
// Given the year, month and day, return the day number.
// (see: https://alcor.concordia.ca/~gpkatch/gdate-method.html)
// ----------------------------------------------------------------------
ul CalcDayNumFromDate(ui y, ui m, ui d)
{
  m = (m + 9) % 12;
  y -= m / 10;
  ul dn = 365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + (d - 1);

  return dn;
}

// ----------------------------------------------------------------------
// Given year, month, day, return the day of week (string).
// ----------------------------------------------------------------------
std::string CalcDayOfWeek(int y, ul m, ul d)
{
  std::string day[] = {
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday",
    "Monday",
    "Tuesday"
  };

  ul dn = CalcDayNumFromDate(y, m, d);

  return day[dn % 7];
}

// ----------------------------------------------------------------------
// Program entry point.
// ----------------------------------------------------------------------
int main(int argc, char **argv)
{
  ui y = 2017, m = 8, d = 29; // 29th August, 2017.
  std::string dow = CalcDayOfWeek(y, m, d);

  std::cout << std::setfill('0') << std::setw(4) << y << "/";
  std::cout << std::setfill('0') << std::setw(2) << m << "/";
  std::cout << std::setfill('0') << std::setw(2) << d << ": ";
  std::cout << dow << std::endl;

  return 0;
}
Andy Turfer
  • 181
  • 1
  • 4
6

You can use the Gregorian Date System from the Boost C++ library to find the day of week of a given date. Here is a simple example:

#include <boost/date_time.hpp>
#include <string>
#include <iostream>

const static std::string daysOfWeek[] = {
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday"
};

int getDayOfWeekIndex(int day, int month, int year) {
    boost::gregorian::date d(year, month, day);
    return d.day_of_week();
}

int main()
{
    const int index = getDayOfWeekIndex(30, 07, 2018);
    std::cout << daysOfWeek[index] << '\n';
}

This code prints Monday.

HugoTeixeira
  • 4,674
  • 3
  • 22
  • 32
3

I had the same problem, and I was able to find a simple solution.

According to this post:
"Following is a simple function suggested by Sakamoto, Lachman, Keith and Craver to calculate day. The following function returns 0 for Sunday, 1 for Monday, etc."

int dayofweek(int d, int m, int y)  
{  
    static int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };  
    y -= m < 3;  
    return ( y + y / 4 - y / 100 + y / 400 + t[m - 1] + d) % 7;  
}
NProg
  • 51
  • 5
  • Does it take leap seconds and such into account? Will it account for future leap seconds? Looks too simple to be pedantically correct. – Violet Giraffe Feb 02 '23 at 13:46
0

What happens when a number is perfectly divisible by 7?

14 / 7 = 2 14 % 7 = 0

The modulo operator (% n) will return a number from 0 to n -1

if n is divided by 7 the remainder can never be 7 so

int b = (a - 2)  % 7;
    switch (b){
    case 1:
        return "Monday";
    case 2:
        return "Tuesday";
    case 3:
        return "Wednesday";
    case 4:
        return "Thursday";
    case 5:
        return "Friday";
    case 6:
        return "Saturday";
    case 7:
        return "Sunday";
    }
}

In this case it can never be Sunday

Try this

int b = (a - 2)  % 7;
        switch (b){
        case 0:
            return "Sunday";
        case 1:
            return "Monday";
        case 2:
            return "Tuesday";
        case 3:
            return "Wednesday";
        case 4:
            return "Thursday";
        case 5:
            return "Friday";
        case 6:
            return "Saturday";
        default:
            return "Error";
        }
ComradeJoecool
  • 734
  • 6
  • 18
  • 2
    Don't listen to people that say not to use your own function. – ComradeJoecool Nov 09 '16 at 23:09
  • For all we know you area doing this for fun, or for a learning experience. – ComradeJoecool Nov 09 '16 at 23:09
  • Also, please bear in mind, that your eventual program will be incorrect as to the day of the week. That is because leap years are tricky things that don't ALWAYS happen every 4 years. Example leap years don't happen on year 100 but they do happen on year 400. So yeah, calendars can get tricky really fast. My answer is primarily to help you finds the cause as to why your program is crashing. – ComradeJoecool Nov 09 '16 at 23:14
  • Thank you, I never even considered that *a* could be a multiple of seven, I will make sure to look into that. – OerllydSaethwr Nov 10 '16 at 23:31
0

try using the CTime class

Exemple:

const CTime currTime = CTime::GetCurrentTime();
const int nWeekDay = currTime.GetDayOfWeek();

switch (nWeekDay)
{
    case 1:
        return "Monday";
    case 2:
        return "Tuesday";
    case 3:
        return "Wednesday";
    case 4:
        return "Thursday";
    case 5:
        return "Friday";
    case 6:
        return "Saturday";
    case 7:
        return "Sunday";
}

in the example above I'm using the current time, but you can do it differently, with the time you want example:

const CTime currTime = CTime(year,month, day, hours, minutes, seconds );
-2
int dayofweek(int day,int month,int year)
{
    int arr[] = {0,3,2,5,3,5,1,4,6,2,4};
    if(month<3)
        year--;
    return ((year+year/4-year/100+year/400+arr[month-1]+day)%7);
}

int main()
{
    int day,month,year;
    cout<<"Enter the Date for which day of the week need to be find (DD/MM/YYYY)."<<endl;
    cin>>day>>month>>year;
    int x = dayofweek(day,month,year);
    if(x==0)
        cout<<"Sunday"<<endl;
    else if(x==1)
        cout<<"Monday"<<endl;
    else if(x==2)
        cout<<"Tuesday"<<endl;
    else if(x==3)
        cout<<"Wednesday"<<endl;
    else if(x==4)
        cout<<"Thursday"<<endl;
    else if(x==5)
        cout<<"Friday"<<endl;
    else if(x==6)
        cout<<"Saturday"<<endl;

}