今天到底是星期几?从数组遍历到 JavaScript 时区边界

举报
yd_241387476 发表于 2026/11/10 09:34:25 2026/11/10
【摘要】 从星期数组遍历出发,区分列表展示与真实日期,说明 getDay 索引、Intl 时区、纯日期解析和无效输入处理,并给出云端实验及 24 项断言验证。

我网站上有一道入门练习:把星期一到星期日放进数组,用循环逐行显示。题目很短,却包含一个容易忽略的区别:打印七个星期名称,只能说明程序遍历了数组,不能证明它知道今天是星期几。

本文从这道练习出发,重新用 JavaScript 实现,并补上真实页面需要处理的星期索引、时区和无效输入。原题使用 foreach;这里是新的 JavaScript 实现,不是把 PHP 代码直接改几个单词。

一、先把“展示列表”与“读取今天”分开

如果需求只是展示一周,数组和循环已经足够。下面的代码在浏览器控制台或 Node.js 中都能运行:

const mondayFirst = [
  '星期一', '星期二', '星期三', '星期四',
  '星期五', '星期六', '星期日'
];
for (const label of mondayFirst) {
  console.log(`星期标签:${label}`);
}

这段程序会输出七行。它没有读取系统时间,也没有判断日期,所以我把输出前缀写成“星期标签”。如果七行都写“今天是”,读者容易误以为程序正在获取日期;这就是展示文案与代码能力不一致。

网页展示时也要分清输出位置。console.log 写到开发者工具;想显示在页面上,可以先放一个容器,再用文本节点追加内容:

<ul id="week-list"></ul>
<script>
const labels = ['星期一','星期二','星期三','星期四','星期五','星期六','星期日'];
const list = document.getElementById('week-list');
for (const label of labels) {
  const item = document.createElement('li');
  item.textContent = `星期标签:${label}`;
  list.appendChild(item);
}
</script>

这里脚本位于容器之后,执行时能找到元素。用 textContent 是因为我们需要展示普通文字,不需要把标签当作 HTML 解析。这段页面例子表达的是渲染方法;下文的自动化断言验证的是日期逻辑,不把两者混为一谈。

二、getDay 的 0 是星期日

真正显示今天,可以从 new Date() 获取当前时刻。问题在于 getDay() 按本地时区返回星期编号,范围是 0 到 6,0 对应星期日;getDate() 返回的则是月内日期。二者名字相近,含义不同。

上面的数组以星期一开头。如果直接写 mondayFirst[now.getDay()],周日会显示星期一,其他日期也会错位。可以把编号转换成“周一从 0 开始”的下标:

const now = new Date();
const index = (now.getDay() + 6) % 7;
console.log(`今天是:${mondayFirst[index]}`);

推算一下就能发现原因:星期一的编号 1,转换后是 0;星期日的编号 0,转换后是 6。另一种办法是把数组改成“日、一、二、三、四、五、六”,直接用 getDay()。应让数组顺序与接口约定匹配,不要为了让某一天显示正确而随手加减一。

这个写法适合明确按运行环境本地时区展示的页面。若服务器部署在海外、用户分布在不同地区,就还缺少业务时区约定。

三、一个时刻可以对应两个不同的星期

用固定时刻做实验,比反复执行 new Date() 更容易复现。以下输入在 UTC 是 11 月 8 日晚间,在上海已经过了零点:

const instant = new Date('2026-11-08T16:30:00Z');
for (const timeZone of ['UTC', 'Asia/Shanghai']) {
  const text = new Intl.DateTimeFormat('zh-CN', {
    weekday: 'long',
    timeZone
  }).format(instant);
  console.log(timeZone, text);
}
// UTC 星期日
// Asia/Shanghai 星期一

同一段代码也在华为云免费 AI DevSpace 的 AI Shell 中执行过,输出与上面一致。日期对象仍表示同一个时刻,变化的是我们观察它的时区。

这里的 zh-CN 控制显示语言,Asia/Shanghai 控制时区。中文界面不自动等于北京时间;语言参数和时区参数需要分别表达需求。个人本地日历可以跟随设备时区,统一按北京时间结算的业务页面则应显式指定时区,前后端采用同一规则。

