【问题标题】:Solution for process.env.TZ = 'UTC' in Node for Windows?Windows 节点中 process.env.TZ = 'UTC' 的解决方案?
【发布时间】:2020-03-18 04:16:53
【问题描述】:

我正在尝试测试我的 Postgres 数据库,以确保所有内容都已正确添加,并将包含此格式 2029-01-22T16:28:32.000Z 的硬编码日期。

这是它的表格:

CREATE TABLE blogful_articles (
    id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    title TEXT NOT NULL,
    content TEXT,
    date_published TIMESTAMP DEFAULT now() NOT NULL
);

我希望返回 2029-01-22T16:28:32.000Z,但返回 2029-01-23T00:28:32.000Z,因为 Windows 会根据时区差异自动调整它,我的测试会失败。

它应该自动调整夏令时,但这是我的解决方案:

function adjustingForTimezone(dateToAdjust) {
  const offsetInMilliseconds = dateToAdjust.getTimezoneOffset() * 60000;
  const theDateWithOffsetAdded =
    Date.parse(dateToAdjust) - offsetInMilliseconds;
  const adjustedTime = new Date(theDateWithOffsetAdded);
  return adjustedTime;
}

这是我的使用方法:

app.get("/articles", (req, res, next) => {
  const knexInstance = req.app.get("db");
  ArticlesService.getAllArticles(knexInstance)
    .then(articles => {
      const os = process.platform

      function adjustingForTimezone(dateToAdjust) {
        const offsetInMilliseconds = dateToAdjust.getTimezoneOffset() * 60000
        const theDateWithOffsetAdded = Date.parse(dateToAdjust) - offsetInMilliseconds
        const adjustedTime = new Date(theDateWithOffsetAdded)
        return adjustedTime
      }

      res.json(
        articles.map(article => ({
          id: article.id,
          title: article.title,
          style: article.style,
          content: article.content,
          date_published: os === "win32" ? adjustingForTimezone(article.date_published) : new Date(article.date_published)
        }))
      );
    })
    .catch(next);
});

我制作了adjustingForTimezone 应该自动调整它的函数,有什么办法可以改进它吗?在我的代码中使用它有哪些潜在的陷阱?

【问题讨论】:

    标签: node.js windows postgresql timezone utc


    【解决方案1】:

    用于“调整”时区的方法实际上并没有调整。相反,它会创建一个不同的时间点。通常认为只需减去偏移量即可考虑时区,但这是两个截然不同的概念。

    Date 对象中仅存储一个实际值,即您在调用 getTimevalueOfparse 等时看到的数字时间戳。任何其他使用本地时区的函数(例如toString)或命名时区(例如toLocaleStringtimeZone 选项)将期望内部时间戳是基于UTC 的。如果不是,那么您可能会得到不正确的结果。

    这在您的代码中很重要的是您调用getTimezoneOffset 的位置。您要求的时间点应该是基于 UTC 的,但由于您需要调整,那么它已经是错误的时间点。您可以测试这个 DST 转换的接近日期/时间,您会发现它切换偏移量有点太早或有点太晚。

    所以让我们回到最初的问题 - 您从 Postgres 中得到一个时间戳,该时间戳已被本地时区移动。这是罪魁祸首:

    date_published TIMESTAMP DEFAULT now() NOT NULL
    

    以下是可能发生的情况:

    • now() 函数返回一个TIMESTAMP WITH TIME ZONE(又名TIMESTAMPTZ),使用插入行时有效的会话时区。在您的示例中,它返回2029-01-23T00:28:32.000+08:00。因此会话时区可能是默认的,它是从服务器的时区设置中提取的。 (UTC+8 - 在中国、澳大利亚和其他一些地方使用。)

    • 然后你将它传递给TIMESTAMP,它在 Postgres 中知道时区。所以它会去掉偏移量并存储2029-01-23T00:28:32.000。因此,您的时间戳不是以 UTC 而是以本地时间存储的。

    • 稍后查询时,会话时区设置为UTC,所以返回2029-01-23T00:28:32.000Z,这是不正确的。

    有几种不同的方法可以解决这个问题:

    • 您可以确保在插入行期间将会话时区设置为UTC

        SET TIME ZONE 'UTC'; INSERT INTO ...
      
    • 在定义表格时,您可以在存储时间戳之前显式转换为 UTC:

        date_published TIMESTAMP DEFAULT (now() AT TIME ZONE 'UTC') NOT NULL
      
    • 您可以将date_published 字段定义为TIMESTAMPTZ 而不是TIMESTAMP。 (这可以说是最好的方法。)

        date_published TIMESTAMPTZ DEFAULT now() NOT NULL
      
    • 您可以确保查询时的会话时区与插入时的本地时区相同。但是,我不推荐这个,因为如果服务器的时区发生变化,或者如果您使用的时区在夏令时偏移之间转换,您将会有差异。

    使用其中任何一个,您可以删除您的 adjustingForTimezone 函数并直接使用从查询返回的时间戳。

    【讨论】:

    • 感谢您为我指明了正确的方向。我最终对TIMESTAMPTIMESTAMPTZ 进行了更多研究,发现我没有参与最佳实践。这比我想象的要简单得多。
    猜你喜欢
    • 2016-11-01
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 2011-12-11
    • 2013-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多