You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Day.js处理时区偏移,实现营业时间转换与判断?

问题描述

我在为地点开发营业时段功能,将营业时间以JSON格式存储在PostgreSQL表中,示例如下:

{
    "dayOfTheWeek": "FRIDAY",
    "isOpen": true,
    "hours": [
        {
            "open": "08:00",
            "closed": "12:00"
        },
        {
            "open": "13:00",
            "closed": "22:00"
        }
   ]
}

该地点使用中部时间(America/Chicago),时区信息单独存储在另一列:

"timezone": "America/Chicago"

目前遇到两个核心问题:

  1. 显示营业时间时,总是比实际快5小时(比如存储的08:00显示成13:00),尝试使用dayjs的timezone和utc offset插件无效,手动减5小时能临时解决,但想通过时区字符串自动转换偏移量。
  2. 需要判断给定的UTC时间(比如2023-04-14T09:00:00Z)是否处于该地点的营业时段内,当前代码因时区不匹配总是判断错误(比如9点UTC对应芝加哥时间4点,代码却和13点UTC比较,导致进入BEFORE分支)。

之前的代码片段:

// dateTimeStart = 2023-04-14T09:00:00
const selectedDay = dayjs(dateTimeStart).format('dddd').toUpperCase();  // FRIDAY

for (let day of location.businessHours) {
    if (selectedDay === day.dayOfTheWeek) {
        if (day.isOpen) {
            let blockStart = dayjs(block.open, 'HH:mm');   // 2023-04-14T13:00:00.000Z
            let blockStop = dayjs(block.closed, 'HH:mm');  // 2023-04-14T17:00:00.000Z
            
            // 比较逻辑
        }
    }
}
解决方案

1. 修复dayjs时区插件失效问题

dayjs的timezone插件依赖utc插件,必须按顺序导入并启用,否则tz方法不会生效:

// 先安装依赖:npm install dayjs dayjs-plugin-timezone
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'

// 先启用utc插件,再启用timezone插件
dayjs.extend(utc)
dayjs.extend(timezone)

2. 正确转换并显示营业时间

存储的08:00是芝加哥本地时间,要正确显示该时区的时间,需把时分绑定到对应日期的芝加哥时区对象上:

// 以请求日期为例,创建芝加哥时区的08:00时间
const chicagoOpenTime = dayjs('2023-04-14').tz('America/Chicago').hour(8).minute(0);
console.log(chicagoOpenTime.format('HH:mm')); // 输出 08:00(芝加哥本地时间)
console.log(chicagoOpenTime.utc().format('HH:mm')); // 输出 13:00(对应UTC时间)

如果需要将时区字符串(如America/Chicago)转为小时偏移量,可以用utcOffset()方法:

// 偏移量单位为分钟,除以60转为小时数
const offsetHours = dayjs().tz('America/Chicago').utcOffset() / 60;
console.log(offsetHours); // 夏令时返回-4,非夏令时返回-5

3. 正确判断请求时间是否在营业时段内

核心原则是统一时间基准:要么把请求的UTC时间转为地点本地时间,要么把营业时段的本地时间转为UTC时间,再进行比较。

方案一:将请求时间转为地点本地时间比较

// 传入的UTC请求时间
const requestedUtcTime = dayjs('2023-04-14T09:00:00Z');
// 转为地点时区的本地时间
const localRequestedTime = requestedUtcTime.tz(location.timezone);
// 获取本地时间对应的星期(大写)
const localDay = localRequestedTime.format('dddd').toUpperCase();

for (let day of location.businessHours) {
  if (localDay === day.dayOfTheWeek && day.isOpen) {
    for (let block of day.hours) {
      // 解析营业时段的时分
      const [openHour, openMin] = block.open.split(':').map(Number);
      const [closeHour, closeMin] = block.closed.split(':').map(Number);
      
      // 创建本地时间对应的营业开始/结束时间(绑定到请求日期)
      const blockLocalStart = localRequestedTime.hour(openHour).minute(openMin).second(0);
      const blockLocalEnd = localRequestedTime.hour(closeHour).minute(closeMin).second(0);
      
      // 判断请求时间是否在营业区间内([]表示包含开始和结束时间)
      if (localRequestedTime.isBetween(blockLocalStart, blockLocalEnd, null, '[]')) {
        console.log('处于营业时段内');
      } else {
        console.log('不处于营业时段内');
      }
    }
  }
}

方案二:将营业时段转为UTC时间比较

const requestedUtcTime = dayjs('2023-04-14T09:00:00Z');
const localRequestedTime = requestedUtcTime.tz(location.timezone);
const localDay = localRequestedTime.format('dddd').toUpperCase();

for (let day of location.businessHours) {
  if (localDay === day.dayOfTheWeek && day.isOpen) {
    for (let block of day.hours) {
      const [openHour, openMin] = block.open.split(':').map(Number);
      const [closeHour, closeMin] = block.closed.split(':').map(Number);
      
      // 创建本地营业时间,再转为UTC
      const blockUtcStart = localRequestedTime.hour(openHour).minute(openMin).second(0).utc();
      const blockUtcEnd = localRequestedTime.hour(closeHour).minute(closeMin).second(0).utc();
      
      // 和UTC请求时间直接比较
      if (requestedUtcTime.isBetween(blockUtcStart, blockUtcEnd, null, '[]')) {
        console.log('处于营业时段内');
      } else {
        console.log('不处于营业时段内');
      }
    }
  }
}

关键注意事项

  • 必须确保utc和timezone插件按顺序加载,否则时区转换会失效
  • 营业时段的时分需要绑定到请求时间对应的日期,避免跨天场景(比如营业到次日凌晨)
  • 夏令时会自动处理:dayjs的timezone插件会根据日期自动应用对应时区的夏令时偏移量,无需手动调整

内容的提问来源于stack exchange,提问作者Damon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 19:15:02