与本地主机和生产环境上的请求日期不同

Different dates from the request on localhost and production

提问人:Aušrys Mikoliūnas 提问时间:11/9/2023 更新时间:11/9/2023 访问量:35

问:

我正在从前端到后端发送信息,信息的一个关键部分是发送日期。我正在使用 React 和 Node.js Express。当我在自己的机器上工作时,它工作正常,我获取日期,使其处于发送它的用户的正确时区,并将其保存到数据库中。当我将我的项目投入生产时:后端 - ''Cyclic.sh“,前端 - Vercel,我在后端收到的日期格式错误。

对于日期操作,我使用了 Luxon。

以下是我收到它们的方式:

const { prods, createdAt, ...rest } = req.body;
  if (prods.length < 1)
    return res.status(400).json({ error: "At least one product is required" });
  const luxonDate = DateTime.fromISO(createdAt);
  console.log(luxonDate);
  if (luxonDate.invalidExplanation)
    return res.status(400).json({ error: luxonDate.invalidExplanation });
  const normalizedDate = luxonDate.toISO();

以下是我发送它们的方式:

const handleSaveMeal = () => {
    const currentDate = new Date();
    postMealWithProducts.mutate({
      ...mealInfo,
      prods: mealProducts,
      title: selectedMealType,
      isPortion: isPortion,
      createdAt: currentDate,
    });

无论是在本地机器上还是在生产环境中,如果我尝试在发送之前记录日期,则显示相同的格式(本地时间)。但是在生产中,当我收到它时,它比我的时间晚了 2 小时,即使在 localhost 上我收到了我的时间日期。
造成这种现象的原因可能是什么?

当我在后端记录日期时,我得到的是: 本地主机 - 标准化 2023-11-08T19:58:24.323+02:
00

生产 - 标准化 2023-11-08T18:01:18.358+00:00

reactjs 节点.js express datetime 卢克森

评论


答:

0赞 Matt Johnson-Pint 11/9/2023 #1

它不是比你的时间晚两个小时,只是它偏移了两个小时,因为生产环境使用 UTC 作为其本地时区。这是最佳做法,因为它会提醒您注意此类问题。

使用 DateTime.fromISO 分析字符串时,默认时区将是计算机的本地时区。如果您想要不同的时区,则可以通过在选项中提供 来覆盖该行为 - 或者您可以在生成的对象上使用。zonesetZoneDateTime

但是,我怀疑您在这里想要的是保留原始输入字符串中提供的本地时间和偏移量。为此,请在解析时设置:setZone: true

const luxonDate = DateTime.fromISO(createdAt, { setZone: true });

当然,这假定字符串实际上包含偏移量,例如您显示的示例中。不过很难说,因为我不知道您的函数在将对象转换为字符串方面做了什么。如果它发送偏移量,那么你应该没问题。但是,如果它只是发送 UTC 时间 (offset: ),则服务器无法保留本地时间和偏移量,因为您不是从客户端发送的。createdAt+02:00postMealWithProducts.mutatecreatedDateDateZ

评论

0赞 Aušrys Mikoliūnas 11/9/2023
mutation 函数只是发送新的日期,它不会转换任何内容,如果记录它看起来像这样:“2023-11-08T19:00:20.857Z”。当使用 luxon 记录时,它在后端如下所示: 归一化 2023-11-08T21:00:20.857+02:00 所以如果我理解,luxon 日期只是添加了我本地机器的时区,而不是客户端的时区?我应该找到一种方法从前端发送时区吗?
0赞 Matt Johnson-Pint 11/9/2023
日期对象不能直接通过 HTTP 发送。它们必须被序列化。在本例中,它将序列化为显示的ISO8601格式的字符串。表示日期和时间以 UTC 为单位。这是执行类似操作时的默认设置 - 这是序列化要发布的对象时可能发生的情况。ZJSON.stringify(new Date())
0赞 Matt Johnson-Pint 11/9/2023
所以,如果你只需要UTC即时时间,那么你已经得到了它。如果需要以用户的本地时间和与 UTC 的偏移量(例如 )表示日期和时间,则需要调整代码以将其包含在序列化中。如果您需要的偏移量超过偏移量,例如用户的实际时区标识符(例如 ),则还必须单独收集该标识符。+02:00"Europe/Vilnius"
0赞 Matt Johnson-Pint 11/9/2023
您可能也对这个问题感兴趣。我的答案或排名第二的答案都可以使用,如果您愿意,可以调整为使用 Luxon。
0赞 Aušrys Mikoliūnas 11/9/2023 #2

问题是,当我在前端记录日期时,我在 Chrome 中的控制台向我显示了带有偏移量的完整日期,这让我认为日期将以正常格式发送。但是,在评论中,有人向我解释说,它只发送日期而没有偏移量。当我复制控制台输出并粘贴到这里时,可以看到它显示了没有偏移量的日期(也许是 Chrome 添加的偏移量)。所以在前端,我添加了偏移量:

const currentDate = new Date();
const userTimezone = currentDate.getTimezoneOffset();
postMealWithProducts.mutate({
  ...mealInfo,
  prods: mealProducts,
  title: selectedMealType,
  isPortion: isPortion,
  createdAt: currentDate,
  userTimezone: userTimezone,
});

然后在后端,我编辑代码,以便 Luxon 添加偏移量。偏移量乘以负 1,因为函数 getTimezoneOffset() 返回提供的差异日期和以分钟为单位的 utc,如果时区在前面,则为负数,如果时区在后面,则为正数。反之亦然,前时区为正,后时区为负,这就是我将其乘以 -1 的原因。

const { prods, createdAt, userTimezone, ...rest } = req.body;
if (prods.length < 1)
return res.status(400).json({ error: "At least one product is required" });
const luxonDate = DateTime.fromISO(createdAt, { zone: userTimezone * -1 });
if (luxonDate.invalidExplanation)
return res.status(400).json({ error: luxonDate.invalidExplanation });
const normalizedDate = luxonDate.toISO();