You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
7.3 KiB

  1. /*!
  2. * Module requirements.
  3. */
  4. var MongooseError = require('../error');
  5. var utils = require('../utils');
  6. var SchemaType = require('../schematype');
  7. var CastError = SchemaType.CastError;
  8. /**
  9. * Date SchemaType constructor.
  10. *
  11. * @param {String} key
  12. * @param {Object} options
  13. * @inherits SchemaType
  14. * @api public
  15. */
  16. function SchemaDate(key, options) {
  17. SchemaType.call(this, key, options, 'Date');
  18. }
  19. /**
  20. * This schema type's name, to defend against minifiers that mangle
  21. * function names.
  22. *
  23. * @api public
  24. */
  25. SchemaDate.schemaName = 'Date';
  26. /*!
  27. * Inherits from SchemaType.
  28. */
  29. SchemaDate.prototype = Object.create(SchemaType.prototype);
  30. SchemaDate.prototype.constructor = SchemaDate;
  31. /**
  32. * Declares a TTL index (rounded to the nearest second) for _Date_ types only.
  33. *
  34. * This sets the `expireAfterSeconds` index option available in MongoDB >= 2.1.2.
  35. * This index type is only compatible with Date types.
  36. *
  37. * ####Example:
  38. *
  39. * // expire in 24 hours
  40. * new Schema({ createdAt: { type: Date, expires: 60*60*24 }});
  41. *
  42. * `expires` utilizes the `ms` module from [guille](https://github.com/guille/) allowing us to use a friendlier syntax:
  43. *
  44. * ####Example:
  45. *
  46. * // expire in 24 hours
  47. * new Schema({ createdAt: { type: Date, expires: '24h' }});
  48. *
  49. * // expire in 1.5 hours
  50. * new Schema({ createdAt: { type: Date, expires: '1.5h' }});
  51. *
  52. * // expire in 7 days
  53. * var schema = new Schema({ createdAt: Date });
  54. * schema.path('createdAt').expires('7d');
  55. *
  56. * @param {Number|String} when
  57. * @added 3.0.0
  58. * @return {SchemaType} this
  59. * @api public
  60. */
  61. SchemaDate.prototype.expires = function(when) {
  62. if (!this._index || this._index.constructor.name !== 'Object') {
  63. this._index = {};
  64. }
  65. this._index.expires = when;
  66. utils.expires(this._index);
  67. return this;
  68. };
  69. /**
  70. * Check if the given value satisfies a required validator. To satisfy
  71. * a required validator, the given value must be an instance of `Date`.
  72. *
  73. * @param {Any} value
  74. * @param {Document} doc
  75. * @return {Boolean}
  76. * @api public
  77. */
  78. SchemaDate.prototype.checkRequired = function(value) {
  79. return value instanceof Date;
  80. };
  81. /**
  82. * Sets a minimum date validator.
  83. *
  84. * ####Example:
  85. *
  86. * var s = new Schema({ d: { type: Date, min: Date('1970-01-01') })
  87. * var M = db.model('M', s)
  88. * var m = new M({ d: Date('1969-12-31') })
  89. * m.save(function (err) {
  90. * console.error(err) // validator error
  91. * m.d = Date('2014-12-08');
  92. * m.save() // success
  93. * })
  94. *
  95. * // custom error messages
  96. * // We can also use the special {MIN} token which will be replaced with the invalid value
  97. * var min = [Date('1970-01-01'), 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).'];
  98. * var schema = new Schema({ d: { type: Date, min: min })
  99. * var M = mongoose.model('M', schema);
  100. * var s= new M({ d: Date('1969-12-31') });
  101. * s.validate(function (err) {
  102. * console.log(String(err)) // ValidationError: The value of path `d` (1969-12-31) is before the limit (1970-01-01).
  103. * })
  104. *
  105. * @param {Date} value minimum date
  106. * @param {String} [message] optional custom error message
  107. * @return {SchemaType} this
  108. * @see Customized Error Messages #error_messages_MongooseError-messages
  109. * @api public
  110. */
  111. SchemaDate.prototype.min = function(value, message) {
  112. if (this.minValidator) {
  113. this.validators = this.validators.filter(function(v) {
  114. return v.validator !== this.minValidator;
  115. }, this);
  116. }
  117. if (value) {
  118. var msg = message || MongooseError.messages.Date.min;
  119. msg = msg.replace(/{MIN}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString()));
  120. var _this = this;
  121. this.validators.push({
  122. validator: this.minValidator = function(val) {
  123. var min = (value === Date.now ? value() : _this.cast(value));
  124. return val === null || val.valueOf() >= min.valueOf();
  125. },
  126. message: msg,
  127. type: 'min',
  128. min: value
  129. });
  130. }
  131. return this;
  132. };
  133. /**
  134. * Sets a maximum date validator.
  135. *
  136. * ####Example:
  137. *
  138. * var s = new Schema({ d: { type: Date, max: Date('2014-01-01') })
  139. * var M = db.model('M', s)
  140. * var m = new M({ d: Date('2014-12-08') })
  141. * m.save(function (err) {
  142. * console.error(err) // validator error
  143. * m.d = Date('2013-12-31');
  144. * m.save() // success
  145. * })
  146. *
  147. * // custom error messages
  148. * // We can also use the special {MAX} token which will be replaced with the invalid value
  149. * var max = [Date('2014-01-01'), 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).'];
  150. * var schema = new Schema({ d: { type: Date, max: max })
  151. * var M = mongoose.model('M', schema);
  152. * var s= new M({ d: Date('2014-12-08') });
  153. * s.validate(function (err) {
  154. * console.log(String(err)) // ValidationError: The value of path `d` (2014-12-08) exceeds the limit (2014-01-01).
  155. * })
  156. *
  157. * @param {Date} maximum date
  158. * @param {String} [message] optional custom error message
  159. * @return {SchemaType} this
  160. * @see Customized Error Messages #error_messages_MongooseError-messages
  161. * @api public
  162. */
  163. SchemaDate.prototype.max = function(value, message) {
  164. if (this.maxValidator) {
  165. this.validators = this.validators.filter(function(v) {
  166. return v.validator !== this.maxValidator;
  167. }, this);
  168. }
  169. if (value) {
  170. var msg = message || MongooseError.messages.Date.max;
  171. msg = msg.replace(/{MAX}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString()));
  172. var _this = this;
  173. this.validators.push({
  174. validator: this.maxValidator = function(val) {
  175. var max = (value === Date.now ? value() : _this.cast(value));
  176. return val === null || val.valueOf() <= max.valueOf();
  177. },
  178. message: msg,
  179. type: 'max',
  180. max: value
  181. });
  182. }
  183. return this;
  184. };
  185. /**
  186. * Casts to date
  187. *
  188. * @param {Object} value to cast
  189. * @api private
  190. */
  191. SchemaDate.prototype.cast = function(value) {
  192. // If null or undefined
  193. if (value === null || value === void 0 || value === '') {
  194. return null;
  195. }
  196. if (value instanceof Date) {
  197. return value;
  198. }
  199. var date;
  200. if (typeof value === 'boolean') {
  201. throw new CastError('date', value, this.path);
  202. }
  203. if (value instanceof Number || typeof value === 'number'
  204. || String(value) == Number(value)) {
  205. // support for timestamps
  206. date = new Date(Number(value));
  207. } else if (value.valueOf) {
  208. // support for moment.js
  209. date = new Date(value.valueOf());
  210. }
  211. if (!isNaN(date.valueOf())) {
  212. return date;
  213. }
  214. throw new CastError('date', value, this.path);
  215. };
  216. /*!
  217. * Date Query casting.
  218. *
  219. * @api private
  220. */
  221. function handleSingle(val) {
  222. return this.cast(val);
  223. }
  224. SchemaDate.prototype.$conditionalHandlers =
  225. utils.options(SchemaType.prototype.$conditionalHandlers, {
  226. $gt: handleSingle,
  227. $gte: handleSingle,
  228. $lt: handleSingle,
  229. $lte: handleSingle
  230. });
  231. /**
  232. * Casts contents for queries.
  233. *
  234. * @param {String} $conditional
  235. * @param {any} [value]
  236. * @api private
  237. */
  238. SchemaDate.prototype.castForQuery = function($conditional, val) {
  239. var handler;
  240. if (arguments.length !== 2) {
  241. return this.cast($conditional);
  242. }
  243. handler = this.$conditionalHandlers[$conditional];
  244. if (!handler) {
  245. throw new Error('Can\'t use ' + $conditional + ' with Date.');
  246. }
  247. return handler.call(this, val);
  248. };
  249. /*!
  250. * Module exports.
  251. */
  252. module.exports = SchemaDate;