这个标题我太熟了。刚学编程那阵子天天在控制台里敲new Date()盯着那一长串输出发懵年份、月份、日期、时分秒都在眼前唯独“今天周几”这个最常用、最直觉的信息反而要再查一眼文档才能拿到。后来才懂真正干活的其实是一个方法getDay()。而这个需求理论上两行代码就能满足。但你别急着把这两行贴进项目里。“告诉我今天周几”这个标题看着简单背后涉及的细节远比字面意思多。从“控制台输出一个数字”到“页面稳定显示周几”再到“程序自动判断工作日”每一步都埋着不同的坑。这篇文章就从这两行代码入手把日期星期处理的常见写法、语言差异以及真实项目里的隐藏问题串一遍。不论你刚入门还是已经写了几年业务代码应该都能挑到点用得上的东西。1. 先把“周几”这个需求拆开看1.1 “周几”不是简单一句话同样是“想知道今天是周几”不同场景下的要求完全不一样。最简单的场景是开发者在调试时看一眼控制台输出一个数字就够。稍微复杂一点的是网页或小程序界面上要显示“今天是星期四”这种自然语句这就不只是拿数字还得做本地化映射。再往下是业务逻辑要拿“周几”当条件分支周报系统要判断“是否周一”、会员系统要判断“是否会员日”、打卡应用要区分“工作日还是周末”。这三种层次难度天差地别。但它们的共同基础只有一个拿到一个可靠的“星期序号”。而这个序号恰恰是整个需求里最容易搞错的部分。1.2 三个典型场景“显示”“判断”“排程”先看第一类纯展示。这种需求最常见比如活动页顶部“限时抢购”旁边标注“周五开启”。实现上只需要 JS 的getDay()加一个字符串映射或者用toLocaleDateString一行也能搞定。这一类对实时性要求不高页面打开时算一次就行。第二类是逻辑判断。比如一个内部工具的规则是“工作日发提醒周末静默”。这类需求要小心的不是如何取到星期几而是不同系统、不同语言对“星期日/星期一”的编号逻辑完全不一致。在 JavaScript 里getDay()返回 0 代表星期日在 Python 里weekday()返回 0 代表星期一在 Shell 里date %u又是周一为 1。同一个逻辑换一种语言实现结论可能完全相反。第三类是排程。定时任务、批处理脚本、周期报表通常不靠运行中的代码逐日判断而是交给调度器直接配置“每周几执行”。如果业务代码里已经有一套“星期几”的映射逻辑还非要自己判断反而容易出现时区、周起始日等叠加问题。1.3 真正的核心一个容易搞错的数字如果把“今天周几”的问题压缩到最小粒度其实就是在问当前时间属于一周中的第几个位置。JavaScript 的答案是一个 0 到 6 的整数getDay()返回 0 表示周日1 表示周一依此类推。这个“0 代表周日”的设计让不少新手栽过跟头。有人周三调用getDay()得到 3觉得“没毛病”等到周日再调用得到 0就开始怀疑人生。实际上它的含义是“在星期序列中的下标”只不过这个序列是从周日开始的。想验证这件事很简单在控制台敲一行new Date().getDay()再对照日历看一眼马上就能明白。注意先别急着背映射表先搞清楚当前语言里“0”到底是一周的开始还是第二天的位置。不同语言规则不同这是所有星期相关错误的第一大来源。2. JavaScript 两行代码的完整过程2.1 两行代码与逐行解读JS 里最朴素的写法是这样const today new Date(); const week 日一二三四五六[today.getDay()]; console.log(今天是周${week});严格来讲这里可以拆成两行逻辑主体第一行new Date()创建一个表示“当前时刻”的日期对象它携带了完整的年月日时分秒信息。第二行用getDay()取出星期下标接着用字符串“日一二三四五六”直接按下标取值得到对应中文。如果希望输出“星期日”这种完整说法把字符串换成星期日一二三四五六但这样下标 0 就对应“星期日”下标 1 对应“一”不是“星期一”需要额外调整不够优雅。如果想极限压缩甚至可以写成一行console.log(今天是周${日一二三四五六[new Date().getDay()]});但我不建议你平时这么写。一行代码虽然“够秀”可读性却差而且如果后续要扩展“星期几”的多种前缀重构起来很别扭。我个人看法是标题里的“两行”反而是最佳平衡一行负责取时间一行负责映射翻译逻辑清晰又足够短。2.2 为什么是“日一二三四五六”而不是“一二三四五六日”这个顺序问题是真正理解getDay()的关键。在数组或字符串里每一个元素的位置对应一个下标。日一二三四五六[0]取到的是“日”因为getDay()返回 0 时表示星期日。如果你把字符串写成一二三四五六日那么周日那天也就是getDay()返回 0 时打印出来的会是“一”整整错了一天而且是那种不仔细看根本发现不了的隐性错误。很多人写第一次就栽在这里因为中文习惯里“星期一、星期二……”的序列是自然的想当然就把“一”放最前面。但在 JavaScript 的序列规则里一周从星期日开始。只要把“日”放在 0 号位后面依次“一、二、三、四、五、六”就不会错位。我的记忆方式很简单把getDay()的下标想象成一个环形数组0 在顶部顺时针依次是一、二、三、四、五、六。这个环形不从“一”开始而从“日”开始。2.3 对比一下几种“今天周几”的写法除了数组映射JS 里还有原生方法可以直接返回星期。常见的三种对比如下写法示例输出优点注意点getDay() 字符串映射周四可控、稳定、直观映射顺序必须正确toLocaleDateString(zh-CN, { weekday: long })星期四代码最少不同环境输出格式可能有差异Intl.DateTimeFormat(...).format()周四国际化规范行数略多性能开销稍大toLocaleDateString的写法适合快速查看但它在不同运行时里可能返回“星期四”“周四”“Thursday”不同风格的文本依赖环境的默认行为。如果你只是想要“周几”这个简短表达自己维护映射反而是最稳的。如果项目里对日期格式有统一要求我更建议把“取星期”的代码封装成一个独立函数而不是在业务代码里到处写getDay()加字符串。这个习惯在大型项目里尤其重要否则一旦要改文案前缀全工程搜索替换会非常痛苦。3. 其他语言也能“两行”跨语言对照3.1 Pythonweekday()和isoweekday()的反直觉区别Python 大概是这类问题里最容易踩坑的语言因为它有两个函数长得非常像语义却不同。from datetime import datetime today datetime.now() print(今天是周 一二三四五六日[today.weekday()])这段代码有个关键细节weekday()返回的 0 表示周一6 表示周日和 JavaScript 的getDay()规则正好错开一天。所以映射字符串的顺序必须从“一”开始到“日”结束。如果是这样写就错了print(今天是周 日一二三四五六[today.weekday()])这个错误很隐蔽因为只有恰好今天不是周一的时候输出结果才是错的而且你对着日历人工核对时可能还会疑惑“周三怎么输出周四了”。Python 还有一个isoweekday()返回 1 到 7周一是 1周日是 7。它和weekday()的区别不仅体现在数值偏移还体现在“周日到底是 7 还是 0”上。写脚本时如果拿isoweekday()的结果去和“周日为 0”的常量比较同样会翻车。3.2 Java、Shell、Go 的写法与各自陷阱Java 里用 JDK 8 之后的java.time包非常舒服java.time.LocalDate date java.time.LocalDate.now(); String[] week {周一, 周二, 周三, 周四, 周五, 周六, 周日}; System.out.println(今天是 week[date.getDayOfWeek().getValue() - 1]);这里的getDayOfWeek().getValue()返回值是 1 到 7分别对应周一到周日。由于是 1 开始直接用数组映射时必须减 1否则会数组越界或者错位。Shell 里用 GNU date 可以写得更简单但要注意两个参数的区别echo 今天是周$(date %w) # %w周日为0周一到周六为1-6这行看似直接实际有问题。因为周日的输出会变成“周0”而不是“周日”。不想看“周0”的话需要一次换算echo 今天是周$(( ( $(date %w) 6 ) % 7 1 ))思路是把 0 到 6 的%w结果先整体偏移让周日变成 7再通过取模映射到中文顺序上。这里的(w 6) % 7 1是一个值得记下来的小公式它能把“周日为 0”的下标转换成“从周一开始计数的序号”。Go 的写法和 JavaScript 很像因为time.Now().Weekday()返回的time.Weekday底层也是 0 代表周日。但 Go 有一个特别的坑字符串下标访问的是字节不是字符。直接写日一二三四五六[time.Now().Weekday()]得到的会是乱码字节因为中文字符在 UTF-8 里占三个字节。正确做法是先定义字符串切片week : []string{日, 一, 二, 三, 四, 五, 六} fmt.Printf(今天是周%s, week[time.Now().Weekday()])这个细节很多从 JS 转 Go 的开发者都会踩一次。3.3 一张表记住各语言起点整理成对照表更直观语言/命令函数/参数返回范围起始日说明JavaScriptgetDay()0-6周日和数组映射“日一二三四五六”匹配Pythonweekday()0-6周一映射“一二三四五六日”Pythonisoweekday()1-7周一ISO 标准JavagetDayOfWeek().getValue()1-7周一数组下标记得减 1Shelldate %w0-6周日和 JS 一致但周日出“0”Shelldate %u1-7周一ISO 标准GoWeekday()0-6周日和 JS 一致注意中文切片这张表建议收藏。以后只要遇到“今天是星期几”的跨语言问题先查一下这个语言是“周日起始”还是“周一起始”就能避免九成错误。4. 从“两行代码”到生产环境的四道坎4.1 时区服务器和你不在同一个“今天”代码能在本地跑通不代表上线没有问题。new Date()在浏览器里取的是用户本机时间在 Node 服务里取的是服务器系统时间。如果服务器部署在 UTC 时区中国用户凌晨 00:30 提交的打卡数据按服务器时间算还是昨天星期几也会随之后移一天。这种问题特别隐蔽因为大多数开发者白天开发调试本国时区和服务器时区恰好一致不会注意到差异等到项目上线用户投诉“打卡日期不对”时才一层层排查到时区头上。处理方案分三层前端展示直接用前端本地的当前时间这最符合用户的直觉。后端逻辑需要判断“业务时区的星期几”时不要直接用服务器本地时间而是通过请求里的时区偏移量换算或者把时区信息显式传给日期库。持久化存储尽量用带时区的 UTC 时间戳只在展示层转换成业务时区。如果确实需要按某个固定时区取星期几可以用Intl.DateTimeFormat的timeZone参数const formatter new Intl.DateTimeFormat(zh-CN, { timeZone: Asia/Shanghai, weekday: long }); console.log(formatter.format(new Date()));这行代码按指定时区输出“星期四”从原理上绕开了服务器时区的影响。缺点是代码有点长适合在专门的工具函数里封装不适合到处都是。4.2 周起始日日历组件被吐槽的经典场景第二个容易翻车的点是周起始日。中文产品习惯认为周一是一周的开始日历年历上通常是“周一、周二……周日”排成一列而很多国际化的组件默认周日是一周的起点直接拿getDay()当列索引时周日会排在第一列。如果你正在做一个日历页想实现“周一开头”的布局需要对下标做一次重映射const columnIndex (dayIndex 6) % 7;验证一下周日的dayIndex是 0(0 6) % 7得 6排最后一列周一得 0排第一列其他依次类推。这个公式记住一个就能举一反三想让周几变成“第一天”就把它和周日之间的偏移量加到 6 上面再取模。注意周起始日不是编程问题是产品问题。做日历之前先确认对齐的是目标用户的习惯再决定使用哪种下标转换公式。4.3 文案与定时任务别把“显示”写成“逻辑”第三道坎是文案本地化。同一个意思有人习惯说“周几”有人习惯“星期几”还有人习惯“礼拜几”。如果代码里到处硬编码“周”这个前缀后续产品想统一改成“星期”要改动的地方会很多还容易遗漏。更稳健的做法是把“取星期几”和“选择前缀文案”拆开。取数字的逻辑只负责返回一个稳定可靠的下标展示层再决定拼接什么前缀。这样文案调整就不动核心逻辑测试也更容易覆盖。第四道坎和定时任务相关如果是“每周一凌晨执行跑批”这类需求优先使用操作系统的 Cron 或任务调度器直接配置周字段而不是写一个守护进程每天运行再去判断“今天是不是周一”。也许有人觉得后者不够“程序员”但前者的可靠性高得多也更容易审计。调度器处理星期字段和本地时区已经非常成熟没必要自己重复造一套容易出错的判断逻辑。5. 实操排错与心得5.1 高频踩坑速查表把常见的异常现象和真正原因整理成一个速查表排查时对着找效率会高很多表面现象真正原因解决办法周三输出 4误把getDay()当成“周几的数字”对照映射表getDay()下标从周日开始Python 里周一输出成周日混淆了weekday()和isoweekday()的起点记住weekday()周一为 0isoweekday()周一为 1服务器显示的星期比中国用户晚一天服务端处于 UTC 等非目标时区用Intl指定业务时区或按请求时区换算周日被排到日历第一列产品需要周一开头但直接用了getDay()用(day 6) % 7重映射输出显示为“周undefined”数组越界或映射表长度不足先打印getDay()的原始值确认在 0-6 内Go 代码输出中文乱码对中文字符串用下标直接取字节用[]string切片代替字符串下标这些坑基本覆盖了九成“今天周几”相关的线上问题。尤其最后一条新手排查起来经常一头雾水因为它不是逻辑错误而是语言底层机制导致的。5.2 用可测试的结构避免改系统时间很多人写完“今天周几”相关函数后想验证对不对第一反应是改系统时间然后刷新页面。这个方法很原始而且容易污染开发环境。更好的做法是让函数接收一个可选的日期参数function getWeekdayText(date new Date()) { return 今天是周${日一二三四五六[date.getDay()]}; } // 边界测试2025年4月6日是周日4月7日是周一 console.log(getWeekdayText(new Date(2025, 3, 6))); // 今天是周日 console.log(getWeekdayText(new Date(2025, 3, 7))); // 今天是周一注意这里构造日期时月份从 0 开始所以 4 月对应3。这是 JavaScript 的另一个经典反直觉设计写测试用例时尤其容易踩到。这种“把依赖注入进去”的写法不需要引入任何测试框架直接在 Node 或浏览器控制台运行就能覆盖边界日期。我习惯在写完日期相关函数后顺手把周一、周日、月末最后一天都跑一遍确认没有移位和越界问题。如果不想依赖浏览器也可以用node命令行直接跑效果完全一致。养成这个习惯以后你会发现自己不再需要频繁修改系统时间也不会因为改回去之后忘记时间不同步而引来更多麻烦。5.3 一个可直接复制的小工具函数最后把我常用的工具函数贴出来兼顾前缀切换和日期注入function formatWeekday(date new Date(), prefix 周) { const dicts { 周: 日一二三四五六, 星期: 日一二三四五六, 礼拜: 日一二三四五六 }; const dict dicts[prefix] || dicts[周]; return ${prefix}${dict[date.getDay()]}; } console.log(formatWeekday()); // 例如今天是周三 console.log(formatWeekday(new Date(), 星期)); // 星期三 console.log(formatWeekday(new Date(2025, 3, 6), 礼拜)); // 礼拜日这个函数在多数项目里已经够用。如果项目里已经引入了社区常见的日期库直接用库提供的星期格式化能力当然也可以但若只是为了一处“今天是周几”的显示引入额外依赖有点重。原生实现不到十行性能、可维护性都更好。我个人这几年写日期处理最深的体会是星期几这个看似基础的功能坑往往不在“取数”而在“映射”和“时区”。取数永远是引擎帮你做好的事但映射规则每个语言都不一样时区更是悄无声息地改变结果。写完这两行代码之后记得顺手把边界日期测一遍。我靠这个习惯挡掉过不止一次“线上日期差一天”的事故。