index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. "use strict";
  2. function padWithZeros(vNumber, width) {
  3. var numAsString = vNumber.toString();
  4. while (numAsString.length < width) {
  5. numAsString = "0" + numAsString;
  6. }
  7. return numAsString;
  8. }
  9. function addZero(vNumber) {
  10. return padWithZeros(vNumber, 2);
  11. }
  12. /**
  13. * Formats the TimeOffset
  14. * Thanks to http://www.svendtofte.com/code/date_format/
  15. * @private
  16. */
  17. function offset(timezoneOffset) {
  18. var os = Math.abs(timezoneOffset);
  19. var h = String(Math.floor(os / 60));
  20. var m = String(os % 60);
  21. if (h.length === 1) {
  22. h = "0" + h;
  23. }
  24. if (m.length === 1) {
  25. m = "0" + m;
  26. }
  27. return timezoneOffset < 0 ? "+" + h + m : "-" + h + m;
  28. }
  29. function datePart(date, displayUTC, part) {
  30. return displayUTC ? date["getUTC" + part]() : date["get" + part]();
  31. }
  32. function asString(format, date) {
  33. if (typeof format !== "string") {
  34. date = format;
  35. format = module.exports.ISO8601_FORMAT;
  36. }
  37. if (!date) {
  38. date = module.exports.now();
  39. }
  40. var displayUTC = format.indexOf("O") > -1;
  41. var vDay = addZero(datePart(date, displayUTC, "Date"));
  42. var vMonth = addZero(datePart(date, displayUTC, "Month") + 1);
  43. var vYearLong = addZero(datePart(date, displayUTC, "FullYear"));
  44. var vYearShort = addZero(vYearLong.substring(2, 4));
  45. var vYear = format.indexOf("yyyy") > -1 ? vYearLong : vYearShort;
  46. var vHour = addZero(datePart(date, displayUTC, "Hours"));
  47. var vMinute = addZero(datePart(date, displayUTC, "Minutes"));
  48. var vSecond = addZero(datePart(date, displayUTC, "Seconds"));
  49. var vMillisecond = padWithZeros(
  50. datePart(date, displayUTC, "Milliseconds"),
  51. 3
  52. );
  53. var vTimeZone = offset(date.getTimezoneOffset());
  54. var formatted = format
  55. .replace(/dd/g, vDay)
  56. .replace(/MM/g, vMonth)
  57. .replace(/y{1,4}/g, vYear)
  58. .replace(/hh/g, vHour)
  59. .replace(/mm/g, vMinute)
  60. .replace(/ss/g, vSecond)
  61. .replace(/SSS/g, vMillisecond)
  62. .replace(/O/g, vTimeZone);
  63. return formatted;
  64. }
  65. function extractDateParts(pattern, str, missingValuesDate) {
  66. var matchers = [
  67. {
  68. pattern: /y{1,4}/,
  69. regexp: "\\d{1,4}",
  70. fn: function(date, value) {
  71. date.setFullYear(value);
  72. }
  73. },
  74. {
  75. pattern: /MM/,
  76. regexp: "\\d{1,2}",
  77. fn: function(date, value) {
  78. date.setMonth(value - 1);
  79. }
  80. },
  81. {
  82. pattern: /dd/,
  83. regexp: "\\d{1,2}",
  84. fn: function(date, value) {
  85. date.setDate(value);
  86. }
  87. },
  88. {
  89. pattern: /hh/,
  90. regexp: "\\d{1,2}",
  91. fn: function(date, value) {
  92. date.setHours(value);
  93. }
  94. },
  95. {
  96. pattern: /mm/,
  97. regexp: "\\d\\d",
  98. fn: function(date, value) {
  99. date.setMinutes(value);
  100. }
  101. },
  102. {
  103. pattern: /ss/,
  104. regexp: "\\d\\d",
  105. fn: function(date, value) {
  106. date.setSeconds(value);
  107. }
  108. },
  109. {
  110. pattern: /SSS/,
  111. regexp: "\\d\\d\\d",
  112. fn: function(date, value) {
  113. date.setMilliseconds(value);
  114. }
  115. },
  116. {
  117. pattern: /O/,
  118. regexp: "[+-]\\d{3,4}|Z",
  119. fn: function(date, value) {
  120. if (value === "Z") {
  121. value = 0;
  122. }
  123. var offset = Math.abs(value);
  124. var minutes = (offset % 100) + Math.floor(offset / 100) * 60;
  125. date.setMinutes(date.getMinutes() + (value > 0 ? minutes : -minutes));
  126. }
  127. }
  128. ];
  129. var parsedPattern = matchers.reduce(
  130. function(p, m) {
  131. if (m.pattern.test(p.regexp)) {
  132. m.index = p.regexp.match(m.pattern).index;
  133. p.regexp = p.regexp.replace(m.pattern, "(" + m.regexp + ")");
  134. } else {
  135. m.index = -1;
  136. }
  137. return p;
  138. },
  139. { regexp: pattern, index: [] }
  140. );
  141. var dateFns = matchers.filter(function(m) {
  142. return m.index > -1;
  143. });
  144. dateFns.sort(function(a, b) {
  145. return a.index - b.index;
  146. });
  147. var matcher = new RegExp(parsedPattern.regexp);
  148. var matches = matcher.exec(str);
  149. if (matches) {
  150. var date = missingValuesDate || module.exports.now();
  151. dateFns.forEach(function(f, i) {
  152. f.fn(date, matches[i + 1]);
  153. });
  154. return date;
  155. }
  156. throw new Error(
  157. "String '" + str + "' could not be parsed as '" + pattern + "'"
  158. );
  159. }
  160. function parse(pattern, str, missingValuesDate) {
  161. if (!pattern) {
  162. throw new Error("pattern must be supplied");
  163. }
  164. return extractDateParts(pattern, str, missingValuesDate);
  165. }
  166. /**
  167. * Used for testing - replace this function with a fixed date.
  168. */
  169. function now() {
  170. return new Date();
  171. }
  172. module.exports = asString;
  173. module.exports.asString = asString;
  174. module.exports.parse = parse;
  175. module.exports.now = now;
  176. module.exports.ISO8601_FORMAT = "yyyy-MM-ddThh:mm:ss.SSS";
  177. module.exports.ISO8601_WITH_TZ_OFFSET_FORMAT = "yyyy-MM-ddThh:mm:ss.SSSO";
  178. module.exports.DATETIME_FORMAT = "dd MM yyyy hh:mm:ss.SSS";
  179. module.exports.ABSOLUTETIME_FORMAT = "hh:mm:ss.SSS";