Skip to content

Commit d4d85c3

Browse files
committed
fix(input[date]): correctly parse 2-digit years
When parsing a date string value, AngularJS uses `new Date(year, ...)` to create a Date object and assign it to the model. In the constructor, 2-digit years map to 1900-1999, so the created Date object has the wrong value for year. This commit fixes it, by explicitly using `setFullYear()` to set the year to the correct value, when necessary. Fixes angular#16537
1 parent 841feb0 commit d4d85c3

File tree

2 files changed

+53
-18
lines changed

2 files changed

+53
-18
lines changed

src/ng/directive/input.js

+18-10
Original file line numberDiff line numberDiff line change
@@ -1415,7 +1415,7 @@ function weekParser(isoWeek, existingDate) {
14151415
}
14161416

14171417
function createDateParser(regexp, mapping) {
1418-
return function(iso, date) {
1418+
return function(iso, previousDate) {
14191419
var parts, map;
14201420

14211421
if (isDate(iso)) {
@@ -1437,15 +1437,15 @@ function createDateParser(regexp, mapping) {
14371437

14381438
if (parts) {
14391439
parts.shift();
1440-
if (date) {
1440+
if (previousDate) {
14411441
map = {
1442-
yyyy: date.getFullYear(),
1443-
MM: date.getMonth() + 1,
1444-
dd: date.getDate(),
1445-
HH: date.getHours(),
1446-
mm: date.getMinutes(),
1447-
ss: date.getSeconds(),
1448-
sss: date.getMilliseconds() / 1000
1442+
yyyy: previousDate.getFullYear(),
1443+
MM: previousDate.getMonth() + 1,
1444+
dd: previousDate.getDate(),
1445+
HH: previousDate.getHours(),
1446+
mm: previousDate.getMinutes(),
1447+
ss: previousDate.getSeconds(),
1448+
sss: previousDate.getMilliseconds() / 1000
14491449
};
14501450
} else {
14511451
map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
@@ -1456,7 +1456,15 @@ function createDateParser(regexp, mapping) {
14561456
map[mapping[index]] = +part;
14571457
}
14581458
});
1459-
return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
1459+
1460+
var date = new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
1461+
if (map.yyyy < 100) {
1462+
// In the constructor, 2-digit years map to 1900-1999.
1463+
// Use `setFullYear()` to set the correct year.
1464+
date.setFullYear(map.yyyy);
1465+
}
1466+
1467+
return date;
14601468
}
14611469
}
14621470

test/ng/directive/inputSpec.js

+35-8
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@
33
/* globals generateInputCompilerHelper: false */
44

55
describe('input', function() {
6-
var helper = {}, $compile, $rootScope, $browser, $sniffer, $timeout, $q;
6+
var helper = {}, $compile, $rootScope, $browser, $sniffer;
77

88
// UA sniffing to exclude Edge from some date input tests
99
var isEdge = /\bEdge\//.test(window.navigator.userAgent);
1010

1111
generateInputCompilerHelper(helper);
1212

13-
beforeEach(inject(function(_$compile_, _$rootScope_, _$browser_, _$sniffer_, _$timeout_, _$q_) {
13+
beforeEach(inject(function(_$compile_, _$rootScope_, _$browser_, _$sniffer_) {
1414
$compile = _$compile_;
1515
$rootScope = _$rootScope_;
1616
$browser = _$browser_;
1717
$sniffer = _$sniffer_;
18-
$timeout = _$timeout_;
19-
$q = _$q_;
2018
}));
2119

2220

@@ -1556,6 +1554,20 @@ describe('input', function() {
15561554

15571555
expect(inputElm).toBeValid();
15581556
});
1557+
1558+
1559+
it('should correctly handle 2-digit years', function() {
1560+
helper.compileInput('<input type="datetime-local" ng-model="value" name="alias" />');
1561+
1562+
helper.changeInputValueTo('0001-01-01T12:34:00');
1563+
expect($rootScope.value.getFullYear()).toBe(1);
1564+
1565+
helper.changeInputValueTo('0099-01-01T12:34:00');
1566+
expect($rootScope.value.getFullYear()).toBe(99);
1567+
1568+
helper.changeInputValueTo('0100-01-01T12:34:00');
1569+
expect($rootScope.value.getFullYear()).toBe(100);
1570+
});
15591571
});
15601572

15611573

@@ -2323,9 +2335,9 @@ describe('input', function() {
23232335

23242336
it('should allow Date objects as valid ng-max values', function() {
23252337
$rootScope.max = new Date(2012, 1, 1, 1, 2, 0);
2326-
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="value" name="alias" ng-max="max" />');
2338+
var inputElm = helper.compileInput('<input type="date" ng-model="value" name="alias" ng-max="max" />');
23272339

2328-
helper.changeInputValueTo('2014-01-01T12:34:00');
2340+
helper.changeInputValueTo('2014-01-01');
23292341
expect(inputElm).toBeInvalid();
23302342

23312343
$rootScope.max = new Date(2013, 1, 1, 1, 2, 0);
@@ -2342,9 +2354,9 @@ describe('input', function() {
23422354

23432355
it('should allow Date objects as valid ng-min values', function() {
23442356
$rootScope.min = new Date(2013, 1, 1, 1, 2, 0);
2345-
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="value" name="alias" ng-min="min" />');
2357+
var inputElm = helper.compileInput('<input type="date" ng-model="value" name="alias" ng-min="min" />');
23462358

2347-
helper.changeInputValueTo('2010-01-01T12:34:00');
2359+
helper.changeInputValueTo('2010-01-01');
23482360
expect(inputElm).toBeInvalid();
23492361

23502362
$rootScope.min = new Date(2014, 1, 1, 1, 2, 0);
@@ -2358,6 +2370,21 @@ describe('input', function() {
23582370
expect(inputElm).toBeValid();
23592371
});
23602372

2373+
2374+
it('should correctly handle 2-digit years', function() {
2375+
helper.compileInput('<input type="date" ng-model="value" name="alias" />');
2376+
2377+
helper.changeInputValueTo('0001-01-01');
2378+
expect($rootScope.value.getFullYear()).toBe(1);
2379+
2380+
helper.changeInputValueTo('0099-01-01');
2381+
expect($rootScope.value.getFullYear()).toBe(99);
2382+
2383+
helper.changeInputValueTo('0100-01-01');
2384+
expect($rootScope.value.getFullYear()).toBe(100);
2385+
});
2386+
2387+
23612388
describe('ISO_DATE_REGEXP', function() {
23622389
var dates = [
23632390
// Validate date

0 commit comments

Comments
 (0)