Windows 节点中 process.env.TZ = 'UTC' 的解决方案?
Solution for process.env.TZ = 'UTC' in Node for Windows?
我正在尝试测试我的 Postgres 数据库以确保所有内容都已正确添加,并且会包含这种格式的硬编码日期 2029-01-22T16:28:32.000Z
。
这是它的 table:
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
功能,应该自动调整它,有什么办法可以改进它吗?在我的代码中使用它有哪些潜在的陷阱?
用于“调整”时区的方法实际上并没有调整。相反,它创建了一个不同的时间点。人们通常认为可以只减去一个偏移量来说明时区,但这是两个截然不同的概念。
Date
对象只存储了一个实际值,即您在调用 getTime
、valueOf
、parse
等时看到的数字时间戳. 任何其他使用本地时区(例如 toString
)或命名时区(例如 toLocaleString
的 timeZone
选项)的函数都期望内部时间戳是 UTC -基于。如果不是,那么您可能会得到不正确的结果。
这在您的代码中重要的地方是您调用 getTimezoneOffset
的地方。您要求的时间点应该是基于 UTC 的,但是由于您需要调整,所以它已经是错误的时间点。您可以在 DST 转换的 date/time 附近对此进行测试,您会发现它会稍微过早或稍微过晚地切换偏移量。
那么让我们回到最初的问题——您从 Postgres 中获取的时间戳已被本地时区偏移。这是罪魁祸首:
date_published TIMESTAMP DEFAULT now() NOT NULL
这是可能发生的事情:
now()
函数 returns a 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 ...
在定义 table:
时,您可以在存储时间戳之前显式转换为 UTC
date_published TIMESTAMP DEFAULT (now() AT TIME ZONE 'UTC') NOT NULL
您可以将 date_published
字段定义为 TIMESTAMPTZ
而不是 TIMESTAMP
。 (这可以说是最好的方法。)
date_published TIMESTAMPTZ DEFAULT now() NOT NULL
您可以确保查询时的会话时区与插入时的本地时区相同。但是,我不推荐这个,因为如果服务器的时区发生变化,或者如果您使用的时区在夏令时的偏移量之间转换,就会出现差异。
有了其中任何一个,您可以删除 adjustingForTimezone
函数并直接使用从查询返回的时间戳。
我正在尝试测试我的 Postgres 数据库以确保所有内容都已正确添加,并且会包含这种格式的硬编码日期 2029-01-22T16:28:32.000Z
。
这是它的 table:
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
功能,应该自动调整它,有什么办法可以改进它吗?在我的代码中使用它有哪些潜在的陷阱?
用于“调整”时区的方法实际上并没有调整。相反,它创建了一个不同的时间点。人们通常认为可以只减去一个偏移量来说明时区,但这是两个截然不同的概念。
Date
对象只存储了一个实际值,即您在调用 getTime
、valueOf
、parse
等时看到的数字时间戳. 任何其他使用本地时区(例如 toString
)或命名时区(例如 toLocaleString
的 timeZone
选项)的函数都期望内部时间戳是 UTC -基于。如果不是,那么您可能会得到不正确的结果。
这在您的代码中重要的地方是您调用 getTimezoneOffset
的地方。您要求的时间点应该是基于 UTC 的,但是由于您需要调整,所以它已经是错误的时间点。您可以在 DST 转换的 date/time 附近对此进行测试,您会发现它会稍微过早或稍微过晚地切换偏移量。
那么让我们回到最初的问题——您从 Postgres 中获取的时间戳已被本地时区偏移。这是罪魁祸首:
date_published TIMESTAMP DEFAULT now() NOT NULL
这是可能发生的事情:
now()
函数 returns aTIMESTAMP 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 ...
在定义 table:
时,您可以在存储时间戳之前显式转换为 UTCdate_published TIMESTAMP DEFAULT (now() AT TIME ZONE 'UTC') NOT NULL
您可以将
date_published
字段定义为TIMESTAMPTZ
而不是TIMESTAMP
。 (这可以说是最好的方法。)date_published TIMESTAMPTZ DEFAULT now() NOT NULL
您可以确保查询时的会话时区与插入时的本地时区相同。但是,我不推荐这个,因为如果服务器的时区发生变化,或者如果您使用的时区在夏令时的偏移量之间转换,就会出现差异。
有了其中任何一个,您可以删除 adjustingForTimezone
函数并直接使用从查询返回的时间戳。