1

我正在写一个简单的日历类。我试图超载operator++使用它将日历移动到下个月。但是,我找到下个月开始日的算法并不完全正确。

calendar calendar::operator ++(int)
{
   int hold;
   calendar cal = *this;

   month++;
   if (month > December)
   {
      month = January;
      year++;
      if (year == 0)
         year++;
   }
   previousStartDay = startDay;
   startDay = nextStartDay;
   nextStartDay = findNextStartDay();
   return cal;
}

int calendar::findNextStartDay() const
{
   int monthLength,
       day = startDay;

   monthLength = findMonthLength(false);
   monthLength -= 28;
   day += monthLength;
   if (day > Saturday)
      day -= Saturday;
   return day;
}

一月定义为0,十二月为11,周日为0,周六为6。 startDay、previousStartDay、nextStartDay、month、year都是私有类变量

当我在 2013 年对此进行测试时,日期在 3 月之前都是正确的。此时它将下一个开始日设为星期二而不是星期一。

我也试过这个:

int calendar::findNextStartDay() const
{
   int monthLength,
       day = startDay;

   monthLength = findMonthLength(false);
   monthLength -= 28;
   day -= monthLength;
   if (day < Sunday)
      day += Saturday;
   return day;
}

但是,它也给出了相同的结果。

编辑:

我计算的是闰年。这是我的代码,findMonthLength()用于确定是否存在。

if ((!(year % 4) && (year % 100)) || !(year % 400))
   monthLength = 29;
else
   monthLength = 28;
4

4 回答 4

3

问题分析

假设我们在 3 月,并且您有正确的开始日期(星期五,5)。

您的findNextStartDay算法将发现monthlength等于 3 (31-28),然后天将是 2 (8 - 6),即星期二 (2) 而不是星期一 (1)...

让我们通过运行算法来看看为什么这是错误的(第一个版本findNextStartDay):

一月:31-28 = 3,day = 2(星期二)+ 3 = 5(星期五),这是 2 月的正确开始日期。

二月:28 - 28 = 0,天 = 5(星期五)+ 0 = 5(星期五),这是三月的正确开始日期。

三月:31-28 = 3,天 = 5(星期五)+ 3 - 6(星期六)= 2(星期二),这是错误的四月开始日期。

错误说明

问题是,当您将星期六减去溢出的结果(超过星期六)时,您将在计数之外留出一天(即:您减去一天比您想要的少)。

想想你最终遇到的情况day == 7。你会想要星期天(比星期六多一个 - 循环增加),那么你必须删除 7,而不是 6,否则你会得到星期一!

错误在于循环增量:在正确的算法中,超过 6(即 7)的 1 必须回到 0,超过 6(即 8)的 2 必须回到 1,依此类推。

在您的算法中,超过 6(即 7)的 1 会回到 1,忽略可怜的 0(星期日),并且每次在这种情况下结束时都会使一周中的某一天消失。

如果你减去Saturday + 1,你会得到下个月的正确日期,以防“工作日溢出”。

错误修复

简而言之,改变这一行:

day -= Saturday;

day -= (Saturday + 1);

但是,请考虑将您的代码审查为更简洁的算法版本!

一个小技巧是使用模运算符进行循环加法:

day = ((day + monthlength) % (Saturday + 1))
于 2013-05-02T20:48:13.827 回答
2

boost 给了你几个很好的例子。在这里,我boost::gregorian基于示例中的一个实现。此代码需要年、月并打印下个月第一天的日期和星期几:

#include <cstdlib>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
#include <stdio.h>

int main(int argc, char** argv) {

    using namespace boost::gregorian;

    greg_year year(1400);
    greg_month month(1);

    // get a month and a year from the user
    try {
      int y, m;
      std::cout << "   Enter Year(ex: 2002): ";
      std::cin >> y;
      year = greg_year(y);
      std::cout << "   Enter Month(1..12): ";
      std::cin >> m;
      month = greg_month(m);
    }
    catch(bad_year by) {
      std::cout << "Invalid Year Entered: " << by.what() << '\n'
        << "Using minimum values for month and year." << std::endl;
    }
    catch(bad_month bm) {
      std::cout << "Invalid Month Entered" << bm.what() << '\n'
        << "Using minimum value for month. " << std::endl;
    }

    // create date and add one day to the end of month
    date d(year, month, 1);
    d=(year,month,d.end_of_month());
    date_duration dd(1);
    d += dd;
    // print date
    std::cout << d << " " << d.day_of_week() << std::endl;
    return 0;
}

示例输出:

输入年份(例如:2002):2013

输入月份(1..12):3

2013-4-01 周一

运行成功(总时间:6s)


使用std::vector

boost::gregorian::date d1(2013,boost::gregorian::Jan,31);
boost::gregorian::date d2(2013,boost::gregorian::Feb,28);
boost::gregorian::date d3(2013,boost::gregorian::Mar,31);

std::vector<boost::gregorian::date > v;
v.push_back(d1);
v.push_back(d2);
v.push_back(d3);

boost::gregorian::date_duration duration(1);

for(std::vector<boost::gregorian::date >::iterator it=v.begin();it!=v.end();it++){
    *it+=duration;
    std::cout << *it <<" "<< (*it).day_of_week() << std::endl;
}
于 2013-05-02T21:08:17.783 回答
0

我认为问题出在 2 月,因为这个月可以有 29 天或 28 天(取决于该年是否是 bissextile 年)。您可以为这个月创建一个 if 语句。您可以使用 mod 运算符判断年份是否为 bissextile:年份 % 4=! 0 比二月有 28 天,否则你有 29 天。希望对你有帮助!

于 2013-05-02T20:33:37.030 回答
0

此代码为您提供上个月的第一天和最后一天。我用它在我的应用程序中设置 TDateTimePicker 组件。

Word Year, Month, Day;
TDateTime datum_tdatetime = Date();

// first day of actual month
datum_tdatetime.DecodeDate(&year, &month, &day);
day = 1;
datum_tdatetime = EncodeDate(year, month, day);
// last day of previous month
datum_tdatetime -= 1;
// first day of previous month
datum_tdatetime.DecodeDate(&year, &month, &day);
day = 1;
datum_tdatetime = EncodeDate(year, month, day);
于 2017-11-09T10:31:33.203 回答