Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 9425015

Browse files
LeeAdcockgkalpak
authored andcommitted
fix(input[date]): support years with more than 4 digits
Previously, the date-related regular expressions only matched years with no more than 4 digits. This commit adds support for years with more than 4 digits. It also resolves an ambiguity in `ISO_DATE_REGEXP` by matching the whole string (when it previosuly allowed extra characters around the date string). Fixes #13735 Closes #13905
1 parent 7b592f9 commit 9425015

File tree

2 files changed

+166
-6
lines changed

2 files changed

+166
-6
lines changed

src/ng/directive/input.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
ngModelMinErr: false,
1010
*/
1111

12-
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
13-
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)/;
12+
// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
13+
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)$/;
1414
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
1515
// Note: We are being more lenient, because browsers are too.
1616
// 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-
2626
var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
2727
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;
2828
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
29-
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
30-
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
31-
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
32-
var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
29+
var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
30+
var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
31+
var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
32+
var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
3333
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
3434

3535
var inputType = {

test/ng/directive/inputSpec.js

+160
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,18 @@ describe('input', function() {
657657
expect($rootScope.form.alias.$error.month).toBeTruthy();
658658
});
659659

660+
it('should allow four or more digits in year', function() {
661+
var inputElm = helper.compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}"/>');
662+
663+
helper.changeInputValueTo('10123-03');
664+
expect(+$rootScope.value).toBe(Date.UTC(10123, 2, 1, 0, 0, 0));
665+
666+
$rootScope.$apply(function() {
667+
$rootScope.value = new Date(Date.UTC(20456, 3, 1, 0, 0, 0));
668+
});
669+
expect(inputElm.val()).toBe('20456-04');
670+
});
671+
660672

661673
it('should only change the month of a bound date', function() {
662674
var inputElm = helper.compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
@@ -856,6 +868,17 @@ describe('input', function() {
856868
expect(inputElm).toBeValid();
857869
});
858870

871+
it('should allow four or more digits in year', function() {
872+
var inputElm = helper.compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'UTC\'}"/>');
873+
874+
helper.changeInputValueTo('10123-W03');
875+
expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 21));
876+
877+
$rootScope.$apply(function() {
878+
$rootScope.value = new Date(Date.UTC(20456, 0, 28));
879+
});
880+
expect(inputElm.val()).toBe('20456-W04');
881+
});
859882

