C++20可用std::year_month_day转sys_days再调weekday()得星期(0=Sunday);C++11/17用std::tm+mktime,注意tm_year/tm_mon偏移及初始化-1;嵌入式可用Zeller公式,但需处理月份调整和模运算防负。

用 std::chrono 和 std::year_month_day 直接算(C++20 起)
如果你的编译器支持 C++20(如 GCC 12+、Clang 14+、MSVC 19.30+),这是最干净的方式:构造 std::year_month_day,转成 sys_days,再用 .weekday() 提取星期。
关键点是:必须先转成系统时间点(sys_days),不能直接对 year_month_day 调用 weekday() —— 否则会编译失败。
示例:
#include <chrono>
#include <iostream>
<p>int main() {
using namespace std::chrono;
auto ymd = year{2024}/month{3}/day{15}; // 2024-03-15
auto wd = sys_days{ymd}.weekday();
std::cout << (unsigned)wd << "\n"; // 输出 5(星期五,0=Sunday)
}-
weekday的值是 0~6,0 表示 Sunday,6 表示 Saturday - 月份和日期必须在合法范围内(如
month{13}或day{32}会导致未定义行为) - 不校验年份有效性(比如
year{-1000000}也能过编译,但运行时可能溢出)
兼容 C++11/14/17:用 std::tm + mktime
老标准下没有日历类型,得靠 C 风格的 std::tm。注意:必须显式设 tm_year(从 1900 起算)、tm_mon(0 起算)、tm_mday(1 起算),且调用 mktime 前要把 tm_wday 和 tm_yday 设为 -1,否则结果不可靠。
立即学习“C++免费学习笔记(深入)”;
示例:
#include <ctime>
#include <iostream>
<p>int main() {
std::tm t{};
t.tm_year = 2024 - 1900; // 必须减 1900
t.tm_mon = 3 - 1; // 3 月 → 2
t.tm_mday = 15;
t.tm_wday = -1; // 关键:告诉 mktime 忽略当前 weekday
t.tm_yday = -1;</p><pre class="brush:php;toolbar:false;">std::mktime(&t);
std::cout << t.tm_wday << "\n"; // 输出 5(同上,0=Sunday)}
-
mktime会规范化越界值(如tm_mon=13会被自动进位),这点比手动计算更鲁棒 - 跨平台安全:Windows、Linux、macOS 行为一致
- 但
tm只支持约 1970–2038 年(32 位 time_t),超出范围可能返回 -1 或错误值
手动实现 Zeller 公式(无依赖,适合嵌入式或教学)
如果连 <ctime></ctime> 都不能用(比如裸机环境),Zeller 同余法是最轻量的选择。注意它对格里高利历(现行公历)有效,且需区分 1–2 月为上一年的 13–14 月处理。
公式(以 C++ 实现):
int zeller(int y, int m, int d) {
if (m < 3) { y--; m += 12; }
int c = y / 100, Y = y % 100;
int w = (Y + Y/4 + c/4 - 2*c + 26*(m+1)/10 + d - 1) % 7;
return (w + 7) % 7; // 确保非负
}- 返回值:0 = Saturday,1 = Sunday,…,6 = Friday(和
tm_wday不同,需留意) - 只适用于公元 1582 年 10 月 15 日之后的格里高利历日期;儒略历需换公式
- 整数除法截断向零,所以用
(w + 7) % 7防负数取模出错
常见错误:误用 localtime 或忽略时区
有人试图用 time_t t = 0; localtime(&t) 得到基准日再推算,这非常危险——time_t(0) 是 UTC 时间,而 localtime 会按本地时区偏移,导致基准错乱。哪怕你只算“今天”,也应避免依赖本地时区做日期运算。
- 所有基于
std::tm的方法,只要用了localtime或gmtime,就必须明确你想要的是本地星期还是 UTC 星期 - C++20 的
sys_days默认是 UTC,无歧义;而local_days需配合时区库(如<chrono></chrono>扩展)才安全 - 测试时别只用 2024 年 3 月——试试 2000 年 2 月 29 日、1900 年 3 月 1 日(不是闰年)、2100 年 2 月 28 日,这些才是真边界
实际项目中,C++20 方案最省心,但要注意编译器版本和 std::chrono::year 的隐式转换限制;老项目用 mktime 最稳;Zeller 只在极端受限环境下值得写。最容易被忽略的是:不同 API 对“星期几”的编号起点完全不同,混用前务必查清文档。


















