date.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*!
  2. * Module requirements.
  3. */
  4. 'use strict';
  5. const MongooseError = require('../error');
  6. const castDate = require('../cast/date');
  7. const utils = require('../utils');
  8. const SchemaType = require('../schematype');
  9. const CastError = SchemaType.CastError;
  10. /**
  11. * Date SchemaType constructor.
  12. *
  13. * @param {String} key
  14. * @param {Object} options
  15. * @inherits SchemaType
  16. * @api public
  17. */
  18. function SchemaDate(key, options) {
  19. SchemaType.call(this, key, options, 'Date');
  20. }
  21. /**
  22. * This schema type's name, to defend against minifiers that mangle
  23. * function names.
  24. *
  25. * @api public
  26. */
  27. SchemaDate.schemaName = 'Date';
  28. /*!
  29. * Inherits from SchemaType.
  30. */
  31. SchemaDate.prototype = Object.create(SchemaType.prototype);
  32. SchemaDate.prototype.constructor = SchemaDate;
  33. /*!
  34. * ignore
  35. */
  36. SchemaDate._cast = castDate;
  37. /**
  38. * Get/set the function used to cast arbitrary values to dates.
  39. *
  40. * ####Example:
  41. *
  42. * // Mongoose converts empty string '' into `null` for date types. You
  43. * // can create a custom caster to disable it.
  44. * const original = mongoose.Schema.Types.Date.cast();
  45. * mongoose.Schema.Types.Date.cast(v => {
  46. * assert.ok(v !== '');
  47. * return original(v);
  48. * });
  49. *
  50. * // Or disable casting entirely
  51. * mongoose.Schema.Types.Date.cast(false);
  52. *
  53. * @param {Function} caster
  54. * @return {Function}
  55. * @function get
  56. * @static
  57. * @api public
  58. */
  59. SchemaDate.cast = function cast(caster) {
  60. if (arguments.length === 0) {
  61. return this._cast;
  62. }
  63. if (caster === false) {
  64. caster = v => {
  65. if (v != null && !(v instanceof Date)) {
  66. throw new Error();
  67. }
  68. return v;
  69. };
  70. }
  71. this._cast = caster;
  72. return this._cast;
  73. };
  74. /**
  75. * Declares a TTL index (rounded to the nearest second) for _Date_ types only.
  76. *
  77. * This sets the `expireAfterSeconds` index option available in MongoDB >= 2.1.2.
  78. * This index type is only compatible with Date types.
  79. *
  80. * ####Example:
  81. *
  82. * // expire in 24 hours
  83. * new Schema({ createdAt: { type: Date, expires: 60*60*24 }});
  84. *
  85. * `expires` utilizes the `ms` module from [guille](https://github.com/guille/) allowing us to use a friendlier syntax:
  86. *
  87. * ####Example:
  88. *
  89. * // expire in 24 hours
  90. * new Schema({ createdAt: { type: Date, expires: '24h' }});
  91. *
  92. * // expire in 1.5 hours
  93. * new Schema({ createdAt: { type: Date, expires: '1.5h' }});
  94. *
  95. * // expire in 7 days
  96. * var schema = new Schema({ createdAt: Date });
  97. * schema.path('createdAt').expires('7d');
  98. *
  99. * @param {Number|String} when
  100. * @added 3.0.0
  101. * @return {SchemaType} this
  102. * @api public
  103. */
  104. SchemaDate.prototype.expires = function(when) {
  105. if (!this._index || this._index.constructor.name !== 'Object') {
  106. this._index = {};
  107. }
  108. this._index.expires = when;
  109. utils.expires(this._index);
  110. return this;
  111. };
  112. /*!
  113. * ignore
  114. */
  115. SchemaDate._checkRequired = v => v instanceof Date;
  116. /**
  117. * Override the function the required validator uses to check whether a string
  118. * passes the `required` check.
  119. *
  120. * ####Example:
  121. *
  122. * // Allow empty strings to pass `required` check
  123. * mongoose.Schema.Types.String.checkRequired(v => v != null);
  124. *
  125. * const M = mongoose.model({ str: { type: String, required: true } });
  126. * new M({ str: '' }).validateSync(); // `null`, validation passes!
  127. *
  128. * @param {Function} fn
  129. * @return {Function}
  130. * @function checkRequired
  131. * @static
  132. * @api public
  133. */
  134. SchemaDate.checkRequired = SchemaType.checkRequired;
  135. /**
  136. * Check if the given value satisfies a required validator. To satisfy
  137. * a required validator, the given value must be an instance of `Date`.
  138. *
  139. * @param {Any} value
  140. * @param {Document} doc
  141. * @return {Boolean}
  142. * @api public
  143. */
  144. SchemaDate.prototype.checkRequired = function(value, doc) {
  145. if (SchemaType._isRef(this, value, doc, true)) {
  146. return !!value;
  147. }
  148. // `require('util').inherits()` does **not** copy static properties, and
  149. // plugins like mongoose-float use `inherits()` for pre-ES6.
  150. const _checkRequired = typeof this.constructor.checkRequired == 'function' ?
  151. this.constructor.checkRequired() :
  152. SchemaDate.checkRequired();
  153. return _checkRequired(value);
  154. };
  155. /**
  156. * Sets a minimum date validator.
  157. *
  158. * ####Example:
  159. *
  160. * var s = new Schema({ d: { type: Date, min: Date('1970-01-01') })
  161. * var M = db.model('M', s)
  162. * var m = new M({ d: Date('1969-12-31') })
  163. * m.save(function (err) {
  164. * console.error(err) // validator error
  165. * m.d = Date('2014-12-08');
  166. * m.save() // success
  167. * })
  168. *
  169. * // custom error messages
  170. * // We can also use the special {MIN} token which will be replaced with the invalid value
  171. * var min = [Date('1970-01-01'), 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).'];
  172. * var schema = new Schema({ d: { type: Date, min: min })
  173. * var M = mongoose.model('M', schema);
  174. * var s= new M({ d: Date('1969-12-31') });
  175. * s.validate(function (err) {
  176. * console.log(String(err)) // ValidationError: The value of path `d` (1969-12-31) is before the limit (1970-01-01).
  177. * })
  178. *
  179. * @param {Date} value minimum date
  180. * @param {String} [message] optional custom error message
  181. * @return {SchemaType} this
  182. * @see Customized Error Messages #error_messages_MongooseError-messages
  183. * @api public
  184. */
  185. SchemaDate.prototype.min = function(value, message) {
  186. if (this.minValidator) {
  187. this.validators = this.validators.filter(function(v) {
  188. return v.validator !== this.minValidator;
  189. }, this);
  190. }
  191. if (value) {
  192. let msg = message || MongooseError.messages.Date.min;
  193. msg = msg.replace(/{MIN}/, (value === Date.now ? 'Date.now()' : value.toString()));
  194. const _this = this;
  195. this.validators.push({
  196. validator: this.minValidator = function(val) {
  197. let _value = value;
  198. if (typeof value === 'function' && value !== Date.now) {
  199. _value = _value.call(this);
  200. }
  201. const min = (_value === Date.now ? _value() : _this.cast(_value));
  202. return val === null || val.valueOf() >= min.valueOf();
  203. },
  204. message: msg,
  205. type: 'min',
  206. min: value
  207. });
  208. }
  209. return this;
  210. };
  211. /**
  212. * Sets a maximum date validator.
  213. *
  214. * ####Example:
  215. *
  216. * var s = new Schema({ d: { type: Date, max: Date('2014-01-01') })
  217. * var M = db.model('M', s)
  218. * var m = new M({ d: Date('2014-12-08') })
  219. * m.save(function (err) {
  220. * console.error(err) // validator error
  221. * m.d = Date('2013-12-31');
  222. * m.save() // success
  223. * })
  224. *
  225. * // custom error messages
  226. * // We can also use the special {MAX} token which will be replaced with the invalid value
  227. * var max = [Date('2014-01-01'), 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).'];
  228. * var schema = new Schema({ d: { type: Date, max: max })
  229. * var M = mongoose.model('M', schema);
  230. * var s= new M({ d: Date('2014-12-08') });
  231. * s.validate(function (err) {
  232. * console.log(String(err)) // ValidationError: The value of path `d` (2014-12-08) exceeds the limit (2014-01-01).
  233. * })
  234. *
  235. * @param {Date} maximum date
  236. * @param {String} [message] optional custom error message
  237. * @return {SchemaType} this
  238. * @see Customized Error Messages #error_messages_MongooseError-messages
  239. * @api public
  240. */
  241. SchemaDate.prototype.max = function(value, message) {
  242. if (this.maxValidator) {
  243. this.validators = this.validators.filter(function(v) {
  244. return v.validator !== this.maxValidator;
  245. }, this);
  246. }
  247. if (value) {
  248. let msg = message || MongooseError.messages.Date.max;
  249. msg = msg.replace(/{MAX}/, (value === Date.now ? 'Date.now()' : value.toString()));
  250. const _this = this;
  251. this.validators.push({
  252. validator: this.maxValidator = function(val) {
  253. let _value = value;
  254. if (typeof _value === 'function' && _value !== Date.now) {
  255. _value = _value.call(this);
  256. }
  257. const max = (_value === Date.now ? _value() : _this.cast(_value));
  258. return val === null || val.valueOf() <= max.valueOf();
  259. },
  260. message: msg,
  261. type: 'max',
  262. max: value
  263. });
  264. }
  265. return this;
  266. };
  267. /**
  268. * Casts to date
  269. *
  270. * @param {Object} value to cast
  271. * @api private
  272. */
  273. SchemaDate.prototype.cast = function(value) {
  274. const castDate = typeof this.constructor.cast === 'function' ?
  275. this.constructor.cast() :
  276. SchemaDate.cast();
  277. try {
  278. return castDate(value);
  279. } catch (error) {
  280. throw new CastError('date', value, this.path);
  281. }
  282. };
  283. /*!
  284. * Date Query casting.
  285. *
  286. * @api private
  287. */
  288. function handleSingle(val) {
  289. return this.cast(val);
  290. }
  291. SchemaDate.prototype.$conditionalHandlers =
  292. utils.options(SchemaType.prototype.$conditionalHandlers, {
  293. $gt: handleSingle,
  294. $gte: handleSingle,
  295. $lt: handleSingle,
  296. $lte: handleSingle
  297. });
  298. /**
  299. * Casts contents for queries.
  300. *
  301. * @param {String} $conditional
  302. * @param {any} [value]
  303. * @api private
  304. */
  305. SchemaDate.prototype.castForQuery = function($conditional, val) {
  306. if (arguments.length !== 2) {
  307. return this._castForQuery($conditional);
  308. }
  309. const handler = this.$conditionalHandlers[$conditional];
  310. if (!handler) {
  311. throw new Error('Can\'t use ' + $conditional + ' with Date.');
  312. }
  313. return handler.call(this, val);
  314. };
  315. /*!
  316. * Module exports.
  317. */
  318. module.exports = SchemaDate;