From cbe6fbd10c0c55dd55c2b4d25f619bf2e05b438e Mon Sep 17 00:00:00 2001 From: Lee Adcock Date: Sun, 31 Jan 2016 04:08:57 +0000 Subject: [PATCH 1/8] fix(input[date]): support years with more than 4 digits Change to regular expressions used to match date formats that increases the number of digits in a year. Previously exactly four digits were required, change allows four or more. Also matches complete string, resolving ambiguity around extra characters at the beginning or end. --- src/ng/directive/input.js | 10 +-- test/ng/directive/inputSpec.js | 142 +++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 5 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 27098a168560..c4225c4bc5bf 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -10,7 +10,7 @@ */ // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 -var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/; +var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)$/ // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) // Note: We are being more lenient, because browsers are too. // 1. Scheme @@ -26,10 +26,10 @@ var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0- var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i; var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/; -var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/; -var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; -var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/; -var MONTH_REGEXP = /^(\d{4})-(\d\d)$/; +var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/; +var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; +var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/; +var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/; var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; var inputType = { diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 4b9727913737..4308f9313725 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -657,6 +657,18 @@ describe('input', function() { expect($rootScope.form.alias.$error.month).toBeTruthy(); }); + it('should allow to specify four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-03'); + expect(+$rootScope.value).toBe(Date.UTC(10123, 2, 1, 0, 0, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(10123, 2, 1, 0, 0 , 0)); + }); + expect(inputElm.val()).toBe('10123-03'); + }); + it('should only change the month of a bound date', function() { var inputElm = helper.compileInput(''); @@ -856,6 +868,18 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + it('should allow to specify four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-W03'); + console.log(new Date($rootScope.value).toUTCString()); + expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 21)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(10321, 0, 21)); + }); + expect(inputElm.val()).toBe('10321-W03'); + }); it('should use UTC if specified in the options', function() { var inputElm = helper.compileInput(''); @@ -1734,6 +1758,19 @@ describe('input', function() { } ); + it('should allow to specify four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-01-01'); + expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 1, 0, 0, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(10123, 0, 1, 0, 0, 0)); + }); + expect(inputElm.val()).toBe('10123-01-01'); + } + ); + it('should label parse errors as `date`', function() { var inputElm = helper.compileInput('', { @@ -1941,6 +1978,111 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + + describe('ISO_DATE_REGEXP', function() { + var dates = [ + // Validate date + ['00:00:00.0000+01:01', false], // date must be specified + ['2010.06.15T00:00:00.0000+01:01', false], // date must use dash seperator + ['x2010-06-15T00:00:00.0000+01:01', false], // invalid leading characters + + // Validate year + ['2010-06-15T00:00:00.0000+01:01', true], + ['20100-06-15T00:00:00.0000+01:01', true], + ['-06-15T00:00:00.0000+01:01', false], // year too few digits + ['2-06-15T00:00:00.0000+01:01', false], // year too few digits + ['20-06-15T00:00:00.0000+01:01', false], // year too few digits + ['201-06-15T00:00:00.0000+01:01', false], // year too few digits + + // Validate month + ['2010-01-15T00:00:00.0000+01:01', true], + ['2010--15T00:00:00.0000+01:01', false], // month too few digits + ['2010-0-15T00:00:00.0000+01:01', false], // month too few digits + ['2010-1-15T00:00:00.0000+01:01', false], // month too few digits + ['2010-111-15T00:00:00.0000+01:01', false], // month too many digits + ['2010-22-15T00:00:00.0000+01:01', false], // month value too large + + // Validate day + ['2010-01-01T00:00:00.0000+01:01', true], + ['2010-01-T00:00:00.0000+01:01', false], // day too few digits + ['2010-01-1T00:00:00.0000+01:01', false], // day too few digits + ['2010-01-41T00:00:00.0000+01:01', false], // day value too large + ['2010-01-200T00:00:00.0000+01:01', false], // day value too many digits + + // Validate time + ['2010-01-01', false], // time must be specified + ['2010-01-0101:00:00.0000+01:01', false], // missing date time seperator + ['2010-01-01V01:00:00.0000+01:01', false], // invalid date time seperator + ['2010-01-01T01-00-00.0000+01:01', false], // time must use period seperator + + // Validate hour + ['2010-01-01T01:00:00.0000+01:01', true], + ['2010-01-01T:00:00.0000+01:01', false], // hour value must be specified + ['2010-01-01T1:00:00.0000+01:01', false], // hour value too few digits + ['2010-01-01T-01:00:00.0000+01:01', false], // hour value must be positive + ['2010-01-01T32:00:00.0000+01:01', false], // hour value too large + ['2010-01-01T220:00:00.0000+01:01', false], // hour value too many digits + + // Validate minutes + ['2010-01-01T01:00:00.0000+01:01', true], + ['2010-01-01T01::00.0000+01:01', false], // minute value must be specified + ['2010-01-01T01:0:00.0000+01:01', false], // minute value too few digits + ['2010-01-01T01:-00:00.0000+01:01', false], // minute value must be positive + ['2010-01-01T01:60:00.0000+01:01', false], // minute value too large + ['2010-01-01T01:100:00.0000+01:01', false], // minute value too many digits + + // Validate seconds + ['2010-01-01T01:00:00.0000+01:01', true], + ['2010-01-01T01:00:.0000+01:01', false], // second value must be specified + ['2010-01-01T01:00:0.0000+01:01', false], // second value too few digits + ['2010-01-01T01:00:-00.0000+01:01', false], // second value must be positive + ['2010-01-01T01:00:60.0000+01:01', false], // second value too large + ['2010-01-01T01:00:100.0000+01:01', false], // second value too many digits + + // Validate milliseconds + ['2010-01-01T01:00:00+01:01', false], // millisecond value must be specified + ['2010-01-01T01:00:00.+01:01', false], // millisecond value must be specified + ['2010-01-01T01:00:00.-0000+01:01', false], // millisecond value must be positive + + // Validate timezone + ['20123-06-15T00:00:00.0000', false], // timezone must be specified + + // Validate timezone offset + ['2010-06-15T00:00:00.0000+01:01', true], + ['2010-06-15T00:00:00.0000-01:01', true], + ['2010-06-15T00:00:00.0000~01:01', false], // timezone must have valid postive/negative indicator + ['2010-06-15T00:00:00.000001:01', false], // timezone must have valid postive/negative indicator + ['2010-06-15T00:00:00.0000+00:01Z', false], // timezone invalid trailing characters + ['2010-06-15T00:00:00.0000+00:01 ', false], // timezone invalid trailing characters + + // Validate timezone hour offset + ['2010-06-15T00:00:00.0000+:01', false], // timezone hour offset must be specified + ['2010-06-15T00:00:00.0000+0:01', false], // timezone hour offset too few digits + ['2010-06-15T00:00:00.0000+31:01', false], // timezone hour offset value too large + ['2010-06-15T00:00:00.0000+211:01', false], // timezone hour offset too many digits + + // Validate timezone minute offset + ['2010-06-15T00:00:00.0000+00:-01', false], // timezone minute offset must be positive + ['2010-06-15T00:00:00.0000+00:', false], // timezone minute offset must be specified + ['2010-06-15T00:00:00.0000+00:0', false], // timezone minute offset too few digits + ['2010-06-15T00:00:00.0000+00:61', false], // timezone minute offset value too large + ['2010-06-15T00:00:00.0000+00:211', false], // timezone minute offset too many digits + + // Validate timezone UTC + ['20123-06-15T00:00:00.0000Z', true], + ['20123-06-15T00:00:00.0000K', false], // UTC timezone indicator is invalid + ['20123-06-15T00:00:00.0000ZZ', false], // UTC timezone indicator invalid trailing characters + ['20123-06-15T00:00:00.0000Z ', false] // UTC timezone indicator invalid trailing characters + ]; + + they('should validate date: $prop', dates, function(item) { + var date = item[0]; + var valid = item[1]; + + /* global ISO_DATE_REGEXP: false */ + expect(ISO_DATE_REGEXP.test(date)).toBe(valid); + }); + }); }); From 2ee3afc88f4b601f978079be5ae5e603906693c2 Mon Sep 17 00:00:00 2001 From: Lee Adcock Date: Sun, 31 Jan 2016 04:28:36 +0000 Subject: [PATCH 2/8] test(input[date]): improve test descriptions, removed erroneous console.log --- test/ng/directive/inputSpec.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 4308f9313725..7421fad350ef 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -657,7 +657,7 @@ describe('input', function() { expect($rootScope.form.alias.$error.month).toBeTruthy(); }); - it('should allow to specify four or more digits in year', function() { + it('should allow four or more digits in year', function() { var inputElm = helper.compileInput(''); helper.changeInputValueTo('10123-03'); @@ -868,11 +868,10 @@ describe('input', function() { expect(inputElm).toBeValid(); }); - it('should allow to specify four or more digits in year', function() { + it('should allow four or more digits in year', function() { var inputElm = helper.compileInput(''); helper.changeInputValueTo('10123-W03'); - console.log(new Date($rootScope.value).toUTCString()); expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 21)); $rootScope.$apply(function() { @@ -1758,7 +1757,7 @@ describe('input', function() { } ); - it('should allow to specify four or more digits in year', function() { + it('should allow four or more digits in year', function() { var inputElm = helper.compileInput(''); helper.changeInputValueTo('10123-01-01'); From 8574cc07624196db357cc4208cda6d579eb997bb Mon Sep 17 00:00:00 2001 From: Lee Adcock Date: Sun, 31 Jan 2016 04:41:54 +0000 Subject: [PATCH 3/8] fix(input[date]): resolving jshint finding --- src/ng/directive/input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index c4225c4bc5bf..b9d61168f74a 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -10,7 +10,7 @@ */ // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 -var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)$/ +var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)$/; // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) // Note: We are being more lenient, because browsers are too. // 1. Scheme From a3408ed9fceb1fb33c2527ad113e9f0eb1d2ff11 Mon Sep 17 00:00:00 2001 From: Lee Adcock Date: Sun, 31 Jan 2016 04:51:24 +0000 Subject: [PATCH 4/8] test(input[date]): removing unnecessary space, per jscs --- test/ng/directive/inputSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 7421fad350ef..40d2c06162b3 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -664,7 +664,7 @@ describe('input', function() { expect(+$rootScope.value).toBe(Date.UTC(10123, 2, 1, 0, 0, 0)); $rootScope.$apply(function() { - $rootScope.value = new Date(Date.UTC(10123, 2, 1, 0, 0 , 0)); + $rootScope.value = new Date(Date.UTC(10123, 2, 1, 0, 0, 0)); }); expect(inputElm.val()).toBe('10123-03'); }); From 4c1955c8e44323e24da733ecd2afa2fcc43282c9 Mon Sep 17 00:00:00 2001 From: Lee Adcock Date: Sun, 31 Jan 2016 14:27:18 +0000 Subject: [PATCH 5/8] fix(input[date]): iso date parsing, increase flexibility for timezone format Support for parsing additional variations of ISO dates, specifically the minute offset in timezones is now optional, as is the colon delimiter between the offset hour and minute. Enhancements to the tests were made to increase robustness and validate edge-case scenarios. --- src/ng/directive/input.js | 4 +- test/ng/directive/inputSpec.js | 141 +++++++++++++++++++-------------- 2 files changed, 82 insertions(+), 63 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index b9d61168f74a..82b962a72daf 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -9,8 +9,8 @@ ngModelMinErr: false, */ -// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 -var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)$/; +// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 +var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d(:?[0-5]\d)?|Z)$/; // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) // Note: We are being more lenient, because browsers are too. // 1. Scheme diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 40d2c06162b3..a168c4af53ff 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -664,9 +664,9 @@ describe('input', function() { expect(+$rootScope.value).toBe(Date.UTC(10123, 2, 1, 0, 0, 0)); $rootScope.$apply(function() { - $rootScope.value = new Date(Date.UTC(10123, 2, 1, 0, 0, 0)); + $rootScope.value = new Date(Date.UTC(20456, 3, 1, 0, 0, 0)); }); - expect(inputElm.val()).toBe('10123-03'); + expect(inputElm.val()).toBe('20456-04'); }); @@ -875,9 +875,9 @@ describe('input', function() { expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 21)); $rootScope.$apply(function() { - $rootScope.value = new Date(Date.UTC(10321, 0, 21)); + $rootScope.value = new Date(Date.UTC(20456, 0, 28)); }); - expect(inputElm.val()).toBe('10321-W03'); + expect(inputElm.val()).toBe('20456-W04'); }); it('should use UTC if specified in the options', function() { @@ -1164,6 +1164,18 @@ describe('input', function() { expect(+$rootScope.value).toBe(+new Date(2000, 0, 1, 1, 2, 0)); }); + it('should allow four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-01-01T01:02'); + expect(+$rootScope.value).toBe(+new Date(10123, 0, 1, 1, 2, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(20456, 1, 1, 1, 2, 0); + }); + expect(inputElm.val()).toBe('20456-02-01T01:02:00.000'); + } + ); it('should label parse errors as `datetimelocal`', function() { var inputElm = helper.compileInput('', { @@ -1764,9 +1776,9 @@ describe('input', function() { expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 1, 0, 0, 0)); $rootScope.$apply(function() { - $rootScope.value = new Date(Date.UTC(10123, 0, 1, 0, 0, 0)); + $rootScope.value = new Date(Date.UTC(20456, 1, 1, 0, 0, 0)); }); - expect(inputElm.val()).toBe('10123-01-01'); + expect(inputElm.val()).toBe('20456-02-01'); } ); @@ -1986,92 +1998,99 @@ describe('input', function() { ['x2010-06-15T00:00:00.0000+01:01', false], // invalid leading characters // Validate year - ['2010-06-15T00:00:00.0000+01:01', true], - ['20100-06-15T00:00:00.0000+01:01', true], - ['-06-15T00:00:00.0000+01:01', false], // year too few digits - ['2-06-15T00:00:00.0000+01:01', false], // year too few digits - ['20-06-15T00:00:00.0000+01:01', false], // year too few digits - ['201-06-15T00:00:00.0000+01:01', false], // year too few digits + ['2010-06-15T00:00:00.0000+01:01', true], // year has four or more digits + ['20100-06-15T00:00:00.0000+01:01', true], // year has four or more digits + ['-06-15T00:00:00.0000+01:01', false], // year has too few digits + ['2-06-15T00:00:00.0000+01:01', false], // year has too few digits + ['20-06-15T00:00:00.0000+01:01', false], // year has too few digits + ['201-06-15T00:00:00.0000+01:01', false], // year has too few digits // Validate month - ['2010-01-15T00:00:00.0000+01:01', true], - ['2010--15T00:00:00.0000+01:01', false], // month too few digits - ['2010-0-15T00:00:00.0000+01:01', false], // month too few digits - ['2010-1-15T00:00:00.0000+01:01', false], // month too few digits - ['2010-111-15T00:00:00.0000+01:01', false], // month too many digits - ['2010-22-15T00:00:00.0000+01:01', false], // month value too large + ['2010-01-15T00:00:00.0000+01:01', true], // month has two digits + ['2010--15T00:00:00.0000+01:01', false], // month has too few digits + ['2010-0-15T00:00:00.0000+01:01', false], // month has too few digits + ['2010-1-15T00:00:00.0000+01:01', false], // month has too few digits + ['2010-111-15T00:00:00.0000+01:01', false], // month has too many digits + ['2010-22-15T00:00:00.0000+01:01', false], // month is too large // Validate day - ['2010-01-01T00:00:00.0000+01:01', true], - ['2010-01-T00:00:00.0000+01:01', false], // day too few digits - ['2010-01-1T00:00:00.0000+01:01', false], // day too few digits - ['2010-01-41T00:00:00.0000+01:01', false], // day value too large - ['2010-01-200T00:00:00.0000+01:01', false], // day value too many digits + ['2010-01-01T00:00:00.0000+01:01', true], // day has two digits + ['2010-01-T00:00:00.0000+01:01', false], // day has too few digits + ['2010-01-1T00:00:00.0000+01:01', false], // day has too few digits + ['2010-01-200T00:00:00.0000+01:01', false], // day has too many digits + ['2010-01-41T00:00:00.0000+01:01', false], // day is too large // Validate time ['2010-01-01', false], // time must be specified ['2010-01-0101:00:00.0000+01:01', false], // missing date time seperator ['2010-01-01V01:00:00.0000+01:01', false], // invalid date time seperator - ['2010-01-01T01-00-00.0000+01:01', false], // time must use period seperator + ['2010-01-01T01-00-00.0000+01:01', false], // time must use colon seperator // Validate hour - ['2010-01-01T01:00:00.0000+01:01', true], - ['2010-01-01T:00:00.0000+01:01', false], // hour value must be specified - ['2010-01-01T1:00:00.0000+01:01', false], // hour value too few digits - ['2010-01-01T-01:00:00.0000+01:01', false], // hour value must be positive - ['2010-01-01T32:00:00.0000+01:01', false], // hour value too large - ['2010-01-01T220:00:00.0000+01:01', false], // hour value too many digits + ['2010-01-01T01:00:00.0000+01:01', true], // hour has two digits + ['2010-01-01T-01:00:00.0000+01:01', false], // hour must be positive + ['2010-01-01T:00:00.0000+01:01', false], // hour has too few digits + ['2010-01-01T1:00:00.0000+01:01', false], // hour has too few digits + ['2010-01-01T220:00:00.0000+01:01', false], // hour has too many digits + ['2010-01-01T32:00:00.0000+01:01', false], // hour is too large // Validate minutes - ['2010-01-01T01:00:00.0000+01:01', true], - ['2010-01-01T01::00.0000+01:01', false], // minute value must be specified - ['2010-01-01T01:0:00.0000+01:01', false], // minute value too few digits - ['2010-01-01T01:-00:00.0000+01:01', false], // minute value must be positive - ['2010-01-01T01:60:00.0000+01:01', false], // minute value too large - ['2010-01-01T01:100:00.0000+01:01', false], // minute value too many digits + ['2010-01-01T01:00:00.0000+01:01', true], // minute has two digits + ['2010-01-01T01:-00:00.0000+01:01', false], // minute must be positive + ['2010-01-01T01::00.0000+01:01', false], // minute has too few digits + ['2010-01-01T01:0:00.0000+01:01', false], // minute has too few digits + ['2010-01-01T01:100:00.0000+01:01', false], // minute has too many digits + ['2010-01-01T01:60:00.0000+01:01', false], // minute is too large // Validate seconds - ['2010-01-01T01:00:00.0000+01:01', true], - ['2010-01-01T01:00:.0000+01:01', false], // second value must be specified - ['2010-01-01T01:00:0.0000+01:01', false], // second value too few digits - ['2010-01-01T01:00:-00.0000+01:01', false], // second value must be positive - ['2010-01-01T01:00:60.0000+01:01', false], // second value too large - ['2010-01-01T01:00:100.0000+01:01', false], // second value too many digits + ['2010-01-01T01:00:00.0000+01:01', true], // second has two digits + ['2010-01-01T01:00:-00.0000+01:01', false], // second must be positive + ['2010-01-01T01:00:.0000+01:01', false], // second has too few digits + ['2010-01-01T01:00:0.0000+01:01', false], // second has too few digits + ['2010-01-01T01:00:100.0000+01:01', false], // second has too many digits + ['2010-01-01T01:00:60.0000+01:01', false], // second is too large // Validate milliseconds - ['2010-01-01T01:00:00+01:01', false], // millisecond value must be specified - ['2010-01-01T01:00:00.+01:01', false], // millisecond value must be specified - ['2010-01-01T01:00:00.-0000+01:01', false], // millisecond value must be positive + ['2010-01-01T01:00:00+01:01', false], // millisecond must be specified + ['2010-01-01T01:00:00.-0000+01:01', false], // millisecond must be positive + ['2010-01-01T01:00:00:0000+01:01', false], // millisecond must use period seperator + ['2010-01-01T01:00:00.+01:01', false], // millisecond has too few digits // Validate timezone - ['20123-06-15T00:00:00.0000', false], // timezone must be specified + ['2010-06-15T00:00:00.0000', false], // timezone must be specified // Validate timezone offset - ['2010-06-15T00:00:00.0000+01:01', true], - ['2010-06-15T00:00:00.0000-01:01', true], - ['2010-06-15T00:00:00.0000~01:01', false], // timezone must have valid postive/negative indicator - ['2010-06-15T00:00:00.000001:01', false], // timezone must have valid postive/negative indicator + ['2010-06-15T00:00:00.0000+01:01', true], // timezone offset can be positive hours and minutes + ['2010-06-15T00:00:00.0000-01:01', true], // timezone offset can be negative hours and minutes + ['2010-06-15T00:00:00.0000~01:01', false], // timezone has postive/negative indicator + ['2010-06-15T00:00:00.000001:01', false], // timezone has postive/negative indicator ['2010-06-15T00:00:00.0000+00:01Z', false], // timezone invalid trailing characters ['2010-06-15T00:00:00.0000+00:01 ', false], // timezone invalid trailing characters // Validate timezone hour offset - ['2010-06-15T00:00:00.0000+:01', false], // timezone hour offset must be specified - ['2010-06-15T00:00:00.0000+0:01', false], // timezone hour offset too few digits - ['2010-06-15T00:00:00.0000+31:01', false], // timezone hour offset value too large + ['2010-06-15T00:00:00.0000+:01', false], // timezone hour offset has too few digits + ['2010-06-15T00:00:00.0000+0:01', false], // timezone hour offset has too few digits ['2010-06-15T00:00:00.0000+211:01', false], // timezone hour offset too many digits + ['2010-06-15T00:00:00.0000+31:01', false], // timezone hour offset value too large // Validate timezone minute offset + ['2010-06-15T00:00:00.0000+00', true], // timezone minute offset is optional + ['2010-06-15T00:00:00.0000+0101', true], // timezone minute offset seperator is optional ['2010-06-15T00:00:00.0000+00:-01', false], // timezone minute offset must be positive - ['2010-06-15T00:00:00.0000+00:', false], // timezone minute offset must be specified - ['2010-06-15T00:00:00.0000+00:0', false], // timezone minute offset too few digits - ['2010-06-15T00:00:00.0000+00:61', false], // timezone minute offset value too large - ['2010-06-15T00:00:00.0000+00:211', false], // timezone minute offset too many digits + ['2010-06-15T00:00:00.0000+00.01', false], // timezone minute offset must use colon seperator + ['2010-06-15T00:00:00.0000+010', false], // timezone minute offset has too few digits + ['2010-06-15T00:00:00.0000+00:', false], // timezone minute offset has too few digits + ['2010-06-15T00:00:00.0000+00:0', false], // timezone minute offset has too few digits + ['2010-06-15T00:00:00.0000+00:211', false], // timezone minute offset has too many digits + ['2010-06-15T00:00:00.0000+01010', false], // timezone minute offset has too many digits + ['2010-06-15T00:00:00.0000+00:61', false], // timezone minute offset is too large // Validate timezone UTC - ['20123-06-15T00:00:00.0000Z', true], - ['20123-06-15T00:00:00.0000K', false], // UTC timezone indicator is invalid - ['20123-06-15T00:00:00.0000ZZ', false], // UTC timezone indicator invalid trailing characters - ['20123-06-15T00:00:00.0000Z ', false] // UTC timezone indicator invalid trailing characters + ['2010-06-15T00:00:00.0000Z', true], // UTC timezone can be indicated with Z + ['2010-06-15T00:00:00.0000K', false], // UTC timezone indicator is invalid + ['2010-06-15T00:00:00.0000 Z', false], // UTC timezone indicator has extra space + ['2010-06-15T00:00:00.0000ZZ', false], // UTC timezone indicator invalid trailing characters + ['2010-06-15T00:00:00.0000Z ', false] // UTC timezone indicator invalid trailing characters ]; they('should validate date: $prop', dates, function(item) { From 6a0bf4f2fa2f5c6e53eaddf7ba4b80be2c072815 Mon Sep 17 00:00:00 2001 From: Lee Adcock Date: Mon, 1 Feb 2016 23:58:31 +0000 Subject: [PATCH 6/8] perf(input[date]): iso date matching regex should be non-capturing --- src/ng/directive/input.js | 2 +- test/ng/directive/inputSpec.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 82b962a72daf..ba24711b11cc 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -10,7 +10,7 @@ */ // Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 -var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d(:?[0-5]\d)?|Z)$/; +var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d(?::?[0-5]\d)?|Z)$/; // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) // Note: We are being more lenient, because browsers are too. // 1. Scheme diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index a168c4af53ff..1cd77fda6e84 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -2100,6 +2100,13 @@ describe('input', function() { /* global ISO_DATE_REGEXP: false */ expect(ISO_DATE_REGEXP.test(date)).toBe(valid); }); + + it('should be non-capturing', function() { + // Non-capturing to increase performance, has no functional impact + var result = ISO_DATE_REGEXP.exec('2010-06-15T00:00:00.0000+01:01'); + expect(result[0]).toBe('2010-06-15T00:00:00.0000+01:01'); + expect(result.length).toBe(1); + }); }); }); From 335fc4827429305de1ceb43c8f60879e63d0639e Mon Sep 17 00:00:00 2001 From: Lee Adcock Date: Tue, 2 Feb 2016 21:48:23 +0000 Subject: [PATCH 7/8] fix(input[date]): iso date parsing, Edge timezone offset requires colon and minutes --- src/ng/directive/input.js | 2 +- test/ng/directive/inputSpec.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index ba24711b11cc..6b31f9998f21 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -10,7 +10,7 @@ */ // Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 -var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d(?::?[0-5]\d)?|Z)$/; +var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/; // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) // Note: We are being more lenient, because browsers are too. // 1. Scheme diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 1cd77fda6e84..38596e56f4cc 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -2074,11 +2074,11 @@ describe('input', function() { ['2010-06-15T00:00:00.0000+31:01', false], // timezone hour offset value too large // Validate timezone minute offset - ['2010-06-15T00:00:00.0000+00', true], // timezone minute offset is optional - ['2010-06-15T00:00:00.0000+0101', true], // timezone minute offset seperator is optional ['2010-06-15T00:00:00.0000+00:-01', false], // timezone minute offset must be positive ['2010-06-15T00:00:00.0000+00.01', false], // timezone minute offset must use colon seperator - ['2010-06-15T00:00:00.0000+010', false], // timezone minute offset has too few digits + ['2010-06-15T00:00:00.0000+0101', false], // timezone minute offset must use colon seperator + ['2010-06-15T00:00:00.0000+010', false], // timezone minute offset must use colon seperator + ['2010-06-15T00:00:00.0000+00', false], // timezone minute offset has too few digits ['2010-06-15T00:00:00.0000+00:', false], // timezone minute offset has too few digits ['2010-06-15T00:00:00.0000+00:0', false], // timezone minute offset has too few digits ['2010-06-15T00:00:00.0000+00:211', false], // timezone minute offset has too many digits From a4acfb544ca02aa5fe4a7ddd2079aefff11f4121 Mon Sep 17 00:00:00 2001 From: Lee Adcock Date: Wed, 10 Feb 2016 02:33:06 +0000 Subject: [PATCH 8/8] test(input[date]): removing test of non-functional regex implementation --- test/ng/directive/inputSpec.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 38596e56f4cc..93efd7d30cfa 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -2100,13 +2100,6 @@ describe('input', function() { /* global ISO_DATE_REGEXP: false */ expect(ISO_DATE_REGEXP.test(date)).toBe(valid); }); - - it('should be non-capturing', function() { - // Non-capturing to increase performance, has no functional impact - var result = ISO_DATE_REGEXP.exec('2010-06-15T00:00:00.0000+01:01'); - expect(result[0]).toBe('2010-06-15T00:00:00.0000+01:01'); - expect(result.length).toBe(1); - }); }); });