diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 27098a168560..6b31f9998f21 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 @@ -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..93efd7d30cfa 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 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(20456, 3, 1, 0, 0, 0)); + }); + expect(inputElm.val()).toBe('20456-04'); + }); + it('should only change the month of a bound date', function() { var inputElm = helper.compileInput(''); @@ -856,6 +868,17 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + it('should allow four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-W03'); + expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 21)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(20456, 0, 28)); + }); + expect(inputElm.val()).toBe('20456-W04'); + }); it('should use UTC if specified in the options', function() { var inputElm = helper.compileInput(''); @@ -1141,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('', { @@ -1734,6 +1769,19 @@ describe('input', function() { } ); + it('should allow 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(20456, 1, 1, 0, 0, 0)); + }); + expect(inputElm.val()).toBe('20456-02-01'); + } + ); + it('should label parse errors as `date`', function() { var inputElm = helper.compileInput('', { @@ -1941,6 +1989,118 @@ 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], // 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], // 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], // 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 colon seperator + + // Validate hour + ['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], // 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], // 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 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 + ['2010-06-15T00:00:00.0000', false], // timezone must be specified + + // Validate timezone offset + ['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 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:-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+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 + ['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 + ['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) { + var date = item[0]; + var valid = item[1]; + + /* global ISO_DATE_REGEXP: false */ + expect(ISO_DATE_REGEXP.test(date)).toBe(valid); + }); + }); });