使用末日法则计算任何一天的星期几





5.00/5 (4投票s)
2002年6月28日
3分钟阅读

154290

2075
末日法则的C++实现,用于确定星期几。
引言
John Conway 发明了一种巧妙而简单的方法来计算一年中的任何一天,称为末日法则。我强烈推荐阅读 S.W. Graham 的文章,其中详细描述了该算法。基本上,通过知道给定世纪的 2 月 28 日或 29 日(闰年)是星期几,以及所有其他月份对应的日期是星期几,就可以确定一年中的任何一天。下表总结了哪些整数代表星期几以及哪些天代表月份和世纪的末日。
由于格里高利历每 400 年重复一次,因此实际上只需要记住四个世纪。例如,1955 年 11 月 5 日是星期几?(更重要的是,哪部电影使用了这个日期?)。
- 年份部分是 55
- 日期是 5
- 1900 年的末日世纪是 3
- 11 月的末日月份是 7
- 每年闰年,末日都会增加一天。
- 我们将末日世纪 + 年份部分 + Floor(年份部分 ÷ 4) % 7 相加
- 对于此示例:(3 + 55 + floor(55 ÷ 4)) % 7 = 1
- 由于 11 月的末日早于日期,我们必须计算偏移量。我们知道末日是第 7 天,而第 5 天是前 2 天,或者与一周后的同一天相同,即第 12 天,或者比第 7 天晚 5 天。因此,我们将 5 加到末日,即 12。12 ÷ 7 的余数是 5,这就是正确的日期。
- 最后,我们需要考虑闰年,所以 5 + 1 = 6,即星期六。
COleDateTime
类的日期限制为 公元 100 年 1 月 1 日至公元 9999 年 12 月 31 日,而CTime
类的日期限制为 公元 1970 年 1 月 1 日至公元 2038 年 1 月 18 日。DoomsdayDate
类可用于查找任何范围内(包括公元前日期)的星期几。
DoomsdayDate
class DoomsdayDate
{
//-----------------------------------------------------
// JulianBeforeGregorian refers to the weird dates
// between October 5-14, 1582 which were deleted from
// the Gregorian calendar. Technically the days never
// existed. The default is false which sets the date
// as invalid.
//-----------------------------------------------------
DoomsdayDate(bool JulianBeforeGregorian = true);
bool AD() { return ad_; }
bool BC() { return !ad_; }
// pass in ad = false for B.C. dates
//--------------------------------------------------
bool Set(int month, int day, int year, bool ad=true);
// Find the first, second, third, or fourth
// weekday in the specified month and year.
// Only A.D. dates are supported.
//----------------------------------------------
bool SetFirst(int weekday, int month, int year);
bool SetSecond(int weekday, int month, int year);
bool SetThird(int weekday, int month, int year);
bool SetFourth(int weekday, int month, int year);
const char* WeekdayStr(int weekDay);
// returns day of month
//---------------------------
int GetDay() { return day_; }
// returns int month
// 1 = January
// 2 = February
// ...
//-------------------------------
int GetMonth() { return month_; }
// returns int year
//-----------------------------
int GetYear() { return year_; }
// 0 == Sunday
// 1 == Monday
// 2 == Tuesday
// 3 == Wednesday
// 4 == Thursday
// 5 == Friday
// 6 == Saturday
//------------------
int Weekday();
void Print();
}
DoomsdayDate 星期几函数
末日法则的核心是Weekday
函数。
int Weekday()
{
int r = -1;
int x = 0, y = 0;
int ddcentury = -1;
int ddmonth = DoomsdayMonth(month_);
if( gregorian_ ) // Gregorian Calendar
{
int century = year_ - (year_ % 100);
ddcentury = DoomsdayCentury(century);
if( ddcentury < 0 ) return -1;
if( ddmonth < 0 ) return -1;
if( ddmonth > day_ )
{
weekday_ = (7 - ((ddmonth-day_) % 7 ) + ddmonth);
}
else
{
weekday_ = day_;
}
x = (weekday_ - ddmonth);
x %= 7;
y = ddcentury + (year_-century) + (floor((year_-century)/4));
y %= 7;
r = (x+y)%7;
}
else if( !ad_ ) // B.C -> AD Julian
{
int dd = -1;
if( year_ > 699 )
{
dd = (year_ - (year_ % 700) + 701) - year_;
}
else
{
dd = (year_ - (year_ % 28) + 29) - year_;
}
if( dd > 0 )
{
ddcentury = (((dd - (dd % 100)) / 100) * 6)%7;
x = ((dd%100)%7) + (int)floor((dd%100)/4)%7;
if( ddmonth > day_ )
y = ddmonth + day_;
else
y = day_ - ddmonth;
y %= 7;
x = ddcentury + x;
x %= 7;
r = (x+y)%7;
}
}
else // Julian Calendar
{
ddcentury = (((year_ - (year_ % 100)) / 100) * 6)%7;
x = ((year_%100)%7) + (int)floor((year_%100)/4)%7;
if( ddmonth > day_ )
y = ddmonth + day_;
else
y = day_ - ddmonth;
y %= 7;
x = ddcentury + x;
x %= 7;
r = (x+y)%7;
}
weekday_ = r;
return weekday_;
}
DoomsdayDate 用法
默认构造函数接受一个布尔值,该值指示是否应在 1582 年 10 月 15 日之前使用儒略历,默认为 true
。由于格里高利历省略了 1582 年 10 月 5 日至 14 日,因此这些不是有效日期。如果传入 false
,则所有日期都与格里高利历同步。
示例项目接受以下参数
MM DD YYYY [BC]
其中 BC 是可选的,用于指示日期是否为公元前。它还将输出 COleDateTime
计算出的星期几以进行比较。以下是一些示例程序的有趣结果
DoomsdayDate | COleDateTime |
Saturday 10/14/1066 | Sunday 10/14/1066 |
Sunday 12/7/1941 | Sunday 12/7/1941 |
Sunday 10/23/4004 B.C. | 无法处理公元前日期 |
Friday 12/31/9999 | Friday 12/31/9999 |
Saturday 1/1/10000 | ¦¦¦¦¦¦¦¦¦¦¦¦¦¦ 1/1/10000 |
美国的法定节假日
以下代码使用DoomsdayDate
类来计算所有美国的法定节假日。它包含在LegalHoliday.h文件中。
namespace // *see note below
{
DoomsdayDate NewYears(int year)
{
return DoomsdayDate(Month::JAN, 1, year);
}
DoomsdayDate MartinLutherKingJr(int year)
{
DoomsdayDate dd;
dd.SetThird(Weekday::MONDAY, Month::JAN, year);
return dd;
}
DoomsdayDate WashingtonsBirthday(int year)
{
DoomsdayDate dd;
dd.SetThird(Weekday::MONDAY, Month::FEB, year);
return dd;
}
DoomsdayDate MemorialDay(int year)
{
DoomsdayDate dd;
dd.SetFourth(Weekday::MONDAY, Month::MAY, year);
return dd;
}
DoomsdayDate IndependenceDay(int year)
{
return DoomsdayDate(Month::JUL, 4, year);
}
DoomsdayDate LaborDay(int year)
{
DoomsdayDate dd;
dd.SetFirst(Weekday::MONDAY, Month::SEP, year);
return dd;
}
DoomsdayDate ColumbusDay(int year)
{
DoomsdayDate dd;
dd.SetSecond(Weekday::MONDAY, Month::OCT, year);
return dd;
}
DoomsdayDate VeteransDay(int year)
{
return DoomsdayDate(Month::NOV, 11, year);
}
DoomsdayDate ThanksgivingDay(int year)
{
DoomsdayDate dd;
dd.SetFourth(Weekday::THURSDAY, Month::NOV, year);
return dd;
}
DoomsdayDate ChristmasDay(int year)
{
return DoomsdayDate(Month::DEC, 25, year);
}
};
今年的输出如下
Holidays 2002
=======================================
New Years is on a Tuesday 1/1/2002
Martin Luther King Jr. is on Monday 1/21/2002
Washington's Birthday is on Monday 2/18/2002
Memorial Day is on Monday 5/27/2002
Independence Day is on a Thursday 7/4/2002
Labor Day is on Monday 9/2/2002
Columbus Day is on Monday 10/14/2002
Veterans Day is on a Monday 11/11/2002
Thanksgiving Day is on a Thursday 11/28/2002
Christmas Day is on a Wednesday 12/25/2002
*关于匿名命名空间的说明
C++ 标准关于匿名或未命名命名空间及其用途的规定如下。
第 7.3.1.1 节第 2 段
"在使用 static 关键字声明命名空间作用域中的对象时,已弃用;未命名命名空间提供了更优越的替代方案。尽管未命名命名空间中的实体可能具有外部链接,但它们有效地限定了一个仅对其翻译单元唯一的名称,因此永远无法从任何其他翻译单元看到。"
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。