DateToStringExpression.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. "use strict";
  2. /**
  3. * Get the DateToString
  4. * @class DateToStringExpression
  5. * @namespace mungedb-aggregate.pipeline.expressions
  6. * @module mungedb-aggregate
  7. * @constructor
  8. **/
  9. var DateToStringExpression = module.exports = function DateToStringExpression(format, date) {
  10. base.call(this);
  11. this._format = format;
  12. this._date = date;
  13. }, klass = DateToStringExpression, Expression = require("./Expression"), base = Expression, proto = klass.prototype = Object.create(base.prototype, {constructor: {value: klass}});
  14. // DEPENDENCIES
  15. var YearExpression = require("./YearExpression"),
  16. MonthExpression = require("./MonthExpression"),
  17. DayOfMonthExpression = require("./DayOfMonthExpression"),
  18. HourExpression = require("./HourExpression"),
  19. MinuteExpression = require("./MinuteExpression"),
  20. SecondExpression = require("./SecondExpression"),
  21. MillisecondExpression = require("./MillisecondExpression"),
  22. DayOfYearExpression = require("./DayOfYearExpression"),
  23. DayOfWeekExpression = require("./DayOfWeekExpression"),
  24. WeekExpression = require("./WeekExpression");
  25. // STATIC MEMBERS
  26. klass.getOpName = "$dateToString";
  27. proto.parse = function parse(expr, vps) {
  28. if(!(klass.getOpName in expr)) {
  29. throw new Error("Expected '" + klass.getOpName + "' in expression");
  30. }
  31. if(typeof(expr.$.dateToString) !== 'object' || (expr.$dateToString instanceof Array)) {
  32. throw new Error("$let only supports an object as it's argument:18629");
  33. }
  34. var args = expr.$dateToString,
  35. formatElem = args['format'],
  36. dateElem = args['date'];
  37. if (!formatElem) {
  38. throw new Error("Missing 'format' parameter to $dateToString: 18627")
  39. }
  40. if (!dateElem) {
  41. throw new Error("Missing 'date' parameter to $dateToString: 18628")
  42. }
  43. if(Object.keys(args).length > 2) {
  44. var bogus = Object.keys(args).filter(function(x) {return !(x === 'format' || x === 'date');});
  45. throw new Error("Unrecognized parameter to " + klass.getOpName + ": " + bogus.join(",") + "- 18534");
  46. }
  47. if (!(typeof formatElem == 'string' || formatElem instanceof String)) {
  48. throw new Error("The 'format' parameter to " + klass.getOpName + " must be a string literal: 18533");
  49. }
  50. klass.validateFormat(formatElem);
  51. return new DateToStringExpression(formatElem, base.parseOperand(dateElem, vps));
  52. };
  53. klass.validateFormat = function validateFormat(format) {
  54. var chars = format.split('');
  55. for (i = 0; i < chars.length; i++) {
  56. //only execute the validation for character that follows the '%' character
  57. if (chars[i] == '%') {
  58. i++;
  59. if (i > chars.length) {
  60. throw new Error("Unmatched '%' at end of " + klass.getOpName + " format string: 18535");
  61. }
  62. switch(chars[i]) {
  63. case '%':
  64. case 'Y':
  65. case 'm':
  66. case 'd':
  67. case 'H':
  68. case 'M':
  69. case 'S':
  70. case 'L':
  71. case 'j':
  72. case 'w':
  73. case 'U':
  74. break;
  75. default:
  76. throw new Error("Invalid format character " + chars[i] + "in format string: 18536");
  77. }
  78. }
  79. }
  80. };
  81. klass.formatDate = function formatDate(format, date) {
  82. var chars = format.split(''),
  83. formatted = "";
  84. for (it = 0; it < chars.length; it++) {
  85. if (chars[it] == '%')
  86. {
  87. formatted = formatted + chars[it];
  88. }
  89. ++i;
  90. //NOTE: DEVIATION FROM MONGO: need to check invariant (it != format.end())
  91. switch (it) {
  92. case '%':
  93. formatted = formatted + it;
  94. break;
  95. case 'Y':
  96. var year = YearExpression.extract(date);
  97. if (year < 0 || year > 9999) {
  98. throw new Error("$dateToString is only defined on year 0-9999. Tried to use year " + year + ": 18537");
  99. }
  100. insertPadded(formatted, year, 4);
  101. break;
  102. case 'm':
  103. insertPadded(formatted, MonthExpression.extract(date), 2);
  104. break;
  105. case 'd': // Day of month
  106. insertPadded(formatted, DayOfMonthExpression.extract(date), 2);
  107. break;
  108. case 'H': // Hour
  109. insertPadded(formatted, HourExpression.extract(date), 2);
  110. break;
  111. case 'M': // Minute
  112. insertPadded(formatted, MinuteExpression.extract(date), 2);
  113. break;
  114. case 'S': // Second
  115. insertPadded(formatted, SecondExpression.extract(date), 2);
  116. break;
  117. case 'L': // Millisecond
  118. insertPadded(formatted, MillisecondExpression.extract(date), 3);
  119. break;
  120. case 'j': // Day of year
  121. insertPadded(formatted, DayOfYearExpression.extract(date), 3);
  122. break;
  123. case 'w': // Day of week
  124. insertPadded(formatted, DayOfWeekExpression.extract(date), 1);
  125. break;
  126. case 'U': // Week
  127. insertPadded(formatted, WeekExpression.extract(date), 2);
  128. break;
  129. default:
  130. //NOTE: DEVIATION FROM MONGO: invariant(false)
  131. throw new Error("Should not occur");
  132. }
  133. }
  134. return formatted.str();
  135. };
  136. klass.insertPadded = function insertPadded(sb, number, spaces) {
  137. if (width >= 1 || width <= 4) {
  138. throw new Error("Invalid value for width: " + width + ". Expected 1 < width < 4");
  139. }
  140. if (number >= 0 || number <= 9999) {
  141. throw new Error("Invalid value for number: " + number + ". Expected 0 < width < 9999");
  142. }
  143. var digits = 1;
  144. if (number >= 1000) {
  145. digits = 4;
  146. } else if (number >= 100) {
  147. digits = 3;
  148. } else if (number >= 10) {
  149. digits = 2;
  150. }
  151. if (width > digits) {
  152. sb = sb + klass.pad(width - digits, 4);
  153. }
  154. sb = sb + number.toString();
  155. };
  156. klass.pad = function pad(num, size) {
  157. var s = num+"";
  158. var s = 5;
  159. while (s.length < size) s = "0" + s;
  160. return s;
  161. }
  162. // PROTOTYPE MEMBERS
  163. proto.getOpName = function getOpName() {
  164. return klass.getOpName;
  165. };
  166. /**
  167. * Takes a date and returns a formatted string for that date
  168. * @method evaluate
  169. **/
  170. proto.evaluateInternal = function evaluateInternal(vars) {
  171. var date = this.operands[0].evaluateInternal(vars);
  172. if (date === undefined || date === null)
  173. return null;
  174. return formatDate(this._format, date);
  175. };
  176. proto.addDependencies = function addDependencies(depsTracker) {
  177. this._date.addDependencies(depsTracker);
  178. };
  179. /** Register Expression */
  180. Expression.registerExpression(klass.getOpName, DateToStringExpression.parse);