dateparser.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. angular.module('ui.bootstrap.dateparser', [])
  2. .service('dateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
  3. // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
  4. var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
  5. var localeId;
  6. var formatCodeToRegex;
  7. this.init = function() {
  8. localeId = $locale.id;
  9. this.parsers = {};
  10. formatCodeToRegex = {
  11. 'yyyy': {
  12. regex: '\\d{4}',
  13. apply: function(value) { this.year = +value; }
  14. },
  15. 'yy': {
  16. regex: '\\d{2}',
  17. apply: function(value) { this.year = +value + 2000; }
  18. },
  19. 'y': {
  20. regex: '\\d{1,4}',
  21. apply: function(value) { this.year = +value; }
  22. },
  23. 'MMMM': {
  24. regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
  25. apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
  26. },
  27. 'MMM': {
  28. regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
  29. apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
  30. },
  31. 'MM': {
  32. regex: '0[1-9]|1[0-2]',
  33. apply: function(value) { this.month = value - 1; }
  34. },
  35. 'M': {
  36. regex: '[1-9]|1[0-2]',
  37. apply: function(value) { this.month = value - 1; }
  38. },
  39. 'dd': {
  40. regex: '[0-2][0-9]{1}|3[0-1]{1}',
  41. apply: function(value) { this.date = +value; }
  42. },
  43. 'd': {
  44. regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
  45. apply: function(value) { this.date = +value; }
  46. },
  47. 'EEEE': {
  48. regex: $locale.DATETIME_FORMATS.DAY.join('|')
  49. },
  50. 'EEE': {
  51. regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
  52. },
  53. 'HH': {
  54. regex: '(?:0|1)[0-9]|2[0-3]',
  55. apply: function(value) { this.hours = +value; }
  56. },
  57. 'hh': {
  58. regex: '0[0-9]|1[0-2]',
  59. apply: function(value) { this.hours = +value; }
  60. },
  61. 'H': {
  62. regex: '1?[0-9]|2[0-3]',
  63. apply: function(value) { this.hours = +value; }
  64. },
  65. 'h': {
  66. regex: '[0-9]|1[0-2]',
  67. apply: function(value) { this.hours = +value; }
  68. },
  69. 'mm': {
  70. regex: '[0-5][0-9]',
  71. apply: function(value) { this.minutes = +value; }
  72. },
  73. 'm': {
  74. regex: '[0-9]|[1-5][0-9]',
  75. apply: function(value) { this.minutes = +value; }
  76. },
  77. 'sss': {
  78. regex: '[0-9][0-9][0-9]',
  79. apply: function(value) { this.milliseconds = +value; }
  80. },
  81. 'ss': {
  82. regex: '[0-5][0-9]',
  83. apply: function(value) { this.seconds = +value; }
  84. },
  85. 's': {
  86. regex: '[0-9]|[1-5][0-9]',
  87. apply: function(value) { this.seconds = +value; }
  88. },
  89. 'a': {
  90. regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
  91. apply: function(value) {
  92. if (this.hours === 12) {
  93. this.hours = 0;
  94. }
  95. if (value === 'PM') {
  96. this.hours += 12;
  97. }
  98. }
  99. }
  100. };
  101. };
  102. this.init();
  103. function createParser(format) {
  104. var map = [], regex = format.split('');
  105. angular.forEach(formatCodeToRegex, function(data, code) {
  106. var index = format.indexOf(code);
  107. if (index > -1) {
  108. format = format.split('');
  109. regex[index] = '(' + data.regex + ')';
  110. format[index] = '$'; // Custom symbol to define consumed part of format
  111. for (var i = index + 1, n = index + code.length; i < n; i++) {
  112. regex[i] = '';
  113. format[i] = '$';
  114. }
  115. format = format.join('');
  116. map.push({ index: index, apply: data.apply });
  117. }
  118. });
  119. return {
  120. regex: new RegExp('^' + regex.join('') + '$'),
  121. map: orderByFilter(map, 'index')
  122. };
  123. }
  124. this.parse = function(input, format, baseDate) {
  125. if (!angular.isString(input) || !format) {
  126. return input;
  127. }
  128. format = $locale.DATETIME_FORMATS[format] || format;
  129. format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
  130. if ($locale.id !== localeId) {
  131. this.init();
  132. }
  133. if (!this.parsers[format]) {
  134. this.parsers[format] = createParser(format);
  135. }
  136. var parser = this.parsers[format],
  137. regex = parser.regex,
  138. map = parser.map,
  139. results = input.match(regex);
  140. if (results && results.length) {
  141. var fields, dt;
  142. if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
  143. fields = {
  144. year: baseDate.getFullYear(),
  145. month: baseDate.getMonth(),
  146. date: baseDate.getDate(),
  147. hours: baseDate.getHours(),
  148. minutes: baseDate.getMinutes(),
  149. seconds: baseDate.getSeconds(),
  150. milliseconds: baseDate.getMilliseconds()
  151. };
  152. } else {
  153. if (baseDate) {
  154. $log.warn('dateparser:', 'baseDate is not a valid date');
  155. }
  156. fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
  157. }
  158. for (var i = 1, n = results.length; i < n; i++) {
  159. var mapper = map[i-1];
  160. if (mapper.apply) {
  161. mapper.apply.call(fields, results[i]);
  162. }
  163. }
  164. if (isValid(fields.year, fields.month, fields.date)) {
  165. dt = new Date(fields.year, fields.month, fields.date,
  166. fields.hours, fields.minutes, fields.seconds,
  167. fields.milliseconds || 0);
  168. }
  169. return dt;
  170. }
  171. };
  172. // Check if date is valid for specific month (and year for February).
  173. // Month: 0 = Jan, 1 = Feb, etc
  174. function isValid(year, month, date) {
  175. if (date < 1) {
  176. return false;
  177. }
  178. if (month === 1 && date > 28) {
  179. return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
  180. }
  181. if (month === 3 || month === 5 || month === 8 || month === 10) {
  182. return date < 31;
  183. }
  184. return true;
  185. }
  186. }]);