I've been tearing my hair out for the past 2 hours, At first I thought Moment.js is the culprit for not returning a correct time, but it was mongoose Date.now that has been doing some evil stuff.
Here's the code
const moment = require('moment');
const mongoose = require('mongoose');
const item = new mongoose.Schema({
time: { type: Date, default: Date.now },
time2: { type: Date }
});
As you can see I have two fields, one is for the default date from mongoose and the other one is just a field for storing date.
item.pre('save', function() {
console.log(moment()); // Showing a correct date and time
console.log(this.time); // Showing a correct date but false time
this.time2 = moment(); // When it is saved to the database, it will show a correct date but false time
});
The result is
moment("2017-01-09T19:42:48.896") // the first console.log. This is correct, the time is correct
2017-01-09T11:42:48.884Z // Second console.log. The date is correct but the time is FALSE
I thought If I do this everything will be solved
const item = new mongoose.Schema({
time: { type: Date, default: moment() },
time2: { type: Date, default: Date.now }
});
But you know what is the console.log for the first field which is time?
2017-01-09T11:42:48.884Z // it is this time which is WRONG TIME
My guess would be that mongoose data type which is Date has an inaccurate timezone check.
Any help would be appreciated.
You are comparing two different things. moment() gives time in local time zone and Date.now is time in UTC. The only reason mongoose has that way is because mongo db saves it that way. No fix is required here.
Just convert the fetched mongoose date back to local time zone using moment library.
Related
I'm using moment for generating time and date:
const moment = require('moment-timezone');
const emailModel = require('./api/models/emails');
sentTime=moment().tz('America/Los_Angeles').format();
console.log(sentTime); //console log shows correct time
emailModel.findOneAndUpdate({ _id: emailInfo._id }, {sentTime: sentTime }, { upsert: true },function (err, doc) {
if (err)
console.log(err);
});
And this is Schema that I'm using mongoose :
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const EmailSchema = new Schema({
.
.
.
sentTime: {
type: Date,
trim: true
}
.
.
.
});
Problem is:
Console log shows correct time 2020-01-07T12:23:00-08:00 BUT mongoose saved incorrect timezone in DB : 2020-01-07T20:23:01.000+00:00
Currently the default behavior of Mongodb is to: (From the docs)
MongoDB stores times in UTC by default, and will convert any local
time representations into this form.
As a solution (and rightly so) what they recommend is:
Applications that must operate or report on some unmodified local time
value may store the time zone alongside the UTC timestamp, and compute
the original local time in their application logic.
Update:
Since you are already using moment-timezone a simple way I would go about this is:
Change the EmailSchema to have a timezone field and create a Mongoose virtual field on that schema to get adjusted time.
const schemaOpts = { toJSON: { virtuals: true } };
const EmailSchema = new Schema(
{
sentTime: {
type: Date,
trim: true
},
timeZone: {
type: String
}
},
schemaOpts
);
EmailSchema.virtual("adjustedTime").get(function() {
return moment.tz(this.sentTime, this.timeZone).format();
});
//fetching data
const result = await EmailSchema.findOne({}).exec();
console.info("result::", result.toJSON());
//note that if using .lean() for performance which has a caveat on using .toJSON()
trick for this case is before save, you need to add time with date. Ex: 2021/01/02 ==> 2021/01/02 15:00:00, ofcouse hour is always equal or greater than 04:00:00. Becase without time, date will be 00:00:00 and mongo will convert it to default timezone and substract hour with 4.
I am trying to update the automatically added createdAt date on a record to right now.
Here the code I am using:
Document.findOneAndUpdate({_id: docId}, {createdAt: new Date()}, function(err, updated) {});
It works on my dev environment, but not the server. It does not return an error, but does not update the record, just returns the unchanged record.
I tried formatting it with new Date().toISOnew Date().toISOString() or .toGMTString() but both still only work on the dev environment. Both have node 6.10, but the server has mongo 3.4.4 and the dev has mongo 3.2.10.
If I add another field to be updated (second arugment), that field gets updated fine, but createdAt remains unchanged.
Automatic createdAt and updatedAt fields are populated by mongoose using the timestamps option as
const schema = new Schema({
// Your schema...
}, {
timestamps: { createdAt: true, updatedAt: false }
})
If you take a look at the source code you'll see that createdAt is excluded from updates. It's fairly easy though to modify your schema accordingly.
const schema = mongoose.Schema({
name: String,
createdAt: { type: Date, default: Date.now }
});
const Test = mongoose.model('test', schema);
const john = await Test.create({name: 'John Doe'});
console.log(john);
const updatedJohn = await Test.findOneAndUpdate({name: 'John Doe'}, { createdAt: Date.now() });
console.log(updatedJohn);
I do not know if it works for update, but for create, I first make sure have the createdAt field abled in my DB and then set the createdAt field like this:
{createdAt: moment().toISOString()}
because createdAt and updatedAt are ISO strings.
Edit: Just like Nigel mentioned, I do use moment library but new Date().toISOString() gives you the same result.
In model schema,
Using
updated: {
type: Date,
default: Date.now
In server.js
put(function(req, res) {
var query = {name: req.params.name};
// use our bear model to find the bear we want
Domain.find(query, function(err, domains) {
if (err)
res.send(err);
var domain = domains[0];
domain.password = req.body.password; // update the bears info
domain.updated = new Date();
// save the bear
domain.save(function(err, data) {
if (err)
res.send(err);
res.json({ status: 'success', message: 'domain updated!' }, data);
});
});
});
However,
In db side it shows,
"updated": "2016-02-27T16:20:42.941Z"
But, my timezone is UTC+02.00
So it should be like 18:20:42
What I'm doing wrong?
I'm using moment-timezone
npm install moment-timezone
const moment = require('moment-timezone');
const dateThailand = moment.tz(Date.now(), "Asia/Bangkok");
console.log(dateThailand); // "2018-08-20T16:35:14.033+07:00"
*** Asia/Bangkok +07:00
Schema in the mongoose.
const categorySchema = new Schema(
{
_id: {type: mongoose.Schema.Types.ObjectId, auto: true},
c_name: String,
created_by: String,
created_date: {type: Date, default: dateThailand},
updated_by: String,
updated_date: {type: Date, default: dateThailand}
}, {_id: false}
);
See up that created_date, updated_date: {type: Date, default: dateThailand }
Read more: http://momentjs.com/timezone/docs/
*If you using Robo 3T tool.
You can set "Display Dates In..."
Options > Display Dates In... > Local Timezone
:) Work for me.
The timestamps are timezone agnostic, stored as a unix timestamp. This timestamp will work across timezones, and node interprets it using current timezone of the server. The date you've shown is correctly stored. As soon as you'll retrieve it, if your server's timezone is UTC+2, it will show you correct time.
There is nothing wrong in your code. MongoDb saves date in UTC format no matter in whichever timezone you try to insert your date.
If you log domain.updated before saving in DB, result will be UTC+2 (your local time)
If you see updated column in DB, result will be in UTC
If you fetch updated column value from DB, then again result will be in UTC+2 (your local time)
I changed this,
var utc = new Date();
utc.setHours( utc.getHours() + 2);
domain.updated = utc;
Now it works.
You can create a Date Object from a specific UTC time:
new Date(Date.UTC(year, month, day, hour, minute, second))
Remember that no matter what you use to set time in mongoose schema, mongoose will always use UTC time, hence you need to dynamically allocate the UTC timestamp inside the Schema. Here it goes :-
var current = new Date();
const timeStamp = new Date(Date.UTC(current.getFullYear(),
current.getMonth(),current.getDate(),current.getHours(),
current.getMinutes(),current.getSeconds(), current.getMilliseconds()));
//Here goes your schema
const auditSchema = mongoose.Schema({
dateTime : { type: Date, default : timeStamp }
})
Using moment.js it is as easy as:
var moment = require('moment');
var utcDate = moment.utc().toDate();
Enjoy!
I am using Date.now() and Date.now in mongoose model.
I am a little bit confused about the difference between them. Could you please help me?
I know this is an old question, but the accepted answer doesn't explain the difference properly. It explains the difference in behaviour, but not how it actually works.
In your mongoose schema, your default can be either a value of the specified type or a function that returns a value of the specified type. Date.now is a built in Javascript function that returns the current unix timestamp as a number.
If you pass Date.now as the default in your mongoose schema, you are passing the function and mongoose will call that function every time a document needs a default value for that property. This results in the current time, at the time of document creation, being stored as the value for that property.
However, if you pass Date.now() instead, you are passing the value returned by Date.now() rather than the function. By doing this, your documents will get the current time, at the time of schema creation, as the default value for that property. This means that your documents will store the time of the latest deployment, which is probably not what you want.
If Date.now was a constant "replaced by Mongoose with the current datetime when creating a new record", as suggested by the accepted answer, then the following should logically work:
validUntil: {
type: Date,
default: Date.now + 3*60*60*1000 // 3 hours from now
}
But Date.now is not magically replaced by mongoose, it's a function and you can't add a number to a function. However, you can call a function inside your own function and add a number to the result, as demonstrated below:
validUntil: {
type: Date,
default: () => Date.now() + 3*60*60*1000 // 3 hours from now
}
As it wasn't clear from the accepted answer that Date.now is a function and not a mongoose constant, I wanted to clarify that. The difference between Date.now() and Date.now is that Date.now() calls the function and returns the result, while Date.now returns the function itself.
Date.now can be used in your Mongoose schema definition to set a default value for a field, whereas Date.now() is the JavaScript equivalent. For example, when setting a default value in your schema definition, you use Date.now. With this schema definition, Mongoose will populated createdDate with the current time.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//schema
var yourSchema= new Schema({
text: {type: String},
createdAt: {type: Date, default: Date.now}
});
However, when writing JavaScript code against your schema, you have to use Date.now()
yourSchema.pre('save', function doSomething(next){
var something = this;
something.createdAt(Date.now());
next();
});
Im using this scheme for a session in my node.js app
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// define the schema for our user session model
var UserSessionSchema = new Schema({
sessionActivity: { type: Date, expires: '15s' }, // Expire after 15 s
user_token: { type: String, required: true }
});
module.exports = mongoose.model('UserSession', UserSessionSchema);
And I create a "session" in my app with:
...
var session = new Session();
session.user_token = profile.token;
session.save(function(save_err) {
if (save_err) {
....
} else {
// store session id in profile
profile.session_key = session._id;
profile.save(function(save_err, profile) {
if (save_err) {
...
} else {
res.json({ status: 'OK', session_id: profile.session_id });
}
});
...
The problem is that the document lives permanetly, its never expires. It should only live for 15 seconds (up to a minute). Whats wrong with my code? I have tried to set the expries: string to a number i.e 15, to a string '15s' and so on.
var UserSessionSchema = new Schema({
sessionActivity: { type: Date, expires: '15s', default: Date.now }, // Expire after 15 s
user_token: { type: String, required: true }
});
A TTL index deletes a document 'x' seconds after its value (which should be a Date or an array of Dates) has passed. The TTL is checked every minute, so it may live a little longer than your given 15 seconds.
To give the date a default value, you can use the default option in Mongoose. It accepts a function. In this case, Date() returns the current timestamp. This will set the date to the current time once.
You could also go this route:
UserSessionSchema.pre("save", function(next) {
this.sessionActivity = new Date();
next();
});
This will update the value every time you call .save() (but not .update()).
To double check the indexes that have been created in the DB you can run this command in your mongo shell db.yourdb.getIndexes(). When changing the indexes you have to manually delete it in the collection before the new one will take effect. Check here for more information Mongoose expires property not working properly