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

基于当前时间筛选活跃班次的逻辑问题及跨时区适配需求

问题描述

我有如下用户对象:

const user = {
  id: 3,
  name: "Clark Kent",
  mobileNumber: "1234567892",
  email: "example_user@gmail.com",
  role: "VAN_SALE",
  loc_update_permission: true,
  active_shifts: [
    {
      id: 1,
      name: "Morning",
      startTime: "09:30:00",
      endTime: "05:30:00",
      coordinates: null,
    },
    {
      id: 2,
      name: "Night",
      startTime: "06:30:00",
      endTime: "02:30:00",
      coordinates: null,
    },
  ],
};

需求是基于当前时间返回活跃班次:当时间处于或等于09:30:00至05:30:00区间时返回Morning班次,其他时间返回Night班次(数据库中以time类型存储班次起止时间)。

我写了对应的JavaScript函数,但测试时添加currentTime.setHours(currentTime.getHours() + 10)调整时间后,本该返回Night班次却仍返回Morning。另外,这个逻辑还需要适配IST、AST等不同时区,确保不同时区用户在当地时间9:00 AM时都能显示Morning班次。

问题分析
  1. 班次匹配逻辑bug:两个班次的时间范围存在重叠(比如18:00同时属于Morning和Night的区间),而代码按顺序遍历班次,先匹配到的Morning会直接返回,导致Night班次永远无法被触发。
  2. 时区适配缺失:当前代码使用运行环境的本地时区计算时间,无法根据用户所在时区转换当地时间,导致不同时区用户的判断结果错误。
解决方案

1. 修正班次匹配逻辑

首先要确保班次时间范围无重叠,比如把Night班次的时间调整为startTime: "05:30:00"、endTime: "09:30:00",这样两个班次刚好覆盖一整天且无交叉。同时修正判断逻辑,把原有的< endHours改为<= endHours,满足“等于区间边界也匹配”的需求。

修正后的函数:

function getCurrentActiveShift(user, userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone) {
  // 将当前时间转换为用户时区的本地时间,再计算总分钟数
  const now = new Date();
  const userLocalTime = new Date(now.toLocaleString("en-US", { timeZone: userTimeZone }));
  const currentTotalMinutes = userLocalTime.getHours() * 60 + userLocalTime.getMinutes();

  for (const shift of user.active_shifts) {
    // 解析班次起止时间为总分钟数
    const [startH, startM] = shift.startTime.split(":").map(Number);
    const shiftStartMinutes = startH * 60 + startM;
    const [endH, endM] = shift.endTime.split(":").map(Number);
    const shiftEndMinutes = endH * 60 + endM;

    let isActive = false;
    if (shiftEndMinutes < shiftStartMinutes) {
      // 处理跨午夜的班次:当前时间 >= 开始时间 或 <= 结束时间
      isActive = currentTotalMinutes >= shiftStartMinutes || currentTotalMinutes <= shiftEndMinutes;
    } else {
      // 处理不跨午夜的班次:当前时间在起止时间之间(含边界)
      isActive = currentTotalMinutes >= shiftStartMinutes && currentTotalMinutes <= shiftEndMinutes;
    }

    if (isActive) {
      return shift;
    }
  }

  return null;
}

2. 适配多时区

要支持不同时区,需要:

  • 给每个用户对象添加时区字段(比如timeZone: "Asia/Kolkata"对应IST,America/Puerto_Rico对应AST)
  • 调用函数时传入用户的时区,确保计算的是用户当地时间

修改后的用户对象示例:

const user = {
  id: 3,
  name: "Clark Kent",
  mobileNumber: "1234567892",
  email: "example_user@gmail.com",
  role: "VAN_SALE",
  loc_update_permission: true,
  timeZone: "Asia/Kolkata", // 存储用户所在时区
  active_shifts: [
    {
      id: 1,
      name: "Morning",
      startTime: "09:30:00",
      endTime: "05:30:00",
      coordinates: null,
    },
    {
      id: 2,
      name: "Night",
      startTime: "05:30:00",
      endTime: "09:30:00",
      coordinates: null,
    },
  ],
};

调用函数:

const currentShift = getCurrentActiveShift(user, user.timeZone);
if (currentShift) {
  console.log(`当前活跃班次:${currentShift.name}`);
} else {
  console.log("当前无活跃班次");
}

3. 数据库存储优化建议

当前用time类型存储跨午夜的班次时间,无法明确结束时间是当天还是次日,建议调整存储方式:

  • 方案1:使用数据库支持的时间区间类型(比如PostgreSQL的tstzrange),同时关联用户时区,直接存储带时区的时间范围。
  • 方案2:将起止时间存储为当日总分钟数(0-1439),比如start_minute: 570(对应09:30)、end_minute: 330(对应05:30),减少前端解析成本。
  • 方案3:如果班次是固定每日循环,保留time类型存储,但必须确保班次之间无重叠,同时在用户表中新增time_zone字段存储用户时区。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:45:54