date-parser.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /**
  2. * angular-strap
  3. * @version v2.3.9 - 2016-06-10
  4. * @link http://mgcrea.github.io/angular-strap
  5. * @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
  6. * @license MIT License, http://www.opensource.org/licenses/MIT
  7. */
  8. 'use strict';
  9. angular.module('mgcrea.ngStrap.helpers.dateParser', []).provider('$dateParser', [ '$localeProvider', function($localeProvider) {
  10. function ParseDate() {
  11. this.year = 1970;
  12. this.month = 0;
  13. this.day = 1;
  14. this.hours = 0;
  15. this.minutes = 0;
  16. this.seconds = 0;
  17. this.milliseconds = 0;
  18. }
  19. ParseDate.prototype.setMilliseconds = function(value) {
  20. this.milliseconds = value;
  21. };
  22. ParseDate.prototype.setSeconds = function(value) {
  23. this.seconds = value;
  24. };
  25. ParseDate.prototype.setMinutes = function(value) {
  26. this.minutes = value;
  27. };
  28. ParseDate.prototype.setHours = function(value) {
  29. this.hours = value;
  30. };
  31. ParseDate.prototype.getHours = function() {
  32. return this.hours;
  33. };
  34. ParseDate.prototype.setDate = function(value) {
  35. this.day = value;
  36. };
  37. ParseDate.prototype.setMonth = function(value) {
  38. this.month = value;
  39. };
  40. ParseDate.prototype.setFullYear = function(value) {
  41. this.year = value;
  42. };
  43. ParseDate.prototype.fromDate = function(value) {
  44. this.year = value.getFullYear();
  45. this.month = value.getMonth();
  46. this.day = value.getDate();
  47. this.hours = value.getHours();
  48. this.minutes = value.getMinutes();
  49. this.seconds = value.getSeconds();
  50. this.milliseconds = value.getMilliseconds();
  51. return this;
  52. };
  53. ParseDate.prototype.toDate = function() {
  54. return new Date(this.year, this.month, this.day, this.hours, this.minutes, this.seconds, this.milliseconds);
  55. };
  56. var proto = ParseDate.prototype;
  57. function noop() {}
  58. function isNumeric(n) {
  59. return !isNaN(parseFloat(n)) && isFinite(n);
  60. }
  61. function indexOfCaseInsensitive(array, value) {
  62. var len = array.length;
  63. var str = value.toString().toLowerCase();
  64. for (var i = 0; i < len; i++) {
  65. if (array[i].toLowerCase() === str) {
  66. return i;
  67. }
  68. }
  69. return -1;
  70. }
  71. var defaults = this.defaults = {
  72. format: 'shortDate',
  73. strict: false
  74. };
  75. this.$get = [ '$locale', 'dateFilter', function($locale, dateFilter) {
  76. var DateParserFactory = function(config) {
  77. var options = angular.extend({}, defaults, config);
  78. var $dateParser = {};
  79. var regExpMap = {
  80. sss: '[0-9]{3}',
  81. ss: '[0-5][0-9]',
  82. s: options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
  83. mm: '[0-5][0-9]',
  84. m: options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
  85. HH: '[01][0-9]|2[0-3]',
  86. H: options.strict ? '1?[0-9]|2[0-3]' : '[01]?[0-9]|2[0-3]',
  87. hh: '[0][1-9]|[1][012]',
  88. h: options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
  89. a: 'AM|PM',
  90. EEEE: $locale.DATETIME_FORMATS.DAY.join('|'),
  91. EEE: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
  92. dd: '0[1-9]|[12][0-9]|3[01]',
  93. d: options.strict ? '[1-9]|[1-2][0-9]|3[01]' : '0?[1-9]|[1-2][0-9]|3[01]',
  94. MMMM: $locale.DATETIME_FORMATS.MONTH.join('|'),
  95. MMM: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
  96. MM: '0[1-9]|1[012]',
  97. M: options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
  98. yyyy: '[1]{1}[0-9]{3}|[2]{1}[0-9]{3}',
  99. yy: '[0-9]{2}',
  100. y: options.strict ? '-?(0|[1-9][0-9]{0,3})' : '-?0*[0-9]{1,4}'
  101. };
  102. var setFnMap = {
  103. sss: proto.setMilliseconds,
  104. ss: proto.setSeconds,
  105. s: proto.setSeconds,
  106. mm: proto.setMinutes,
  107. m: proto.setMinutes,
  108. HH: proto.setHours,
  109. H: proto.setHours,
  110. hh: proto.setHours,
  111. h: proto.setHours,
  112. EEEE: noop,
  113. EEE: noop,
  114. dd: proto.setDate,
  115. d: proto.setDate,
  116. a: function(value) {
  117. var hours = this.getHours() % 12;
  118. return this.setHours(value.match(/pm/i) ? hours + 12 : hours);
  119. },
  120. MMMM: function(value) {
  121. return this.setMonth(indexOfCaseInsensitive($locale.DATETIME_FORMATS.MONTH, value));
  122. },
  123. MMM: function(value) {
  124. return this.setMonth(indexOfCaseInsensitive($locale.DATETIME_FORMATS.SHORTMONTH, value));
  125. },
  126. MM: function(value) {
  127. return this.setMonth(1 * value - 1);
  128. },
  129. M: function(value) {
  130. return this.setMonth(1 * value - 1);
  131. },
  132. yyyy: proto.setFullYear,
  133. yy: function(value) {
  134. return this.setFullYear(2e3 + 1 * value);
  135. },
  136. y: function(value) {
  137. return 1 * value <= 50 && value.length === 2 ? this.setFullYear(2e3 + 1 * value) : this.setFullYear(1 * value);
  138. }
  139. };
  140. var regex;
  141. var setMap;
  142. $dateParser.init = function() {
  143. $dateParser.$format = $locale.DATETIME_FORMATS[options.format] || options.format;
  144. regex = regExpForFormat($dateParser.$format);
  145. setMap = setMapForFormat($dateParser.$format);
  146. };
  147. $dateParser.isValid = function(date) {
  148. if (angular.isDate(date)) return !isNaN(date.getTime());
  149. return regex.test(date);
  150. };
  151. $dateParser.parse = function(value, baseDate, format, timezone) {
  152. if (format) format = $locale.DATETIME_FORMATS[format] || format;
  153. if (angular.isDate(value)) value = dateFilter(value, format || $dateParser.$format, timezone);
  154. var formatRegex = format ? regExpForFormat(format) : regex;
  155. var formatSetMap = format ? setMapForFormat(format) : setMap;
  156. var matches = formatRegex.exec(value);
  157. if (!matches) return false;
  158. var date = baseDate && !isNaN(baseDate.getTime()) ? new ParseDate().fromDate(baseDate) : new ParseDate().fromDate(new Date(1970, 0, 1, 0));
  159. for (var i = 0; i < matches.length - 1; i++) {
  160. if (formatSetMap[i]) formatSetMap[i].call(date, matches[i + 1]);
  161. }
  162. var newDate = date.toDate();
  163. if (parseInt(date.day, 10) !== newDate.getDate()) {
  164. return false;
  165. }
  166. return newDate;
  167. };
  168. $dateParser.getDateForAttribute = function(key, value) {
  169. var date;
  170. if (value === 'today') {
  171. var today = new Date();
  172. date = new Date(today.getFullYear(), today.getMonth(), today.getDate() + (key === 'maxDate' ? 1 : 0), 0, 0, 0, key === 'minDate' ? 0 : -1);
  173. } else if (angular.isString(value) && value.match(/^".+"$/)) {
  174. date = new Date(value.substr(1, value.length - 2));
  175. } else if (isNumeric(value)) {
  176. date = new Date(parseInt(value, 10));
  177. } else if (angular.isString(value) && value.length === 0) {
  178. date = key === 'minDate' ? -Infinity : +Infinity;
  179. } else {
  180. date = new Date(value);
  181. }
  182. return date;
  183. };
  184. $dateParser.getTimeForAttribute = function(key, value) {
  185. var time;
  186. if (value === 'now') {
  187. time = new Date().setFullYear(1970, 0, 1);
  188. } else if (angular.isString(value) && value.match(/^".+"$/)) {
  189. time = new Date(value.substr(1, value.length - 2)).setFullYear(1970, 0, 1);
  190. } else if (isNumeric(value)) {
  191. time = new Date(parseInt(value, 10)).setFullYear(1970, 0, 1);
  192. } else if (angular.isString(value) && value.length === 0) {
  193. time = key === 'minTime' ? -Infinity : +Infinity;
  194. } else {
  195. time = $dateParser.parse(value, new Date(1970, 0, 1, 0));
  196. }
  197. return time;
  198. };
  199. $dateParser.daylightSavingAdjust = function(date) {
  200. if (!date) {
  201. return null;
  202. }
  203. date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
  204. return date;
  205. };
  206. $dateParser.timezoneOffsetAdjust = function(date, timezone, undo) {
  207. if (!date) {
  208. return null;
  209. }
  210. if (timezone && timezone === 'UTC') {
  211. date = new Date(date.getTime());
  212. date.setMinutes(date.getMinutes() + (undo ? -1 : 1) * date.getTimezoneOffset());
  213. }
  214. return date;
  215. };
  216. function regExpForFormat(format) {
  217. var re = buildDateAbstractRegex(format);
  218. return buildDateParseRegex(re);
  219. }
  220. function buildDateAbstractRegex(format) {
  221. var escapedFormat = escapeReservedSymbols(format);
  222. var escapedLiteralFormat = escapedFormat.replace(/''/g, '\\\'');
  223. var literalRegex = /('(?:\\'|.)*?')/;
  224. var formatParts = escapedLiteralFormat.split(literalRegex);
  225. var dateElements = Object.keys(regExpMap);
  226. var dateRegexParts = [];
  227. angular.forEach(formatParts, function(part) {
  228. if (isFormatStringLiteral(part)) {
  229. part = trimLiteralEscapeChars(part);
  230. } else {
  231. for (var i = 0; i < dateElements.length; i++) {
  232. part = part.split(dateElements[i]).join('${' + i + '}');
  233. }
  234. }
  235. dateRegexParts.push(part);
  236. });
  237. return dateRegexParts.join('');
  238. }
  239. function escapeReservedSymbols(text) {
  240. return text.replace(/\\/g, '[\\\\]').replace(/-/g, '[-]').replace(/\./g, '[.]').replace(/\*/g, '[*]').replace(/\+/g, '[+]').replace(/\?/g, '[?]').replace(/\$/g, '[$]').replace(/\^/g, '[^]').replace(/\//g, '[/]').replace(/\\s/g, '[\\s]');
  241. }
  242. function isFormatStringLiteral(text) {
  243. return /^'.*'$/.test(text);
  244. }
  245. function trimLiteralEscapeChars(text) {
  246. return text.replace(/^'(.*)'$/, '$1');
  247. }
  248. function buildDateParseRegex(abstractRegex) {
  249. var dateElements = Object.keys(regExpMap);
  250. var re = abstractRegex;
  251. for (var i = 0; i < dateElements.length; i++) {
  252. re = re.split('${' + i + '}').join('(' + regExpMap[dateElements[i]] + ')');
  253. }
  254. return new RegExp('^' + re + '$', [ 'i' ]);
  255. }
  256. function setMapForFormat(format) {
  257. var re = buildDateAbstractRegex(format);
  258. return buildDateParseValuesMap(re);
  259. }
  260. function buildDateParseValuesMap(abstractRegex) {
  261. var dateElements = Object.keys(regExpMap);
  262. var valuesRegex = new RegExp('\\${(\\d+)}', 'g');
  263. var valuesMatch;
  264. var keyIndex;
  265. var valueKey;
  266. var valueFunction;
  267. var valuesFunctionMap = [];
  268. while ((valuesMatch = valuesRegex.exec(abstractRegex)) !== null) {
  269. keyIndex = valuesMatch[1];
  270. valueKey = dateElements[keyIndex];
  271. valueFunction = setFnMap[valueKey];
  272. valuesFunctionMap.push(valueFunction);
  273. }
  274. return valuesFunctionMap;
  275. }
  276. $dateParser.init();
  277. return $dateParser;
  278. };
  279. return DateParserFactory;
  280. } ];
  281. } ]);