How to prevent Sequelize from converting Date object to local time - node.js

I am using sequelize for a node project. It's connecting to a Postgres databsae, which contains a table with a DATE field (unlike TIMESTAMP WITH TIMEZONE, DATE has not time data).
In the code I'm modeling the date using a javascript Date object, which stores the time as UTC midnight. When I use that to insert a record into the table using that Date object, sequelize is apparently coverting it to local time first because the records are always 1 day behind. So if I want to insert 2000-10-31 into the database I end up with 2000-10-30. I am in UTC-5.
How do I tell sequelize to not convert the Date to a local time before inserting into the database?
Here is some sample code. I also created a repository if you want to run it yourself.
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;
}

The problem is that the date is created as UTC time, but because DATEONLY has no awareness of the timezone, it formats the Date object "as is" (in local time) with format YYYY-MM-DD (using moment.js - see here in source).
For a DATEONLY you can just do this:
var date = new Date(2000, 9, 31);
That will insert the date correctly.
Programmers seldom say this, but for once you were thinking too much about timezones!
OLD INCORRECT ANSWER
It depends on how you are checking the value, but javascript and postgresql are converting it to your local timezone for display.
Sequelize uses a TIMESTAMP WITH TIMEZONE type for a date field (source).
In the postgresql docs it says:
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).
If you are outputting the time value in postgresql, try converting it to UTC. If you are are outputting it in javascript, try date.toUTCString().
The reason your hacks appear to work is because they are actually storing 2000-11-01 05:00 but when you inspect the value, it is converted into your local time zone.

Related

how to store date and timezone in Indian Standard Time(IST) format in node.js?

