1. 为什么需要营业时间组件?
在电商、本地生活、O2O等互联网产品中,营业时间展示是个看似简单却暗藏玄机的功能点。我做过一个餐饮SaaS项目,商家后台配置的营业时间曾导致30%的客户投诉——问题就出在时间格式混乱和跨天营业的特殊场景处理上。
一个合格的营业时间组件需要解决:
- 多时段配置(如餐厅午市11:00-14:00,晚市17:00-21:30)
- 跨天营业处理(如酒吧20:00-次日3:00)
- 国际化时区适配
- 实时状态显示("正在营业"/"已打烊")
2. 组件设计核心思路
2.1 数据结构设计
用JSON数组存储时段配置是行业通用做法:
const businessHours = [ { day: 1, // 周一 periods: [ { start: "09:00", end: "12:00"}, { start: "13:30", end: "18:00"} ] }, // ...其他日期 ]关键细节:时间建议用24小时制字符串存储,避免AM/PM转换的本地化问题
2.2 跨天营业的特殊处理
遇到结束时间小于开始时间(如23:00-02:00)时,组件内部需要做日期进位计算:
// 判断是否跨天 const isOvernight = endTime < startTime // 计算剩余营业时长 if(isOvernight) { const endDate = new Date(`1970-01-02 ${endTime}`) // ...时间差计算逻辑 }3. React实现方案
3.1 基础组件结构
推荐使用Compound Components模式:
<BusinessHours> <BusinessHours.Config value={hours} onChange={handleChange} /> <BusinessHours.Display hours={hours} timezone="Asia/Shanghai" /> </BusinessHours>3.2 核心hooks封装
封装useBusinessHours Hook处理核心逻辑:
function useBusinessHours(initialHours) { const [hours, setHours] = useState(initialHours); // 校验时间格式 const validatePeriod = (period) => { if(period.start >= period.end) { throw new Error("结束时间必须晚于开始时间"); } }; return { hours, setHours, validatePeriod }; }4. 企业级功能扩展
4.1 节假日特殊配置
通过overrideDates字段支持特殊日期配置:
{ "regularHours": [...], "overrideDates": { "2023-10-01": [], // 国庆节休业 "2023-12-25": [{ start: "18:00", end: "24:00"}] // 圣诞节延长营业 } }4.2 实时状态计算
根据当前时间返回营业状态:
function getCurrentStatus(hours, timezone) { const now = new Date(); const tzNow = utcToZonedTime(now, timezone); return hours.some(day => day.periods.some(p => isWithinInterval(tzNow, { start: setTime(now, p.start), end: setTime(now, p.end) }) ) ) ? "OPEN" : "CLOSED"; }5. 性能优化方案
5.1 时间计算缓存
使用memoization优化高频调用:
const getDayHours = useMemo( () => memoize((day) => hours.find(h => h.day === day)), [hours] );5.2 Web Worker处理
复杂时区计算放入Web Worker:
const worker = new Worker('./timezoneWorker.js'); worker.postMessage({ hours, timezone });6. 测试要点
6.1 边界用例
- 23:59-00:01的跨天场景
- 夏令时/冬令时切换日
- 闰秒处理(需mock Date对象)
6.2 视觉回归测试
- 超长时段列表的滚动测试
- 阿拉伯语RTL布局适配
- 高对比度模式下的可读性
7. 实际踩坑记录
- 时区陷阱:某次上线后发现海外门店显示时间全部错乱,原因是服务器默认UTC时间。解决方案:
// 明确指定时区 dayjs.tz.setDefault("America/New_York");iOS日期解析问题:Safari不支持
new Date("2023-01-01")格式,必须改为new Date("2023/01/01")性能卡顿:在渲染500+门店列表时出现滚动卡顿,最终通过虚拟滚动和Web Worker方案解决
这个组件我们最终封装成了公司内部物料,累计接入68个业务线。关键点在于把业务规则(如最小营业时长30分钟)和显示逻辑彻底解耦,通过props注入不同行业的特殊校验规则。