860883
it('should use UTC if specified in the options', function() {
861884
var inputElm = helper.compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
@@ -1141,6 +1164,18 @@ describe('input', function() {
11411164
expect(+$rootScope.value).toBe(+new Date(2000, 0, 1, 1, 2, 0));
11421165
});
11431166

1167+
it('should allow four or more digits in year', function() {
1168+
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="value" />');
1169+
1170+
helper.changeInputValueTo('10123-01-01T01:02');
1171+
expect(+$rootScope.value).toBe(+new Date(10123, 0, 1, 1, 2, 0));
1172+
1173+
$rootScope.$apply(function() {
1174+
$rootScope.value = new Date(20456, 1, 1, 1, 2, 0);
1175+
});
1176+
expect(inputElm.val()).toBe('20456-02-01T01:02:00.000');
1177+
}
1178+
);
11441179

11451180
it('should label parse errors as `datetimelocal`', function() {
11461181
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="val" name="alias" />', {
@@ -1734,6 +1769,19 @@ describe('input', function() {
17341769
}
17351770
);
17361771

1772+
it('should allow four or more digits in year', function() {
1773+
var inputElm = helper.compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1774+
1775+
helper.changeInputValueTo('10123-01-01');
1776+
expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 1, 0, 0, 0));
1777+
1778+
$rootScope.$apply(function() {
1779+
$rootScope.value = new Date(Date.UTC(20456, 1, 1, 0, 0, 0));
1780+
});
1781+
expect(inputElm.val()).toBe('20456-02-01');
1782+
}
1783+
);
1784+
17371785

17381786
it('should label parse errors as `date`', function() {
17391787
var inputElm = helper.compileInput('<input type="date" ng-model="val" name="alias" />', {
@@ -1941,6 +1989,118 @@ describe('input', function() {
19411989

19421990
expect(inputElm).toBeValid();
19431991
});
1992+
1993+
describe('ISO_DATE_REGEXP', function() {
1994+
var dates = [
1995+
// Validate date
1996+
['00:00:00.0000+01:01', false], // date must be specified
1997+
['2010.06.15T00:00:00.0000+01:01', false], // date must use dash seperator
1998+
['x2010-06-15T00:00:00.0000+01:01', false], // invalid leading characters
1999+
2000+
// Validate year
2001+
['2010-06-15T00:00:00.0000+01:01', true], // year has four or more digits
2002+
['20100-06-15T00:00:00.0000+01:01', true], // year has four or more digits
2003+
['-06-15T00:00:00.0000+01:01', false], // year has too few digits
2004+
['2-06-15T00:00:00.0000+01:01', false], // year has too few digits
2005+
['20-06-15T00:00:00.0000+01:01', false], // year has too few digits
2006+
['201-06-15T00:00:00.0000+01:01', false], // year has too few digits
2007+
2008+
// Validate month
2009+
['2010-01-15T00:00:00.0000+01:01', true], // month has two digits
2010+
['2010--15T00:00:00.0000+01:01', false], // month has too few digits
2011+
['2010-0-15T00:00:00.0000+01:01', false], // month has too few digits
2012+
['2010-1-15T00:00:00.0000+01:01', false], // month has too few digits
2013+
['2010-111-15T00:00:00.0000+01:01', false], // month has too many digits
2014+
['2010-22-15T00:00:00.0000+01:01', false], // month is too large
2015+
2016+
// Validate day
2017+
['2010-01-01T00:00:00.0000+01:01', true], // day has two digits
2018+
['2010-01-T00:00:00.0000+01:01', false], // day has too few digits
2019+
['2010-01-1T00:00:00.0000+01:01', false], // day has too few digits
2020+
['2010-01-200T00:00:00.0000+01:01', false], // day has too many digits
2021+
['2010-01-41T00:00:00.0000+01:01', false], // day is too large
2022+
2023+
// Validate time
2024+
['2010-01-01', false], // time must be specified
2025+
['2010-01-0101:00:00.0000+01:01', false], // missing date time seperator
2026+
['2010-01-01V01:00:00.0000+01:01', false], // invalid date time seperator
2027+
['2010-01-01T01-00-00.0000+01:01', false], // time must use colon seperator
2028+
2029+
// Validate hour
2030+
['2010-01-01T01:00:00.0000+01:01', true], // hour has two digits
2031+
['2010-01-01T-01:00:00.0000+01:01', false], // hour must be positive
2032+
['2010-01-01T:00:00.0000+01:01', false], // hour has too few digits
2033+
['2010-01-01T1:00:00.0000+01:01', false], // hour has too few digits
2034+
['2010-01-01T220:00:00.0000+01:01', false], // hour has too many digits
2035+
['2010-01-01T32:00:00.0000+01:01', false], // hour is too large
2036+
2037+
// Validate minutes
2038+
['2010-01-01T01:00:00.0000+01:01', true], // minute has two digits
2039+
['2010-01-01T01:-00:00.0000+01:01', false], // minute must be positive
2040+
['2010-01-01T01::00.0000+01:01', false], // minute has too few digits
2041+
['2010-01-01T01:0:00.0000+01:01', false], // minute has too few digits
2042+
['2010-01-01T01:100:00.0000+01:01', false], // minute has too many digits
2043+
['2010-01-01T01:60:00.0000+01:01', false], // minute is too large
2044+
2045+
// Validate seconds
2046+
['2010-01-01T01:00:00.0000+01:01', true], // second has two digits
2047+
['2010-01-01T01:00:-00.0000+01:01', false], // second must be positive
2048+
['2010-01-01T01:00:.0000+01:01', false], // second has too few digits
2049+
['2010-01-01T01:00:0.0000+01:01', false], // second has too few digits
2050+
['2010-01-01T01:00:100.0000+01:01', false], // second has too many digits
2051+
['2010-01-01T01:00:60.0000+01:01', false], // second is too large
2052+
2053+
// Validate milliseconds
2054+
['2010-01-01T01:00:00+01:01', false], // millisecond must be specified
2055+
['2010-01-01T01:00:00.-0000+01:01', false], // millisecond must be positive
2056+
['2010-01-01T01:00:00:0000+01:01', false], // millisecond must use period seperator
2057+
['2010-01-01T01:00:00.+01:01', false], // millisecond has too few digits
2058+
2059+
// Validate timezone
2060+
['2010-06-15T00:00:00.0000', false], // timezone must be specified
2061+
2062+
// Validate timezone offset
2063+
['2010-06-15T00:00:00.0000+01:01', true], // timezone offset can be positive hours and minutes
2064+
['2010-06-15T00:00:00.0000-01:01', true], // timezone offset can be negative hours and minutes
2065+
['2010-06-15T00:00:00.0000~01:01', false], // timezone has postive/negative indicator
2066+
['2010-06-15T00:00:00.000001:01', false], // timezone has postive/negative indicator
2067+
['2010-06-15T00:00:00.0000+00:01Z', false], // timezone invalid trailing characters
2068+
['2010-06-15T00:00:00.0000+00:01 ', false], // timezone invalid trailing characters
2069+
2070+
// Validate timezone hour offset
2071+
['2010-06-15T00:00:00.0000+:01', false], // timezone hour offset has too few digits
2072+
['2010-06-15T00:00:00.0000+0:01', false], // timezone hour offset has too few digits
2073+
['2010-06-15T00:00:00.0000+211:01', false], // timezone hour offset too many digits
2074+
['2010-06-15T00:00:00.0000+31:01', false], // timezone hour offset value too large
2075+
2076+
// Validate timezone minute offset
2077+
['2010-06-15T00:00:00.0000+00:-01', false], // timezone minute offset must be positive
2078+
['2010-06-15T00:00:00.0000+00.01', false], // timezone minute offset must use colon seperator
2079+
['2010-06-15T00:00:00.0000+0101', false], // timezone minute offset must use colon seperator
2080+
['2010-06-15T00:00:00.0000+010', false], // timezone minute offset must use colon seperator
2081+
['2010-06-15T00:00:00.0000+00', false], // timezone minute offset has too few digits
2082+
['2010-06-15T00:00:00.0000+00:', false], // timezone minute offset has too few digits
2083+
['2010-06-15T00:00:00.0000+00:0', false], // timezone minute offset has too few digits
2084+
['2010-06-15T00:00:00.0000+00:211', false], // timezone minute offset has too many digits
2085+
['2010-06-15T00:00:00.0000+01010', false], // timezone minute offset has too many digits
2086+
['2010-06-15T00:00:00.0000+00:61', false], // timezone minute offset is too large
2087+
2088+
// Validate timezone UTC
2089+
['2010-06-15T00:00:00.0000Z', true], // UTC timezone can be indicated with Z
2090+
['2010-06-15T00:00:00.0000K', false], // UTC timezone indicator is invalid
2091+
['2010-06-15T00:00:00.0000 Z', false], // UTC timezone indicator has extra space
2092+
['2010-06-15T00:00:00.0000ZZ', false], // UTC timezone indicator invalid trailing characters
2093+
['2010-06-15T00:00:00.0000Z ', false] // UTC timezone indicator invalid trailing characters
2094+
];
2095+
2096+
they('should validate date: $prop', dates, function(item) {
2097+
var date = item[0];
2098+
var valid = item[1];
2099+
2100+
/* global ISO_DATE_REGEXP: false */
2101+
expect(ISO_DATE_REGEXP.test(date)).toBe(valid);
2102+
});
2103+
});
19442104
});
19452105

19462106

0 commit comments

Comments
 (0)