angular.module('ui.bootstrap.dateparser', []) .service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) { this.parsers = {}; var formatCodeToRegex = { 'yyyy': { regex: '\\d{4}', apply: function(value) { this.year = +value; } }, 'yy': { regex: '\\d{2}', apply: function(value) { this.year = +value + 2000; } }, 'y': { regex: '\\d{1,4}', apply: function(value) { this.year = +value; } }, 'MMMM': { regex: $locale.DATETIME_FORMATS.MONTH.join('|'), apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); } }, 'MMM': { regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); } }, 'MM': { regex: '0[1-9]|1[0-2]', apply: function(value) { this.month = value - 1; } }, 'M': { regex: '[1-9]|1[0-2]', apply: function(value) { this.month = value - 1; } }, 'dd': { regex: '[0-2][0-9]{1}|3[0-1]{1}', apply: function(value) { this.date = +value; } }, 'd': { regex: '[1-2]?[0-9]{1}|3[0-1]{1}', apply: function(value) { this.date = +value; } }, 'EEEE': { regex: $locale.DATETIME_FORMATS.DAY.join('|') }, 'EEE': { regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|') } }; this.createParser = function(format) { var map = [], regex = format.split(''); angular.forEach(formatCodeToRegex, function(data, code) { var index = format.indexOf(code); if (index > -1) { format = format.split(''); regex[index] = '(' + data.regex + ')'; format[index] = '$'; // Custom symbol to define consumed part of format for (var i = index + 1, n = index + code.length; i < n; i++) { regex[i] = ''; format[i] = '$'; } format = format.join(''); map.push({ index: index, apply: data.apply }); } }); return { regex: new RegExp('^' + regex.join('') + '$'), map: orderByFilter(map, 'index') }; }; this.parse = function(input, format) { if ( !angular.isString(input) ) { return input; } format = $locale.DATETIME_FORMATS[format] || format; if ( !this.parsers[format] ) { this.parsers[format] = this.createParser(format); } var parser = this.parsers[format], regex = parser.regex, map = parser.map, results = input.match(regex); if ( results && results.length ) { var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt; for( var i = 1, n = results.length; i < n; i++ ) { var mapper = map[i-1]; if ( mapper.apply ) { mapper.apply.call(fields, results[i]); } } if ( isValid(fields.year, fields.month, fields.date) ) { dt = new Date( fields.year, fields.month, fields.date, fields.hours); } return dt; } }; // Check if date is valid for specific month (and year for February). // Month: 0 = Jan, 1 = Feb, etc function isValid(year, month, date) { if ( month === 1 && date > 28) { return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); } if ( month === 3 || month === 5 || month === 8 || month === 10) { return date < 31; } return true; } }]);