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

Firestore跨天营业时间时间范围查询无结果问题排查

问题:跨天营业时间的Firestore时间范围查询无结果

问题背景

有一个每10分钟运行的Cloud Function,用于计算餐厅门店的订单与客户数据。门店采用跨天营业时间(例如周二6:00至周三3:00),因此需要处理当日营业时间(本地当日6:00到23:59:59.999)和前一日剩余时段(本地当日00:00到03:00,属于前一日的营业时段)。但查询前一日剩余时段时,即使该时段有订单,查询结果始终为空。当前处于多伦多时区(UTC-5/冬令时),Firestore以UTC存储日期,仅在控制台显示本地时区。

现有代码

const getRangeForOneDay = async (forPreviousDay = false) => {
  const lowerBound = "06:00"
  const upperBound = "03:00";


  const lowerBoundSplit = ["06", "00"];
  const startOfToday = new Date();
  startOfToday.setHours(
    forPreviousDay ? 0 : parseInt(lowerBoundSplit[0]),
    forPreviousDay ? 0 : parseInt(lowerBoundSplit[1]),
    0,
    0
  );


  const upperBoundSplit = ["03", "00"];
  const endOfToday = new Date();
  endOfToday.setHours(
    forPreviousDay ? parseInt(upperBoundSplit[0]) : 23,
    forPreviousDay ? parseInt(upperBoundSplit[1]) : 59,
    forPreviousDay ? 0 : 59,
    forPreviousDay ? 0 : 999
  );
  return [startOfToday, endOfToday];
};

const rangeToday = getRangeForOneDay(false);
const rangeYesterday = getRangeForOneDay(true);

const ranges = [rangeToday, rangeYesterday];

const branchId = "some real id";

for (const range of ranges) {
  const [startOfToday, endOfToday] = range;
  const orders = await admin
    .firestore()
    .collection("Orders")
    .where("branch_id", "==", branchId)
    .where("create_on", ">=", startOfToday)
    .where("create_on", "<=", endOfToday)
    .orderBy("create_on", "desc")
    .get();

  console.log(`Orders length = ${orders.size}`);
}

当前生成的时间范围

Current Day(Tuesday):
From Date = 2022-11-08T06:00:00.000Z
To Date = 2022-11-08T23:59:59.999Z

Previous Day(Monday remaining hours data):
From Date = 2022-11-08T00:00:00.000Z
To Date = 2022-11-08T03:00:00.000Z

问题根源

核心问题是Cloud Function运行环境默认使用UTC时区,代码中的日期操作基于UTC时间,而非多伦多本地时间:

  • 你需要查询的是多伦多本地时间的「当日00:00-03:00」,对应UTC时间是「当日05:00-08:00」(冬令时UTC-5)。
  • 但代码中setHours(0, 0, 0, 0)是设置UTC时间的0点,对应多伦多本地时间的「前一日19:00」,生成的查询范围是UTC「当日00:00-03:00」,对应多伦多本地「前一日19:00-22:00」,完全不匹配目标时段,因此查不到订单。

修复方案

需要明确基于多伦多时区(America/Toronto)计算本地日期和时段,避免依赖运行环境的UTC时区。以下是修复后的代码:

// 获取多伦多本地时间的日期对象
const getTorontoLocalDate = () => {
  const now = new Date();
  const formatter = new Intl.DateTimeFormat('en-US', {
    timeZone: 'America/Toronto',
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit'
  });
  const parts = formatter.formatToParts(now);
  const dateParts = parts.reduce((acc, part) => {
    acc[part.type] = part.value;
    return acc;
  }, {});
  return new Date(`${dateParts.year}-${dateParts.month}-${dateParts.day}T${dateParts.hour}:${dateParts.minute}:${dateParts.second}`);
};

// 生成多伦多本地时间对应的查询范围
const getRangeForOneDay = (forPreviousDay = false) => {
  const lowerBound = [6, 0]; // 多伦多本地营业时间开始(6:00)
  const upperBound = [3, 0]; // 多伦多本地营业时间结束(次日3:00)

  // 获取多伦多本地的当前日期
  const torontoNow = getTorontoLocalDate();
  // 多伦多本地的当日0点
  const torontoStartOfDay = new Date(torontoNow.getFullYear(), torontoNow.getMonth(), torontoNow.getDate());

  let start, end;

  if (!forPreviousDay) {
    // 多伦多本地当日6:00 到 23:59:59.999
    start = new Date(torontoStartOfDay.getTime() + lowerBound[0] * 3600 * 1000 + lowerBound[1] * 60 * 1000);
    end = new Date(torontoStartOfDay.getTime() + 23 * 3600 * 1000 + 59 * 60 * 1000 + 999);
  } else {
    // 多伦多本地当日00:00 到 03:00(属于前一日营业时段)
    start = torontoStartOfDay;
    end = new Date(torontoStartOfDay.getTime() + upperBound[0] * 3600 * 1000 + upperBound[1] * 60 * 1000);
  }

  return [start, end];
};

// 后续查询逻辑保持不变
const rangeToday = getRangeForOneDay(false);
const rangeYesterday = getRangeForOneDay(true);

const ranges = [rangeToday, rangeYesterday];

const branchId = "some real id";

for (const range of ranges) {
  const [startOfToday, endOfToday] = range;
  const orders = await admin
    .firestore()
    .collection("Orders")
    .where("branch_id", "==", branchId)
    .where("create_on", ">=", startOfToday)
    .where("create_on", "<=", endOfToday)
    .orderBy("create_on", "desc")
    .get();

  console.log(`Orders length = ${orders.size}`);
}

修复说明

  1. getTorontoLocalDate函数通过Intl.DateTimeFormat明确指定多伦多时区,获取当前的本地日期和时间,避免受运行环境UTC时区影响。
  2. getRangeForOneDay基于多伦多本地的当日0点,计算目标时段的起始和结束时间,生成的Date对象会自动转换为正确的UTC时间,与Firestore存储的订单时间匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 21:40:25