服务端渲染与浏览器渲染也需要一致。如果服务端按 UTC 计算、浏览器按本地时间计算,午夜附近可能出现首次页面与更新后文字不同。排查时应记录同一个时间值和两端的时区设置,先核对输入,再判断显示逻辑。

四、把输入边界写进函数

可以把展示逻辑封装起来,让调用者传入 Date 对象和明确的时区。下面的函数适用于普通应用中当前 JavaScript 执行环境创建的 Date;跨 iframe 的对象传递需要另外约定数据格式。

function weekdayAt(date, timeZone = 'Asia/Shanghai') {
  if (!(date instanceof Date)) {
    throw new TypeError('请传入 Date 对象');
  }
  if (!Number.isFinite(date.getTime())) {
    throw new RangeError('日期无效');
  }
  return new Intl.DateTimeFormat('zh-CN', {
    weekday: 'long', timeZone
  }).format(date);
}
console.log(`今天是:${weekdayAt(new Date())}`);

这里刻意不接受字符串自动转换,防止接口悄悄替调用者猜测日期格式。无效 Date 必须显式失败;时区名称无效时,Intl.DateTimeFormat 也会抛出异常。上层可以把异常转为“日期不可用”,不应兜底成星期日,因为星期日是一个真实结果。

还有一个常见边界:new Date('2026-11-09') 这样的标准纯日期字符串按 UTC 零点解释;它在洛杉矶对应前一天,因此显示星期日是可以复现的结果。若表达一个瞬间,接口可使用带 Z 或明确偏移量的日期时间字符串;若表达生日、账期等纯日历日期,应单独设计日期字段与时区规则,避免随意当作一个 UTC 瞬间处理。

例如下面两个字符串表示同一时刻:

const a = new Date('2026-11-08T16:30:00Z');
const b = new Date('2026-11-09T00:30:00+08:00');
console.log(a.getTime() === b.getTime()); // true

五、固定输入,验证最容易出错的边界

本次用 Node.js v26.3.0 执行了 24 项断言,覆盖七个星期的索引转换、错误索引反例、UTC/上海/洛杉矶的显示差异、上海零点前后一毫秒、纯日期解析、月份从零开始、无效日期、错误类型、错误时区,以及格式化不会修改原时间值。

24 项断言在系统默认时区下通过,又分别在 TZ=UTC 和 TZ=America/Los_Angeles 下通过。后两次用于检查代码是否意外依赖开发机时区;这些结果属于本次 Node 环境验证,不代表已经对所有浏览器或全部历史时区做了兼容性测试。

复制前面的 weekdayAt 后,下面这组关键断言可以先跑起来:

const assert = require('node:assert/strict');
assert.equal(weekdayAt(new Date('2026-11-08T16:30:00Z'), 'UTC'), '星期日');
assert.equal(weekdayAt(new Date('2026-11-08T16:30:00Z')), '星期一');
assert.equal(weekdayAt(new Date('2026-11-08T15:59:59.999Z')), '星期日');
assert.equal(weekdayAt(new Date('2026-11-08T16:00:00.000Z')), '星期一');
assert.throws(() => weekdayAt(new Date(NaN)), RangeError);
assert.throws(() => weekdayAt('2026-11-09'), TypeError);
console.log('6 项关键断言通过');

练习数组遍历时,先确保七个标签按预期输出;要展示真实的今天,再明确日期来源、索引约定和业务时区。这样从课堂小题走到实际页面,功能边界才是清楚的。

来源与参考

原题来自本人网站“安全问答”的星期数组遍历练习:https://www.cveoy.top/t/topic/bEmc 。本文基于该题重新组织说明并编写 JavaScript 示例,使用 AI 辅助整理,运行结果经过上述实测。

星期编号语义:MDN Date.prototype.getDay,https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDay

日期解析边界:MDN Date.parse,https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse

语言与时区格式化:MDN Intl.DateTimeFormat,https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat

标准算法:ECMAScript Date.prototype.getDay,https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-date.prototype.getday

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。