Issue
For an application I’m currently working on I need to store dates without a time. I’ve done this by creating a custom schema type that looks something like so:
var mongoose = require('mongoose');
/**
* Registers a new DateOnly type field. Extends the `Date` Schema Type
*/
function DateOnly(key, options) {
mongoose.SchemaTypes.Date.call(this, key, options, 'DateOnly');
}
DateOnly.prototype = Object.create(mongoose.SchemaTypes.Date.prototype);
DateOnly.prototype.cast = (originalValue) => {
try {
var value = originalValue;
if (typeof value === 'string' && !value.match(/^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))(T00:00:00.000Z)?$/)) {
throw new Error('Date is invalid');
} else if (typeof value === 'number') {
value = new Date(value);
}
if (value instanceof Date) {
value = new Date(value.getFullYear(), value.getMonth(), value.getDate());
}
return mongoose.Schema.Types.Date._cast(value);
} catch (err) {
throw new mongoose.SchemaType.CastError('date', originalValue, this.path);
}
};
mongoose.Schema.Types.DateOnly = DateOnly;
module.exports = DateOnly;
Which allows the model to accept date strings (ex: 2020-01-01
) and date objects. Now this will store all dates at midnight UTC time, that way I still get all the advantages of them being stored as dates in mongodb.
Where my issue comes in is that when I’m returning one of these dates to the API it gets returned in full ISO format (ex: 2020-01-01T00:00:00.000Z
), which will get converted into the local user’s time zone. In my timezone, this date will show up as 1 day earlier than desired.
So my question is, how can I make it so that when document.toJSON
is called the date is transformed? I know that what I want to be returning is date.toISOString().substring(0,10)
.
I’ve tried inheriting from the Date class, but I discovered it isn’t compatible with how mongoose and the mongodb driver work.
I know I could write a method to put in the toJSON.transform
options, but then I’d have to do this for every field and model that uses the type.
Solution
A solution for this was added in mongoose 5.9.0
, and can be done like so:
DateOnly.set('transform', (val) => {
return /* transformed value */;
});