如何防止 Sequelize 将 Date 对象转换为本地时间

d51*_*512 4 utc node.js sequelize.js

我正在为节点项目使用sequelize。它连接到一个 Postgres 数据库,它包含一个带有DATE字段的表(与 不同TIMESTAMP WITH TIMEZONEDATE没有时间数据)。

在代码中,我使用 javascriptDate对象对日期进行建模,该对象将时间存储为 UTC 午夜。当我使用该Date对象将记录插入到表中时,sequelize 显然首先将其覆盖到本地时间,因为记录总是落后 1 天。因此,如果我想将 2000-10-31 插入到数据库中,我最终会得到 2000-10-30。我在UTC-5。

如何告诉 sequelize 在插入数据库之前不要将日期转换为本地时间?

这是一些示例代码。如果你想自己运行它,我还创建了一个存储库

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;
}
Run Code Online (Sandbox Code Playgroud)

小智 5

问题是日期是作为 UTC 时间创建的,但由于DATEONLY不知道时区,它使用格式YYYY-MM-DD(使用 moment.js -请参阅此处的源代码)“按原样”(本地时间)格式化日期对象。

对于 aDATEONLY你可以这样做:

var date = new Date(2000, 9, 31);
Run Code Online (Sandbox Code Playgroud)

这将正确插入日期。

程序员很少这么说,但有一次你对时区考虑太多了


旧的错误答案

这取决于您如何检查值,但 javascript 和 postgresql 正在将其转换为您的本地时区以进行显示。

Sequelize 使用TIMESTAMP WITH TIMEZONE类型作为日期字段(source)。

postgresql 文档中它说:

对于带时区的时间戳,内部存储的值始终采用 UTC(世界协调时间,传统上称为格林威治标准时间,GMT)。使用该时区的适当偏移量将具有指定显式时区的输入值转换为 UTC。如果输入字符串中未指定时区,则假定其位于系统时区参数指示的时区中,并使用时区区域的偏移量转换为 UTC。

当输出带有时区值的时间戳时,它总是从UTC转换为当前时区区域,并显示为该区域的本地时间。要查看另一个时区的时间,请更改时区或使用 AT TIME ZONE 构造(请参阅第 9.9.3 节)。

如果您在 postgresql 中输出时间值,请尝试将其转换为 UTC。如果您在 javascript 中输出它,请尝试date.toUTCString().

您的 hack 似乎有效的原因是因为它们实际上是在存储,2000-11-01 05:00但是当您检查该值时,它会转换为您的本地时区。