I have installed moment-timezone node-module and getting the date correctly but when I try to save it to database it is converting back into some another time-zone.
const moment = require('moment-timezone');
const dateIndia = moment.tz(Date.now(), "Asia/Kolkata");
console.log(dateIndia);
Here console is printing the correct IST time:
Moment<2022-06-04T15:08:08+05:30>
Then I am storing it into the database from schema:
bookingDate: {
type: Date,
default: dateIndia,
required: true,
},
But it is saving date into UTC timezone like this:
2022-06-04T09:18:38.540+00:00
That is not about your NodeJS. That is your database time-zone configuration.
You can solve that with one of the 2 below:
1 - Change your database time-zone configurations
2 - When you get from the database just use the same function you used to create the date and convert it back to the timezone you want:
let dateUTC = (moment.tz(databaseDate, "UTC");
let dateIndia = dateUTC.tz("Asia/Kolkata").format()

Store wrong timezone in default date and time

I am trying to store default date and time using mongoose with the Node.JS but somehow, it is storing different time zone value in database. I'm using "MongoDB Atlas Database" as a DB server and also configured default time zone with reference to this. I have also tried this for change time zone and also tried "moment-timezone.js", But didn't get any luck.
I just want to store default date and time with Indian standard time format.
Following is my code for the schema.
const testSchema = new mongoose.Schema({
from: String,
to: String,
amount: Number,
message: {
type: String,
default: ""
},
creationdate: {
type: Date,
default: Date.now
}
});
Please help me with this issue. Show me the best way to solve this problem.
MongoDB stores Date fields as a UTC timestamp in milliseconds since the Unix epoch, which is a 64bit integer (53bit in JS). This is also what Date.now() is. There is no timezone component to the data by design.
If you need a consistent timezone format for a Date object, add a virtual field that returns the required timezone adjusted string or object from the date.
const testSchema = new mongoose.Schema({
creationdate: {
type: Date,
default: Date.now
}
});
testSchema.virtual('formattedCreationDate').get(function() {
return this.creationdate.toLocaleString(); // or day.js/luxon
});
If you need a timezone other than the user or system supplied value, store the required TZ data in another field and use that timezone field in the virtual formatting.
The virtual will probably be easier if you use day.js or luxon.

Date conversion in nodejs and postgres

I have column birthday in postgres with type date, I receive a unixtimestamp from front-end like 716500800. When I am saving it to postgresql, it seems like it is converting based on my local time zone. I don't understand what I should do, here is a code
const date = moment.utc(data.birthday * 1000).format();
console.log(date); // 1992-09-15T00:00:00Z it is right date
db.query(
'UPDATE app_users SET birthday=$1 where id=$2 RETURNING birthday',
[
date,
id
],
(err, bd) => {
console.log(bd.rows); // birthday: 1992-09-14T20:00:00.000Z
}
So I set timezone on server and db to UTC. From front-end I get timezone and chande db field to timestamptz (with time zone). Now I operate with all dates in UTC and show/get from client with time zone.

Knex NodeJS insert date timezone compatible inside PostgreSQL

I have a postgreSQL table like this:
table.string('id');
table.string('name');
table.specificType('data', 'JSONB');
table.timestamp('runDate');
table.boolean('done').default(false);
I wonder what is the safe way to insert date time inside the database.
This is what I do:
await _i_.knex("jobs")
.transacting(ctx ? ctx.transaction : null)
.insert({
id: job.id,
name: job.name,
data: job.data,
id: job.id,
runDate: job.runDate,
done: false
});
When I want to query my table, I use:
return await _i_.knex('jobs')
.transacting(ctx ? ctx.transaction : null)
.whereRaw('"runDate" < NOW()')
.andWhere('done', false)
.returning("*")
.update({
done: true
});
So I can have issue with the timezone if my nodeJS server doesn't have the save timezone than my PostgreSQL.
How do you manage that?
By default knex creates timestamp with time zone (timestamptz) type for .timestamp() columns for PostgreSQL. So, your date and time are stored with timezone. For inserting timestamp values I like to use moment package.
const moment = require('moment');
const dateStr = moment().utc().format();
console.log(dateStr); // => Date in format 'YYYY-MM-DDTHH:mm:ssZ'
knex.insert({ runDate: dateStr })
How about using the DB time on insert: ...runDate: knex.raw("NOW()")...
Then the storage and retrieval dates will be synchronized to the same timezone.
BUT if the job.runDate needs millisecond accuracy, or the record has been held for a significant amount of time before storage, then this would not be appropriate.

Mongoose saving and retrieving dates with UTC time, change to server timezone

I'm using mongoose models to save my records including created and updated dates. Here's my model schema:
var CasesModelSchema = new mongoose.Schema(
{
caseId: Number,
sessionId: String,
createdAt: {type: Date},
updatedAt: {type: Date, default: Date.now},
docs: mongoose.Schema.Types.Mixed
},
{
collection: 'cases'
}
);
The problem I'm facing is that the updatedAt field saves the datetime as ISODate("2017-04-24T12:40:48.193Z"), which is in UTC, but my server's timezone is Asia/Calcutta. Since I need to make queries according to my server's time, I need the datetimes to be saved in my preferred timezone.
Here's the query I need to execute (get all data for the last 10 days)
var today = moment(moment().format('YYYY-MM-DD')).toDate();
var tenDaysDate = moment(moment().format('YYYY-MM-DD')).add(-10, 'days').toDate();
CasesModel.findOne({updatedAt: {$gte: tenDaysDate, $lt: today}},
function(err, caseData){
cl(caseData, __line);
});
What I want is to do a query to get all the updated records in the last 10 days, exactly from 10 days ago midnight (Asia/Calcutta timezone) to today's midnight (Asia/Calcutta timezone). How do I do that?
What I want is to do a query to get all the updated records in the last 10 days, exactly from 10 days ago midnight (Asia/Calcutta timezone) to today's midnight (Asia/Calcutta timezone). How do I do that?
If by "today's midnight" you mean the last midnight that happened (in which case you will not get results from a moment ago), then you can use something like this - using your local time zone:
// use moment-timezone to handle time zones correctly:
const moment = require('moment-timezone');
let end = moment().startOf('day').tz('UTC')
let start = end.subtract(10, 'days');
or explicitly using Asia/Calcutta time zone:
let end = moment.tz('Asia/Calcutta').startOf('day').tz('UTC');
let start = end.subtract(10, 'days');
If by "today's midnight" you mean the next midnight that will happen (in which case you will get results from a moment ago), then you can use something like this - using your local time zone:
let end = moment().endOf('day').tz('UTC')
let start = end.subtract(10, 'days');
or explicitly using Asia/Calcutta time zone:
let end = moment.tz('Asia/Calcutta').endOf('day').tz('UTC');
let start = end.subtract(10, 'days');
You will have UTC dates that correspond to your local midnights, ready to use in Mongo queries.
No need to change anything in Mongo.

Resources