From ee76d2f5f2247bb37a7bd6ae4b8b0d448ad5da62 Mon Sep 17 00:00:00 2001 From: Tobias Gunkel Date: Wed, 16 Sep 2015 22:18:48 +0200 Subject: [PATCH 1/4] add support for value literals --- src/dateparser/dateparser.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/dateparser/dateparser.js b/src/dateparser/dateparser.js index 38774c3d0b..89429fcead 100644 --- a/src/dateparser/dateparser.js +++ b/src/dateparser/dateparser.js @@ -120,6 +120,25 @@ angular.module('ui.bootstrap.dateparser', []) } }); + // check for literal values + for (var i = 0; i < regex.length; i++) { + var literal = false; + if (regex[i] === '\'') { + if (!literal) { + regex[i] = ''; + literal = true; + } else { + if ((i+1 < regex.length) && (regex[i+1] !== '\'')) { + regex[i] = '\''; + regex[i+1] = ''; + } else { + regex[i] = ''; + literal = false; + } + } + } + } + return { regex: new RegExp('^' + regex.join('') + '$'), map: orderByFilter(map, 'index') From 3dd1524139b54b748288c6fa02aec9bb8f885cd0 Mon Sep 17 00:00:00 2001 From: Tobias Gunkel Date: Fri, 18 Sep 2015 20:29:17 +0200 Subject: [PATCH 2/4] test(dateparser): add unit-tests for value literals --- src/dateparser/test/dateparser.spec.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/dateparser/test/dateparser.spec.js b/src/dateparser/test/dateparser.spec.js index 0762866ed1..4de4eecdd6 100644 --- a/src/dateparser/test/dateparser.spec.js +++ b/src/dateparser/test/dateparser.spec.js @@ -176,6 +176,24 @@ describe('date parser', function() { }); }); + describe('with value literals', function() { + it('should work with multiple literals', function () { + expect(dateParser.parse('29 de January de 2013', 'd \'de\' MMMM \'de\' y')).toEqual(new Date(2013, 0, 29)); + }); + it('should work with escaped single quote', function () { + expect(dateParser.parse('22.March.15 12 o\'clock', 'd.MMMM.yy h \'o\'\'clock\'')).toEqual(new Date(2015, 2, 22, 12)); + }); + it('should work with only a single quote', function () { + expect(dateParser.parse('22.March.15 \'', 'd.MMMM.yy \'\'\'')).toEqual(new Date(2015, 2, 22)); + }); + it('should work with trailing literal', function () { + expect(dateParser.parse('year 2013', '\'year\' y')).toEqual(new Date(2013, 0, 1)); + }); + it('should work without whitespace', function () { + expect(dateParser.parse('year:2013', '\'year:\'y')).toEqual(new Date(2013, 0, 1)); + }); + }); + describe('with edge case', function() { it('should not work for invalid number of days in February', function() { expect(dateParser.parse('29.02.2013', 'dd.MM.yyyy')).toBeUndefined(); From a3bcc7f656887b5f3ae69c33ad2e8418e87b861e Mon Sep 17 00:00:00 2001 From: Tobias Gunkel Date: Fri, 18 Sep 2015 20:35:20 +0200 Subject: [PATCH 3/4] fix(dateparser): fix handling of escaped single-quotes and placeholders in literals - escaped single-quotes ('o''clock') are now replaced correctly - literals containing format-placeholders (e.g. 'd', 'y') are now handled correctly --- src/dateparser/dateparser.js | 225 ++++++++++++++++++----------------- 1 file changed, 117 insertions(+), 108 deletions(-) diff --git a/src/dateparser/dateparser.js b/src/dateparser/dateparser.js index 89429fcead..a04ad4d8d7 100644 --- a/src/dateparser/dateparser.js +++ b/src/dateparser/dateparser.js @@ -4,104 +4,132 @@ angular.module('ui.bootstrap.dateparser', []) // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; - this.parsers = {}; + 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('|') - }, - 'HH': { - regex: '(?:0|1)[0-9]|2[0-3]', - apply: function(value) { this.hours = +value; } - }, - 'hh': { - regex: '0[0-9]|1[0-2]', - apply: function(value) { this.hours = +value; } - }, - 'H': { - regex: '1?[0-9]|2[0-3]', - apply: function(value) { this.hours = +value; } - }, - 'h': { - regex: '[0-9]|1[0-2]', - apply: function(value) { this.hours = +value; } - }, - 'mm': { - regex: '[0-5][0-9]', - apply: function(value) { this.minutes = +value; } - }, - 'm': { - regex: '[0-9]|[1-5][0-9]', - apply: function(value) { this.minutes = +value; } - }, - 'sss': { - regex: '[0-9][0-9][0-9]', - apply: function(value) { this.milliseconds = +value; } - }, - 'ss': { - regex: '[0-5][0-9]', - apply: function(value) { this.seconds = +value; } - }, - 's': { - regex: '[0-9]|[1-5][0-9]', - apply: function(value) { this.seconds = +value; } - }, - 'a': { - regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), - apply: function(value) { - if (this.hours === 12) { - this.hours = 0; - } + '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('|') + }, + 'HH': { + regex: '(?:0|1)[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; } + }, + 'hh': { + regex: '0[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; } + }, + 'H': { + regex: '1?[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; } + }, + 'h': { + regex: '[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; } + }, + 'mm': { + regex: '[0-5][0-9]', + apply: function(value) { this.minutes = +value; } + }, + 'm': { + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.minutes = +value; } + }, + 'sss': { + regex: '[0-9][0-9][0-9]', + apply: function(value) { this.milliseconds = +value; } + }, + 'ss': { + regex: '[0-5][0-9]', + apply: function(value) { this.seconds = +value; } + }, + 's': { + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.seconds = +value; } + }, + 'a': { + regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), + apply: function(value) { + if (this.hours === 12) { + this.hours = 0; + } - if (value === 'PM') { - this.hours += 12; + if (value === 'PM') { + this.hours += 12; + } } } - } - }; + }; function createParser(format) { var map = [], regex = format.split(''); + // check for literal values + var quoteIndex = format.indexOf('\''); + if (quoteIndex > -1) { + var inLiteral = false; + format = format.split(''); + for (var i = quoteIndex; i < format.length; i++) { + if (inLiteral) { + if (format[i] === '\'') { + if ((i+1 < format.length) && (format[i+1] === '\'')) { // escaped single quote + format[i+1] = '$'; + regex[i+1] = ''; + } else { // end of literal + regex[i] = ''; + inLiteral = false; + } + } + format[i] = '$'; + } else { + if (format[i] === '\'') { // start of literal + format[i] = '$'; + regex[i] = ''; + inLiteral = true; + } + } + } + format = format.join(''); + } + angular.forEach(formatCodeToRegex, function(data, code) { var index = format.indexOf(code); @@ -120,25 +148,6 @@ angular.module('ui.bootstrap.dateparser', []) } }); - // check for literal values - for (var i = 0; i < regex.length; i++) { - var literal = false; - if (regex[i] === '\'') { - if (!literal) { - regex[i] = ''; - literal = true; - } else { - if ((i+1 < regex.length) && (regex[i+1] !== '\'')) { - regex[i] = '\''; - regex[i+1] = ''; - } else { - regex[i] = ''; - literal = false; - } - } - } - } - return { regex: new RegExp('^' + regex.join('') + '$'), map: orderByFilter(map, 'index') From 8d2ca3ba17c5c4d3001c99ceccc1e7b32ebcbf11 Mon Sep 17 00:00:00 2001 From: Tobias Gunkel Date: Fri, 18 Sep 2015 20:52:40 +0200 Subject: [PATCH 4/4] chore(dateparser): revert indentation changes --- src/dateparser/dateparser.js | 178 +++++++++++++++++------------------ 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/src/dateparser/dateparser.js b/src/dateparser/dateparser.js index a04ad4d8d7..99dd452305 100644 --- a/src/dateparser/dateparser.js +++ b/src/dateparser/dateparser.js @@ -4,100 +4,100 @@ angular.module('ui.bootstrap.dateparser', []) // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; - this.parsers = {}; + 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('|') - }, - 'HH': { - regex: '(?:0|1)[0-9]|2[0-3]', - apply: function(value) { this.hours = +value; } - }, - 'hh': { - regex: '0[0-9]|1[0-2]', - apply: function(value) { this.hours = +value; } - }, - 'H': { - regex: '1?[0-9]|2[0-3]', - apply: function(value) { this.hours = +value; } - }, - 'h': { - regex: '[0-9]|1[0-2]', - apply: function(value) { this.hours = +value; } - }, - 'mm': { - regex: '[0-5][0-9]', - apply: function(value) { this.minutes = +value; } - }, - 'm': { - regex: '[0-9]|[1-5][0-9]', - apply: function(value) { this.minutes = +value; } - }, - 'sss': { - regex: '[0-9][0-9][0-9]', - apply: function(value) { this.milliseconds = +value; } - }, - 'ss': { - regex: '[0-5][0-9]', - apply: function(value) { this.seconds = +value; } - }, - 's': { - regex: '[0-9]|[1-5][0-9]', - apply: function(value) { this.seconds = +value; } - }, - 'a': { - regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), - apply: function(value) { - if (this.hours === 12) { - this.hours = 0; - } + '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('|') + }, + 'HH': { + regex: '(?:0|1)[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; } + }, + 'hh': { + regex: '0[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; } + }, + 'H': { + regex: '1?[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; } + }, + 'h': { + regex: '[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; } + }, + 'mm': { + regex: '[0-5][0-9]', + apply: function(value) { this.minutes = +value; } + }, + 'm': { + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.minutes = +value; } + }, + 'sss': { + regex: '[0-9][0-9][0-9]', + apply: function(value) { this.milliseconds = +value; } + }, + 'ss': { + regex: '[0-5][0-9]', + apply: function(value) { this.seconds = +value; } + }, + 's': { + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.seconds = +value; } + }, + 'a': { + regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), + apply: function(value) { + if (this.hours === 12) { + this.hours = 0; + } - if (value === 'PM') { - this.hours += 12; - } + if (value === 'PM') { + this.hours += 12; } } - }; + } + }; function createParser(format) { var map = [], regex = format.split('');