给定日期的C++星期几

17

我正在尝试用c++编写一个简单的程序,根据给定的日期返回星期几。

输入格式为日、月、年。我无法处理闰年。当输入年份是闰年时,我尝试从变量a中减去1,但程序最终会崩溃并没有出现错误信息。

我会感激任何建议,但请尝试保持简单,因为我还是个初学者。对于愚蠢的问题,我深表歉意,请原谅我的错误,这是我第一次在这个网站上发帖。

#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);
}

3
当您在调试器中逐步执行时会发生什么? - MrEricSir
在处理日期时,没有所谓的简单程序(去问Jon Skeet吧)。那个功能已经存在了,为什么要重新发明它呢? - Paul Rooney
1
有一个简单的公式可以进行计算。请参见https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week。 - Dan Nagle
在互联网上搜索“c++计算星期几”,可以找到大量的示例。 - Thomas Matthews
y 已经是一个整数。因此,强制转换 (int) y/ 4 是多余的,并且 (int)y/4 == y/4 总是成立的。 - StoryTeller - Unslander Monica
显示剩余3条评论
8个回答

24

首先:如果已经有标准化的函数可以处理相同的问题,请勿编写自己的函数。因为您可能会轻易地犯错(就像现在您的weekday()函数第一行已经出现了一个错误),而标准化函数的实现已经经过了彻底的测试,您可以确信它们会提供您期望得到的结果。

话虽如此,这里提供一种使用std::localtimestd::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;
}

3
谢谢你的建议,但写这个程序的整个目的是为了练习编程,并试图掌握c++的语法。我不会怪你提出完全不同的方法,因为我在文章中没有提到这一点。但如果我在编写更复杂的程序时需要类似的东西并有更多经验,我一定会考虑这种方法! - OerllydSaethwr
一年时间, ~1800 次浏览,却没有一个赞 oO。刚刚修复了它。@OerllydSaethwr 请通过点击其分数旁边的绿色勾号接受此答案(或其他答案)。 - YSC
1
关于 std::localtime 的解决方案是尽快复制其返回值:const std::tm time_out = *std::localtime(&time_temp); - YSC
@Striezel,为什么你要使用mktime?为什么不直接使用strftime呢? - aderchox
1
这似乎是在众多错误答案中唯一正确的答案。谢谢。 - Violet Giraffe
显示剩余3条评论

7

你对闰年的理解是错误的:

每4年是一个闰年除非它是100的倍数,但是如果该年份是400的倍数,则仍然是一个闰年。

如何计算“日期编号”(dn)的清晰简洁的解释可以在这里找到。

一旦你有了日期编号(dn),只需执行模7操作。结果将是星期几(dow)。

以下是一个示例实现(不检查日期是否有效):

#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;
}

7

因为工具的变化,所以现在针对旧问题提供新答案...

C++20规范说明以下内容将具有与问题中代码意图相同的功能:

#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';
}

现在可以使用这个 免费、开源的日期/时间库 来尝试这种语法,只是日期对象在命名空间date中而不是std::chrono中,并且格式字符串的语法略有改变。

#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';
}

2
谢谢你的代码 Howard。不幸的是,最新的g++和clang++似乎仍然没有准备好parseformat(使用-std=c++20)。我指的是g++-11和clang++ 13。我说的是没有包含头文件“date/date.h”的代码部分:( - aafulei

6
你可以使用公历日期系统来自Boost C++ 库查找给定日期的星期几。以下是一个简单的例子:
#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';
}

这段代码打印出 Monday


3

我曾经遇到了同样的问题,但是我找到了一个简单的解决办法。

根据这篇文章:
"以下是由Sakamoto、Lachman、Keith和Craver提供的计算日期的简单函数。此函数返回0代表星期天,1代表星期一,以此类推。"

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;  
}

它是否考虑了闰秒等因素?它会考虑未来的闰秒吗?看起来过于简单,无法严谨地正确。 - Violet Giraffe

0

尝试使用CTime类

例子:

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";
}

在上面的例子中,我使用了当前时间,但您可以使用您想要的时间来进行不同的操作。例如:
const CTime currTime = CTime(year,month, day, hours, minutes, seconds );

0

当一个数字能够被7整除时会发生什么?

14 / 7 = 2 14 % 7 = 0

取模运算符(% n)将返回从0到n-1的数字

如果n被7整除,余数永远不可能是7 因此

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 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";
        }

2
不要听那些说不要使用自己的函数的人。 - ComradeJoecool
就我们所知,你可能是出于兴趣或学习经验而这样做。 - ComradeJoecool
请注意,您最终的程序将无法正确计算星期几。这是因为闰年是棘手的事情,并不总是每4年发生一次。例如,闰年不会在100年发生,但会在400年发生。所以,日历可能会变得非常棘手。我的答案主要是帮助您找到程序崩溃的原因。 - ComradeJoecool
谢谢,我甚至没有考虑过a可能是七的倍数,我会确保研究一下。 - OerllydSaethwr

-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;

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接