I need to get the current time, according to the database timezone (not my local timezone, and not default UTC timezone), using Sequelize. Is there a Sequelize method to do this?
My database is in Eastern time, and when I query the db directly SELECT CURRENT_TIMESTAMP; it returns the date/time in Eastern time (which is correct).
But when I query via Sequelize in Node const [[{time}]] = await db.sequelize.query('SELECT CURRENT_TIMESTAMP AS time'); it returns the date/time in UTC.
Two problems:
1 - I would prefer using a Sequelize method instead of a raw query.
2 - This still doesn't get me the result I want. The time needs to be Eastern.
This is my DB setup:
const sequelize = new Sequelize(dbUrl, {
dialectOptions: {
useUTC: false // for reading from database
},
timezone: '-04:00', // for writing to database
define: {
charset: 'utf8'
}
})
As mentioned above, when I query using the above queries, the date is always returned in UTC, which I did not expect, given I said useUTC: false. How do I get it in Eastern time (the database timezone)?
I'm not aware of a sequelize method like getCurrentDate().
The UTC conversion problem seems to bite everyone (myself included). Here are some details. Not sure if dialectOptions: {useUTC: false }, has any function at all - just adding the typeCast method solved the problem for me.
dialectOptions: {
typeCast: function (field, next) { // for reading from database
if (field.type === 'DATETIME') {
return field.string()
}
return next()
},
The result can be used for a new js Date object:
const sql = 'select current_timestamp';
my_app.my_DB.query(sql, {raw: true, type: Sequelize.QueryTypes.SELECT})
.then(data => {
console.log(data[0].current_timestamp);
let d1 = new Date(data[0].current_timestamp);
});
This works fine for me - but make sure to test thoroughly!
Add useUTC property in your dialectOptions like this
dialectOptions: {
encrypt: false ,
options: {
useUTC: false, // for reading from database
},
},
Related
Model:
const fooSchema = new mongoose.Schema({
fooCreationDate: {
type: Date
},
bar: [{
barCreationDate: {
type: Date
}
}]
});
const foo = mongoose.model(`foo`, fooSchema);
If we want to search for foo objects that were created between 2022-01-01 and 2022-01-02, we can use the following mongoose query:
foo.find({
fooCreationDate: {
$gte: "2022-01-01T00:00:00.000",
$lt: "2022-01-02T00:00:00.000"
}
});
Please note that I'm using strings instead of date objects. The reason is that the query is passed by the client through an AJAX call with dataType: "jsonp". Every date object that is passed like that to the backend is automatically converted to an ISO string. Despite that, the query works without any issues - the find function automatically parses dates represented as ISO strings.
We'd now like to extract every bar object that was created in the same time range, so we'll need to use an aggregation:
foo.aggregate([{
$unwind: `$bar`,
}, {
$match: {
"bar.barCreationDate": {
$gte: "2022-01-01T00:00:00.000",
$lt: "2022-01-02T00:00:00.000"
}
}
}]);
Unfortunately, nothing is found despite the fact that the database contains matching bar objects. This can be confirmed by passing Date objects instead of strings to the $match aggregation:
foo.aggregate([{
$unwind: `$bar`,
}, {
$match: {
"bar.barCreationDate": {
$gte: new Date("2022-01-01T00:00:00.000"),
$lt: new Date("2022-01-02T00:00:00.000")
}
}
}]);
This query returns some results, so the conclusion is that mongoose accepts ISO date strings in the find function, but can't handle them in the aggregate function.
Is there any known workaround? I could, for example, deep-scan every query object passed from the client and search for ISO date strings, then convert them to Date objects, but that's a bit dirty in my opinion. I'm using mongoose v5.6.4 and mongodb v4.2.2.
When I execute a query that that has a data passed as replacement, the date is not set as UTC-Date in the query.
My code:
let startInterval = moment('2020-12-09').toDate();
db.query(`
SELECT kv.kpiId
FROM kpiValues kv
WHERE kv.insertDate >= :startInterval
`, {
type: QueryTypes.SELECT,
replacements: {
startInterval: startInterval,
}
}).catch(next)
Printing the startInterval variable results in 2020-12-08T23:00:00.000Z.
When I output the query it shows
SELECT kv.kpiId
FROM kpiValues kv
WHERE kv.insertDate >= '2020-12-09 00:00:00.000'
The column is a sequelize Date column (SQL Datetime). The value in the query is my local time - but the database should only use UTC values - so I would expect it to use 2020-12-08T23:00:00.000 in the query. What can I do?
The sequelize connection:
const sequelize = new Sequelize(dbName, dbUser, dbPassword, {
host: Settings.dbUrl,
port: dbPort,
dialect: 'mariadb',
dialectOptions: {},
timezone: '+00:00',
pool: {
max: 70,
min: 5,
acquire: 30000
},
define: {
timestamps: false,
freezeTableName: true
},
logging: true // Remove property when all statements that are executed should be printed
});
As there is no solution for this problem with mariaDB as DBMS using sequelize the following workaround works:
Just manually pass the replacement variable as UTC-String:
dateVariable.toISOString()
I'm using SequlizeJS ORM in my ExpressJS application to communicate with MariaDB. I'm working with existing database scheme so I can't change data types of the fields. Existing database uses unix timestamps in createdAt, updatedAt & deletedAt fields. Now I need to follow that for the new ExpressJS app too.
In each modal I use the following Sequelize hooks to convert createdAt & updatedAt fields to unix timestamps.
hooks: {
beforeCreate: (instance, options) => {
instance.dataValues.createdAt = Math.floor(Date.now() / 1000);
instance.dataValues.updatedAt = Math.floor(Date.now() / 1000);
},
beforeUpdate: (instance, options) => {
instance.dataValues.updatedAt = Math.floor(Date.now() / 1000);
}
}
but the thing is that I can't set deletedAt as a timestamp on beforeBulkDestroy hook. Can anyone please help me to resolve this ?
Thanks !
One thing to try - the individualHooks option calls the beforeDestroy() hook for each instance. This can be applied at the query level:
db.myFunkyModel.destroy({
where: {
'field' : { [Op.like]: '%someValue%' }
},
individualHooks : true
});
Or at a broader level:
const sequelizeDb = new Sequelize(
...
{
host: '127.0.0.1',
....
define: {
....
individualHooks : true
}
....
});
Take a look at the Model Hooks section of the manual for potential performance hits from individualHooks.
I have following row saved in timezone Europe/Madrid at database:
dateA = '2019-03-26 15:00:00'
dateB = '2019-03-26 14:00:00'
When selecting the entry:
let entry = this.query().findById(id)
I get following values (console.log(entry)):
{
dateA: 2019-03-26T06:00:00.000Z,
dateB: 2019-03-26T05:00:00.000Z
}
What is applying this convertion?
My enviroment:
Im using knex, objection and moment
Moment is configured with moment.tz.setDefault('Europe/Madrid')
My local machine timezone is set to UTC+09:00 (Chita) for testing
I tried:
Setting a connection timezone on knex creation:
const connection = {
user: dbCreds.username,
password: dbCreds.password,
host: hostname,
database: dbCreds.database,
timezone: '-1:00'
}
const pool = knex({
client: 'mysql2',
connection,
pool: {
min: 2,
max: 30
}
})
I still get the same result
Finally found out whats going on.
Knex, when retrieving the info of the datetime field checks the timezone of the database. You can check it using:
SELECT ##global.time_zone, ##session.time_zone;
This returns SYSTEM SYSTEM in my case. So its using my machine timezone. To test it, I changed it to UTC.
So now, when reading the following value:
dateA = '2019-03-26 15:00:00'
dateB = '2019-03-26 14:00:00'
Knex assumes its in UTC, so the value in UTC is returned:
{
dateA: 2019-03-26T15:00:00.000Z,
dateB: 2019-03-26T14:00:00.000Z
}
Seems not possible to configure MySQL to use 'Europe/Madrid' as default timezone.
I want to delete all records with dates before 20 minutes ago. Postgres (or Sequelize) is not satisfied with the bare javascript Date object I provide as the comparison value.
I'm using sequelize 4.37 on top of a postgres 9.6 database.
The column in question was declared with type: Sequelize.DATE, which research suggests is equivalent to TIMESTAMP WITH TIME ZONE: a full date and time with microsecond precision and a timezone signifier. (That is also what I see when I use the psql CLI tool to describe the table.)
So, I do this:
const Sequelize = require('sequelize')
const { SomeModel } = require('../models.js')
// calculate 20 minutes ago
async function deleteStuff() {
const deletionCutoff = new Date()
deletionCutoff.setMinutes( deletionCutoff.getMinutes() - 20 )
await SomeModel.destroy({
where: {
[ Sequelize.Op.lt ]: { dateColumn: deletionCutoff }
}
})
But I get this error:
Error: Invalid value { dateColumn: 2018-11-21T21:26:16.849Z }
The docs suggest I should be able to provide either a bare javascript Date, or an ISO8601 string, but both throw the same Invalid Value error. The only difference is that, if I pass a string, the error shows single quotes around the value:
// error when dateColumn: deletionCutoff.toISOString()
Error: Invalid value { dateColumn: '2018-11-21T21:26:16.849Z' }
Well, this is pretty embarrassing. I structured the where clause incorrectly.
// BAD CODE
await SomeModel.destroy({
where: {
[ Sequelize.Op.lt ]: {
dateColumn: deletionCutoff
}
}
})
// GOOD CODE
await SomeModel.destroy({
where: {
dateColumn: {
[ Sequelize.Op.lt ]: deletionCutoff
}
}
})
Maybe I should delete the question. Maybe not -- the error I got probably could be more helpful.