如何防止 Sequelize 将 Date 对象转换为本地时间
How to prevent Sequelize from converting Date object to local time
我正在为节点项目使用 sequelize。它连接到一个 Postgres 数据库,它包含一个 table 和一个 DATE
字段(不像 TIMESTAMP WITH TIMEZONE
,DATE
没有时间数据)。
在代码中,我使用 javascript Date
对象对日期进行建模,该对象将时间存储为 UTC 午夜。当我使用它使用 Date
对象将记录插入 table 时,sequelize 显然首先将其覆盖到本地时间,因为记录总是落后 1 天。因此,如果我想将 2000-10-31 插入到数据库中,我最终会得到 2000-10-30。我在 UTC-5。
如何告诉 sequelize 在插入数据库之前不要将日期转换为本地时间?
这是一些示例代码。我还创建了一个 repository 如果你想 运行 自己。
var Sequelize = require('sequelize');
const sequelize = new Sequelize('testdb', 'postgres', '???', {
host: 'localhost',
dialect: 'postgres'
});
TestTable = sequelize.define('date_test',
{
id: {
primaryKey: true,
type: Sequelize.INTEGER,
autoIncrement: true
},
someDate: {
field: 'some_date',
type: Sequelize.DATEONLY
}
},
{
timestamps: false,
freezeTableName: true
}
);
// midnight UTC on Halloween
var date = new Date(Date.UTC(2000, 9, 31));
// converts to local time resulting in 2000-10-30
TestTable.create({ someDate: date })
.then(function() {
// also apparently converts to local time resulting in 2000-10-30
return TestTable.create({ someDate: date.toUTCString() });
})
.then(function() {
// convert to string, definitely works but seems unnecessary
var strDate = date.getUTCFullYear() + '-' + pad2(date.getUTCMonth() + 1) + '-' + pad2(date.getUTCDate());
return TestTable.create({ someDate: strDate });
})
.then(function() {
// cerate a new local Date, also works but seems hacky
var newDate = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
return TestTable.create({ someDate: newDate });
})
.then(function() {
process.exit(0);
});
function pad2(n) {
if (n.length === 1) {
return '0' + n;
}
return n;
}
问题是日期是作为 UTC 时间创建的,但是因为 DATEONLY
不知道时区,所以它将 Date 对象“按原样”(本地时间)格式化为格式 YYYY-MM-DD
(使用 moment.js - see here in source)。
对于 DATEONLY
你可以这样做:
var date = new Date(2000, 9, 31);
这将正确插入日期。
程序员很少这么说,但有一次你对时区想得太多了!
旧的错误答案
这取决于您检查值的方式,但 javascript 和 postgresql 会将其转换为您当地的时区以供显示。
Sequelize 使用 TIMESTAMP WITH TIMEZONE 日期字段类型 (source)。
在 postgresql docs 中说:
For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's timezone parameter, and is converted to UTC using the offset for the timezone zone.
When a timestamp with time zone value is output, it is always converted from UTC to the current timezone zone, and displayed as local time in that zone. To see the time in another time zone, either change timezone or use the AT TIME ZONE construct (see Section 9.9.3).
如果您在 postgresql 中输出时间值,请尝试将其转换为 UTC。如果您在 javascript 中输出它,请尝试 date.toUTCString()
.
你的 hack 似乎有效的原因是因为它们实际上是在存储 2000-11-01 05:00
但是当你检查该值时,它会被转换成你当地的时区。
我正在为节点项目使用 sequelize。它连接到一个 Postgres 数据库,它包含一个 table 和一个 DATE
字段(不像 TIMESTAMP WITH TIMEZONE
,DATE
没有时间数据)。
在代码中,我使用 javascript Date
对象对日期进行建模,该对象将时间存储为 UTC 午夜。当我使用它使用 Date
对象将记录插入 table 时,sequelize 显然首先将其覆盖到本地时间,因为记录总是落后 1 天。因此,如果我想将 2000-10-31 插入到数据库中,我最终会得到 2000-10-30。我在 UTC-5。
如何告诉 sequelize 在插入数据库之前不要将日期转换为本地时间?
这是一些示例代码。我还创建了一个 repository 如果你想 运行 自己。
var Sequelize = require('sequelize');
const sequelize = new Sequelize('testdb', 'postgres', '???', {
host: 'localhost',
dialect: 'postgres'
});
TestTable = sequelize.define('date_test',
{
id: {
primaryKey: true,
type: Sequelize.INTEGER,
autoIncrement: true
},
someDate: {
field: 'some_date',
type: Sequelize.DATEONLY
}
},
{
timestamps: false,
freezeTableName: true
}
);
// midnight UTC on Halloween
var date = new Date(Date.UTC(2000, 9, 31));
// converts to local time resulting in 2000-10-30
TestTable.create({ someDate: date })
.then(function() {
// also apparently converts to local time resulting in 2000-10-30
return TestTable.create({ someDate: date.toUTCString() });
})
.then(function() {
// convert to string, definitely works but seems unnecessary
var strDate = date.getUTCFullYear() + '-' + pad2(date.getUTCMonth() + 1) + '-' + pad2(date.getUTCDate());
return TestTable.create({ someDate: strDate });
})
.then(function() {
// cerate a new local Date, also works but seems hacky
var newDate = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
return TestTable.create({ someDate: newDate });
})
.then(function() {
process.exit(0);
});
function pad2(n) {
if (n.length === 1) {
return '0' + n;
}
return n;
}
问题是日期是作为 UTC 时间创建的,但是因为 DATEONLY
不知道时区,所以它将 Date 对象“按原样”(本地时间)格式化为格式 YYYY-MM-DD
(使用 moment.js - see here in source)。
对于 DATEONLY
你可以这样做:
var date = new Date(2000, 9, 31);
这将正确插入日期。
程序员很少这么说,但有一次你对时区想得太多了!
旧的错误答案
这取决于您检查值的方式,但 javascript 和 postgresql 会将其转换为您当地的时区以供显示。
Sequelize 使用 TIMESTAMP WITH TIMEZONE 日期字段类型 (source)。
在 postgresql docs 中说:
For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's timezone parameter, and is converted to UTC using the offset for the timezone zone.
When a timestamp with time zone value is output, it is always converted from UTC to the current timezone zone, and displayed as local time in that zone. To see the time in another time zone, either change timezone or use the AT TIME ZONE construct (see Section 9.9.3).
如果您在 postgresql 中输出时间值,请尝试将其转换为 UTC。如果您在 javascript 中输出它,请尝试 date.toUTCString()
.
你的 hack 似乎有效的原因是因为它们实际上是在存储 2000-11-01 05:00
但是当你检查该值时,它会被转换成你当地的时区。