date.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. 'use strict';
  2. // Load modules
  3. const Any = require('./any');
  4. const Ref = require('./ref');
  5. const Hoek = require('hoek');
  6. const Moment = require('moment');
  7. // Declare internals
  8. const internals = {};
  9. internals.isoDate = /^(?:\d{4}(?!\d{2}\b))(?:(-?)(?:(?:0[1-9]|1[0-2])(?:\1(?:[12]\d|0[1-9]|3[01]))?|W(?:[0-4]\d|5[0-2])(?:-?[1-7])?|(?:00[1-9]|0[1-9]\d|[12]\d{2}|3(?:[0-5]\d|6[1-6])))(?![T]$|[T][\d]+Z$)(?:[T\s](?:(?:(?:[01]\d|2[0-3])(?:(:?)[0-5]\d)?|24\:?00)(?:[.,]\d+(?!:))?)(?:\2[0-5]\d(?:[.,]\d+)?)?(?:[Z]|(?:[+-])(?:[01]\d|2[0-3])(?::?[0-5]\d)?)?)?)?$/;
  10. internals.invalidDate = new Date('');
  11. internals.isIsoDate = (() => {
  12. const isoString = internals.isoDate.toString();
  13. return (date) => {
  14. return date && (date.toString() === isoString);
  15. };
  16. })();
  17. internals.Date = class extends Any {
  18. constructor() {
  19. super();
  20. this._type = 'date';
  21. }
  22. _base(value, state, options) {
  23. const result = {
  24. value: (options.convert && internals.Date.toDate(value, this._flags.format, this._flags.timestamp, this._flags.multiplier)) || value
  25. };
  26. if (result.value instanceof Date && !isNaN(result.value.getTime())) {
  27. result.errors = null;
  28. }
  29. else if (!options.convert) {
  30. result.errors = this.createError('date.strict', null, state, options);
  31. }
  32. else {
  33. let context = null;
  34. let type;
  35. if (internals.isIsoDate(this._flags.format)) {
  36. type = 'isoDate';
  37. }
  38. else if (this._flags.timestamp) {
  39. type = 'timestamp.' + this._flags.timestamp;
  40. }
  41. else if (this._flags.format) {
  42. type = 'format';
  43. context = { format: this._flags.format };
  44. }
  45. else {
  46. type = 'base';
  47. }
  48. result.errors = this.createError('date.' + type, context, state, options);
  49. }
  50. return result;
  51. }
  52. static toDate(value, format, timestamp, multiplier) {
  53. if (value instanceof Date) {
  54. return value;
  55. }
  56. if (typeof value === 'string' ||
  57. (typeof value === 'number' && !isNaN(value) && isFinite(value))) {
  58. if (typeof value === 'string' &&
  59. /^[+-]?\d+(\.\d+)?$/.test(value)) {
  60. value = parseFloat(value);
  61. }
  62. let date;
  63. if (format) {
  64. if (internals.isIsoDate(format)) {
  65. date = format.test(value) ? new Date(value) : internals.invalidDate;
  66. }
  67. else {
  68. date = Moment(value, format, true);
  69. date = date.isValid() ? date.toDate() : internals.invalidDate;
  70. }
  71. }
  72. else if (timestamp && multiplier) {
  73. date = new Date(value * multiplier);
  74. }
  75. else {
  76. date = new Date(value);
  77. }
  78. if (!isNaN(date.getTime())) {
  79. return date;
  80. }
  81. }
  82. return null;
  83. }
  84. format(format) {
  85. Hoek.assert(typeof format === 'string' || (Array.isArray(format) && format.every((f) => typeof f === 'string')), 'Invalid format.');
  86. const obj = this.clone();
  87. obj._flags.format = format;
  88. return obj;
  89. }
  90. iso() {
  91. const obj = this.clone();
  92. obj._flags.format = internals.isoDate;
  93. return obj;
  94. }
  95. timestamp(type) {
  96. type = type || 'javascript';
  97. const allowed = ['javascript', 'unix'];
  98. Hoek.assert(allowed.indexOf(type) !== -1, '"type" must be one of "' + allowed.join('", "') + '"');
  99. const obj = this.clone();
  100. obj._flags.timestamp = type;
  101. obj._flags.multiplier = type === 'unix' ? 1000 : 1;
  102. return obj;
  103. }
  104. _isIsoDate(value) {
  105. return internals.isoDate.test(value);
  106. }
  107. };
  108. internals.compare = function (type, compare) {
  109. return function (date) {
  110. const isNow = date === 'now';
  111. const isRef = Ref.isRef(date);
  112. if (!isNow && !isRef) {
  113. date = internals.Date.toDate(date);
  114. }
  115. Hoek.assert(date, 'Invalid date format');
  116. return this._test(type, date, function (value, state, options) {
  117. let compareTo;
  118. if (isNow) {
  119. compareTo = Date.now();
  120. }
  121. else if (isRef) {
  122. compareTo = internals.Date.toDate(date(state.parent, options));
  123. if (!compareTo) {
  124. return this.createError('date.ref', { ref: date.key }, state, options);
  125. }
  126. compareTo = compareTo.getTime();
  127. }
  128. else {
  129. compareTo = date.getTime();
  130. }
  131. if (compare(value.getTime(), compareTo)) {
  132. return value;
  133. }
  134. return this.createError('date.' + type, { limit: new Date(compareTo) }, state, options);
  135. });
  136. };
  137. };
  138. internals.Date.prototype.min = internals.compare('min', (value, date) => value >= date);
  139. internals.Date.prototype.max = internals.compare('max', (value, date) => value <= date);
  140. module.exports = new internals.Date();