- 教程
- 文档
【免费下载链接】30-seconds-of-code
Coding articles to level up your development skills
导读
判断某个日期是否处于「营业时间」是业务开发中常见的需求:客服系统需要只在工作时间响应、订单系统需要区分工作日与周末的配送策略、报表工具需要过滤非交易时段的数据。本文以 30-seconds-of-code 仓库中的 date-inside-business-hours.md 为核心,讲解两条完整的技术路线:一是基于原生Date对象的轻量方案,二是基于Intl.DateTimeFormat的跨时区方案。读完本文,你将掌握如何用不到 20 行原生代码实现营业时间判断,也能应对「用户与服务器不在同一时区、还需排除节假日」的复杂场景。
基于Date对象的轻量方案
JavaScript 的原生Date对象内置了足够的方法来完成日期与时间的基础运算,这也是 30-seconds-of-code 全仓库坚持「不依赖库、用原生 API 解决 80% 需求」的一贯风格——仓库中的 date-is-weekday-or-weekend.md 同样只用Date.prototype.getDay()配合取模运算就完成了工作日/周末判断。
核心 API:getDay()与getHours()
Date.prototype.getDay():返回一周中的第几天,取值范围是0(周日)到6(周六),其中1代表周一、5代表周五;Date.prototype.getHours():返回当天的小时数,取值0~23,9即上午 9 点、17即下午 5 点。
基于这两个方法,只需做两次区间比较即可得到结论:日期落在周一(1)到周五(5)之间,且小时落在 9(含)到 17(不含)之间,即视为处于营业时间。
完整实现
const startDay = 1; // Monday const endDay = 5; // Friday const startHour = 9; // 9:00 AM const endHour = 17; // 5:00 PM const isInsideBusinessHours = (date = new Date()) => { const day = date.getDay(); const hour = date.getHours(); return day >= startDay && day <= endDay && hour >= startHour && hour < endHour; };这段代码有几个值得注意的设计点:
- 默认参数
date = new Date():不传参时自动取当前时刻,传参时则可判断任意日期,例如isInsideBusinessHours(new Date('2024-03-13T10:00:00'))可直接用于单元测试; - 边界语义:
startHour使用>=(含 9 点),endHour使用<(不含 17 点)。这意味着9:00属于营业时间、17:00不属于,上下午边界清晰无重叠; - 配置与逻辑分离:
startDay、endDay、startHour、endHour四个常量被独立提出来,方便按业务调整,无需改动判断逻辑本身。
[!NOTE]
上述代码默认配置为周一至周五、上午 9:00 至下午 5:00的营业时间。你可以通过修改
startDay、endDay、startHour、endHour四个变量来匹配自己的营业时段,例如把startHour改为8、endHour改为22,即可覆盖「早 8 点到晚 10 点」的排班。
方案的局限
轻量方案的最大前提是:运行环境(服务器/浏览器)的本地时区就是业务时区。getDay()与getHours()返回的都是执行环境本地时间。一旦用户分布在多个时区,或者服务器部署在 UTC 而业务发生在「欧洲/雅典」,这个方案就会产生偏差。这正是下文Intl.DateTimeFormat方案存在的意义。
基于Intl.DateTimeFormat的跨时区方案
当用户与业务方不在同一时区时,需要先把日期时间格式化到目标时区再参与判断。Intl.DateTimeFormat是 ECMAScript 内置的国际化 API,可以按指定timeZone输出日期时间片段。
思路拆解
整体流程分三步:
- 用
Intl.DateTimeFormat()构造带timeZone选项的格式化器; - 用
Intl.DateTimeFormat.prototype.formatToParts()把日期拆解为weekday、day、month、hour等结构化片段; - 基于这些片段分别判断「是否在营业时段内」「是否为工作日」「是否为节假日」,三个条件同时满足才算营业时间。
formatToParts()返回的是对象数组,每个对象形如{ type: 'hour', value: '10' },其中type表示片段类型、value是对应的字符串值。相比直接调用format()拿到一整串字符串再正则匹配,formatToParts()的结构化输出更可靠、可读性也更好。
完整实现
const holidays = [ [1, 1], // New Year's Day [6, 1], // Epiphany [25, 3], // Greek Independence Day [1, 5], // Labour Day [15, 8], // Dormition of the Holy Virgin [28, 10], // Ochi Day [25, 12], // Christmas Day [26, 12] // Boxing Day ]; const dateTimeFormatOptions = { timeZone: 'Europe/Athens', year: 'numeric', month: 'numeric', day: 'numeric', weekday: 'narrow', hour: 'numeric', hour12: false, minute: 'numeric' }; const startHour = 9, endHour = 17; const isInsideHours = (hour) => hour >= startHour && hour < endHour; const isWeekday = (weekday) => weekday !== 'S'; const isHoliday = (day, month) => holidays.some(([hDay, hMonth]) => hDay === day && hMonth === month); const parseDateSegments = (date = new Date()) => new Intl.DateTimeFormat('en-US', dateTimeFormatOptions) .formatToParts(date) .reduce((acc, part) => { const { type, value } = part; if (type === 'weekday') acc.weekday = value; else if (type !== 'literal') acc[type] = parseInt(value, 10); return acc; }, {}); export const isInsideBusinessHours = (date = new Date()) => { const { weekday, day, month, hour } = parseDateSegments(date); return isInsideHours(hour) && isWeekday(weekday) && !isHoliday(day, month); };逐段解读
1. 节假日清单holidays
用[日, 月]二元组数组表示全年固定日期的法定假日(示例为希腊的公共假日,含元旦、主显节、希腊独立日、劳动节、圣母安息日、Ochi Day、圣诞节与节礼日)。isHoliday(day, month)通过Array.prototype.some()遍历比对,命中任意一组即返回true。这套数据结构可以很方便地替换成任意国家/地区的节假日表。
2. 格式化选项dateTimeFormatOptions
| 选项 | 取值示例 | 作用 |
|---|---|---|
timeZone | 'Europe/Athens' | 指定目标时区,这是跨时区判断的核心 |
year/month/day | 'numeric' | 输出数字形式的年月日 |
weekday | 'narrow' | 输出最窄形式的星期缩写(如M、T、S) |
hour/minute | 'numeric' | 输出数字形式的小时与分钟 |
hour12 | false | 强制 24 小时制,保证hour为0~23的数字,避免 AM/PM 歧义 |
注意:由于weekday使用了'narrow',周一至周六的缩写各不相同,而周六与周日均以S开头,因此代码用isWeekday = (weekday) => weekday !== 'S'同时排除周六、周日两个周末日——这是该方案的巧妙之处,但也意味着如果换成'short'(如Sat/Sun)或'long'(如Saturday/Sunday)就需要改写判断逻辑。
3. 片段解析器parseDateSegments
通过reduce把formatToParts()返回的数组收敛为{ weekday, year, month, day, hour, minute }对象:
weekday直接以字符串保存(用于'S'判断);- 其他非
literal类型的片段(year、month、day、hour、minute)用parseInt(value, 10)转成数字,便于数值比较; literal类型(如分隔符/、:)被跳过,不参与结果。
4. 最终判断isInsideBusinessHours
return isInsideHours(hour) && isWeekday(weekday) && !isHoliday(day, month);三个条件按「与」连接,任一项不满足即返回false:既要在9:00~17:00时段内、又要避开周末、还要排除节假日。
与轻量方案的对比
| 维度 | Date方案 | Intl.DateTimeFormat方案 |
|---|---|---|
| 时区 | 依赖运行环境本地时区 | 通过timeZone显式指定目标时区 |
| 周末判断 | 用getDay()数值区间(1~5) | 用weekday缩写排除'S' |
| 节假日 | 不支持 | 支持自定义holidays清单 |
| 代码量 | 约 10 行 | 约 40 行 |
| 适用场景 | 单一时区、快速判断 | 跨时区、含节假日规则的完整业务 |
仓库上下文:这份代码从哪里来
本文核心代码来自 30-seconds-of-code 仓库的 date-inside-business-hours.md,它归属于 JavaScript 语言集合中的date标签分类。在仓库的集合配置 date.yaml 中,该分类被描述为「使用原生 JavaScript 格式化与操作日期」的 ES6 文章集合,定位与本文展示的「零依赖实现」完全一致。
同系列的相关 snippets 可以帮你把「营业时间」判断扩展成一套完整的日期工具链:
- date-is-weekday-or-weekend.md:不依赖
Intl的轻量工作日/周末判断,用getDay() % 6一行搞定; - date-comparison.md 与 date-difference.md:日期比较与差值计算,可用于统计营业时段内的耗时;
- add-minutes-hours-days-to-date.md:在日期上增减分钟/小时/天,可用于生成营业时间窗口或排班表。
边界情况与扩展建议
结合上文两套实现,在实际落地时还有几个值得注意的边界:
- 跨天班次:如果营业时间跨越午夜(如
22:00至次日06:00),startHour < endHour的简单比较不再成立,需要额外判断「hour >= startHour || hour < endHour」或引入日期偏移逻辑; hour12的重要性:省略hour12: false时,Intl可能输出 12 小时制的hour值,导致下午时段(如 17 点被格式化为5)判断错误,务必显式指定;weekday宽窄度依赖:isWeekday基于'narrow'下周六/周日同为'S'这一约定,修改选项时需同步调整判断逻辑;- 测试友好性:两个方案的函数都接收
date参数并默认取当前时间,测试时传入固定日期即可覆盖工作日、周末、节假日、时段边界等用例,无需依赖系统时钟。
小结
判断日期是否处于营业时间,本质上是「时段判断 + 星期判断 +(可选)节假日判断」的组合。30-seconds-of-code 给出了两条递进的实现路径:Date对象的getDay()/getHours()方案简洁直观,适合单时区场景;Intl.DateTimeFormat+formatToParts()方案则把时区、工作日、节假日统一收敛到一个可配置的判定函数中,适合面向全球用户或对营业规则要求完整的业务。两者都不依赖任何第三方库,代码量小、易于嵌入现有项目并按需调整边界常量。
- 教程
- 文档
【免费下载链接】30-seconds-of-code
Coding articles to level up your development skills
相关推荐
Elementor 组件嵌套规则深度解析:循环依赖校验、并发编辑锁与实例 ID 作用域
Elementor 组件嵌套规则深度解析:循环依赖校验、并发编辑锁与实例 ID 作用域 组件(Component)是 Elementor 原子化构建体系中用于复
教程文档30 seconds of code:用 JavaScript 判断元素是否在视口(viewport)内可见
30 seconds of code:用 JavaScript 判断元素是否在视口(viewport)内可见 导读 在无限滚动加载、懒加载、滚动动画触发、"回到
教程文档30 seconds of code:判断 JavaScript 数组是否有唯一匹配或多个匹配
30 seconds of code:判断 JavaScript 数组是否有唯一匹配或多个匹配 按条件在数组中查找元素,是日常开发中最常见的操作之一。本指南以
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考