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

Commit 622c421

Browse files
committed
fix(dateFilter, input): fix Date parsing in IE/Edge when timezone offset contains :
When `Date.parse`-ing a date string, IE and Edge don't recognize the timezone offset in the format `+HH:mm` (but only without the `:`). According to [the spec][1], the timezone offset should contain `:`. The [ISO 8601 Standard][2] allows both forms (with and without `:`). Although the `Date` implementation in JavaScript does not 100% follow the ISO 8601 Standard (it's just _based on it_), all other browsers seem to recognize both forms as well. [1]: http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 [2]: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC Fixes #13880 Closes #13887
1 parent db1180f commit 622c421

File tree

4 files changed

+94
-64
lines changed

4 files changed

+94
-64
lines changed

src/Angular.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1215,7 +1215,10 @@ function fromJson(json) {
12151215
}
12161216

12171217

1218+
var ALL_COLONS = /:/g;
12181219
function timezoneToOffset(timezone, fallback) {
1220+
// IE/Edge do not "understand" colon (`:`) in timezone
1221+
timezone = timezone.replace(ALL_COLONS, '');
12191222
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
12201223
return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
12211224
}
@@ -1230,8 +1233,9 @@ function addDateMinutes(date, minutes) {
12301233

12311234
function convertTimezoneToLocal(date, timezone, reverse) {
12321235
reverse = reverse ? -1 : 1;
1233-
var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1234-
return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1236+
var dateTimezoneOffset = date.getTimezoneOffset();
1237+
var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1238+
return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
12351239
}
12361240

12371241

src/ng/filter/filters.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ function dateFilter($locale) {
607607

608608
var dateTimezoneOffset = date.getTimezoneOffset();
609609
if (timezone) {
610-
dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
610+
dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
611611
date = convertTimezoneToLocal(date, timezone, true);
612612
}
613613
forEach(parts, function(value) {

test/ng/directive/inputSpec.js

+70-45
Original file line numberDiff line numberDiff line change
@@ -628,17 +628,22 @@ describe('input', function() {
628628
});
629629

630630

631-
it('should use any timezone if specified in the options', function() {
632-
var inputElm = helper.compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'+0500\'}" />');
631+
they('should use any timezone if specified in the options (format: $prop)',
632+
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
633+
function(tz) {
634+
var ngModelOptions = "{timezone: '" + tz + "'}";
635+
var inputElm = helper.compileInput(
636+
'<input type="month" ng-model="value" ng-model-options="' + ngModelOptions + '" />');
633637

634-
helper.changeInputValueTo('2013-07');
635-
expect(+$rootScope.value).toBe(Date.UTC(2013, 5, 30, 19, 0, 0));
638+
helper.changeInputValueTo('2013-07');
639+
expect(+$rootScope.value).toBe(Date.UTC(2013, 5, 30, 19, 0, 0));
636640

637-
$rootScope.$apply(function() {
638-
$rootScope.value = new Date(Date.UTC(2014, 5, 30, 19, 0, 0));
639-
});
640-
expect(inputElm.val()).toBe('2014-07');
641-
});
641+
$rootScope.$apply(function() {
642+
$rootScope.value = new Date(Date.UTC(2014, 5, 30, 19, 0, 0));
643+
});
644+
expect(inputElm.val()).toBe('2014-07');
645+
}
646+
);
642647

643648

644649
it('should label parse errors as `month`', function() {
@@ -865,17 +870,22 @@ describe('input', function() {
865870
});
866871

867872

868-
it('should use any timezone if specified in the options', function() {
869-
var inputElm = helper.compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'+0500\'}" />');
873+
they('should use any timezone if specified in the options (format: $prop)',
874+
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
875+
function(tz) {
876+
var ngModelOptions = "{timezone: '" + tz + "'}";
877+
var inputElm = helper.compileInput(
878+
'<input type="week" ng-model="value" ng-model-options="' + ngModelOptions + '" />');
870879

871-
helper.changeInputValueTo('2013-W03');
872-
expect(+$rootScope.value).toBe(Date.UTC(2013, 0, 16, 19, 0, 0));
880+
helper.changeInputValueTo('2013-W03');
881+
expect(+$rootScope.value).toBe(Date.UTC(2013, 0, 16, 19, 0, 0));
873882

874-
$rootScope.$apply(function() {
875-
$rootScope.value = new Date(Date.UTC(2014, 0, 16, 19, 0, 0));
876-
});
877-
expect(inputElm.val()).toBe('2014-W03');
878-
});
883+
$rootScope.$apply(function() {
884+
$rootScope.value = new Date(Date.UTC(2014, 0, 16, 19, 0, 0));
885+
});
886+
expect(inputElm.val()).toBe('2014-W03');
887+
}
888+
);
879889

880890

881891
it('should label parse errors as `week`', function() {
@@ -1066,17 +1076,22 @@ describe('input', function() {
10661076
});
10671077

10681078

1069-
it('should use any timezone if specified in the options', function() {
1070-
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="value" ng-model-options="{timezone: \'+0500\'}" />');
1079+
they('should use any timezone if specified in the options (format: $prop)',
1080+
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
1081+
function(tz) {
1082+
var ngModelOptions = "{timezone: '" + tz + "'}";
1083+
var inputElm = helper.compileInput(
1084+
'<input type="datetime-local" ng-model="value" ng-model-options="' + ngModelOptions + '" />');
10711085

1072-
helper.changeInputValueTo('2000-01-01T06:02');
1073-
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 0));
1086+
helper.changeInputValueTo('2000-01-01T06:02');
1087+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 0));
10741088

1075-
$rootScope.$apply(function() {
1076-
$rootScope.value = new Date(Date.UTC(2001, 0, 1, 1, 2, 0));
1077-
});
1078-
expect(inputElm.val()).toBe('2001-01-01T06:02:00.000');
1079-
});
1089+
$rootScope.$apply(function() {
1090+
$rootScope.value = new Date(Date.UTC(2001, 0, 1, 1, 2, 0));
1091+
});
1092+
expect(inputElm.val()).toBe('2001-01-01T06:02:00.000');
1093+
}
1094+
);
10801095

10811096

10821097
it('should fallback to default timezone in case an unknown timezone was passed', function() {
@@ -1390,17 +1405,22 @@ describe('input', function() {
13901405
});
13911406

13921407

1393-
it('should use any timezone if specified in the options', function() {
1394-
var inputElm = helper.compileInput('<input type="time" ng-model="value" ng-model-options="{timezone: \'+0500\'}" />');
1408+
they('should use any timezone if specified in the options (format: $prop)',
1409+
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
1410+
function(tz) {
1411+
var ngModelOptions = "{timezone: '" + tz + "'}";
1412+
var inputElm = helper.compileInput(
1413+
'<input type="time" ng-model="value" ng-model-options="' + ngModelOptions + '" />');
13951414

1396-
helper.changeInputValueTo('23:02:00');
1397-
expect(+$rootScope.value).toBe(Date.UTC(1970, 0, 1, 18, 2, 0));
1415+
helper.changeInputValueTo('23:02:00');
1416+
expect(+$rootScope.value).toBe(Date.UTC(1970, 0, 1, 18, 2, 0));
13981417

1399-
$rootScope.$apply(function() {
1400-
$rootScope.value = new Date(Date.UTC(1971, 0, 1, 18, 2, 0));
1401-
});
1402-
expect(inputElm.val()).toBe('23:02:00.000');
1403-
});
1418+
$rootScope.$apply(function() {
1419+
$rootScope.value = new Date(Date.UTC(1971, 0, 1, 18, 2, 0));
1420+
});
1421+
expect(inputElm.val()).toBe('23:02:00.000');
1422+
}
1423+
);
14041424

14051425

14061426
it('should allow to specify the milliseconds', function() {
@@ -1697,17 +1717,22 @@ describe('input', function() {
16971717
});
16981718

16991719

1700-
it('should use any timezone if specified in the options', function() {
1701-
var inputElm = helper.compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'+0500\'}" />');
1720+
they('should use any timezone if specified in the options (format: $prop)',
1721+
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
1722+
function(tz) {
1723+
var ngModelOptions = "{timezone: '" + tz + "'}";
1724+
var inputElm = helper.compileInput(
1725+
'<input type="date" ng-model="value" ng-model-options="' + ngModelOptions + '" />');
17021726

1703-
helper.changeInputValueTo('2000-01-01');
1704-
expect(+$rootScope.value).toBe(Date.UTC(1999, 11, 31, 19, 0, 0));
1727+
helper.changeInputValueTo('2000-01-01');
1728+
expect(+$rootScope.value).toBe(Date.UTC(1999, 11, 31, 19, 0, 0));
17051729

1706-
$rootScope.$apply(function() {
1707-
$rootScope.value = new Date(Date.UTC(2000, 11, 31, 19, 0, 0));
1708-
});
1709-
expect(inputElm.val()).toBe('2001-01-01');
1710-
});
1730+
$rootScope.$apply(function() {
1731+
$rootScope.value = new Date(Date.UTC(2000, 11, 31, 19, 0, 0));
1732+
});
1733+
expect(inputElm.val()).toBe('2001-01-01');
1734+
}
1735+
);
17111736

17121737

17131738
it('should label parse errors as `date`', function() {

test/ng/filter/filtersSpec.js

+17-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

33
describe('filters', function() {
4-
54
var filter;
65

76
beforeEach(inject(function($filter) {
@@ -165,13 +164,12 @@ describe('filters', function() {
165164
}));
166165
});
167166

168-
169167
describe('number', function() {
170168
var number;
171169

172-
beforeEach(inject(function($rootScope) {
170+
beforeEach(function() {
173171
number = filter('number');
174-
}));
172+
});
175173

176174

177175
it('should do basic filter', function() {
@@ -270,17 +268,16 @@ describe('filters', function() {
270268
});
271269

272270
describe('date', function() {
273-
274-
var morning = new angular.mock.TzDate(+5, '2010-09-03T12:05:08.001Z'); //7am
275-
var noon = new angular.mock.TzDate(+5, '2010-09-03T17:05:08.012Z'); //12pm
276-
var midnight = new angular.mock.TzDate(+5, '2010-09-03T05:05:08.123Z'); //12am
277-
var earlyDate = new angular.mock.TzDate(+5, '0001-09-03T05:05:08.000Z');
278-
var secondWeek = new angular.mock.TzDate(+5, '2013-01-11T12:00:00.000Z'); //Friday Jan 11, 2012
271+
var morning = new angular.mock.TzDate(+5, '2010-09-03T12:05:08.001Z'); //7am
272+
var noon = new angular.mock.TzDate(+5, '2010-09-03T17:05:08.012Z'); //12pm
273+
var midnight = new angular.mock.TzDate(+5, '2010-09-03T05:05:08.123Z'); //12am
274+
var earlyDate = new angular.mock.TzDate(+5, '0001-09-03T05:05:08.000Z');
275+
var secondWeek = new angular.mock.TzDate(+5, '2013-01-11T12:00:00.000Z'); //Friday Jan 11, 2013
279276
var date;
280277

281-
beforeEach(inject(function($filter) {
282-
date = $filter('date');
283-
}));
278+
beforeEach(function() {
279+
date = filter('date');
280+
});
284281

285282
it('should ignore falsy inputs', function() {
286283
expect(date(null)).toBeNull();
@@ -456,7 +453,6 @@ describe('filters', function() {
456453
expect(date(morning, 'yy/xxx')).toEqual('10/xxx');
457454
});
458455

459-
460456
it('should support various iso8061 date strings with timezone as input', function() {
461457
var format = 'yyyy-MM-dd ss';
462458

@@ -479,7 +475,6 @@ describe('filters', function() {
479475
expect(date('2003-09-10T13Z', format)).toEqual('2003-09-' + localDay + ' 00');
480476
});
481477

482-
483478
it('should parse iso8061 date strings without timezone as local time', function() {
484479
var format = 'yyyy-MM-dd HH-mm-ss';
485480

@@ -514,7 +509,13 @@ describe('filters', function() {
514509
});
515510

516511
it('should support conversion to any timezone', function() {
517-
expect(date(new Date(Date.UTC(2003, 8, 10, 3, 2, 4)), 'yyyy-MM-dd HH-mm-ssZ', 'GMT+0500')).toEqual('2003-09-10 08-02-04+0500');
512+
var dateObj = new Date(Date.UTC(2003, 8, 10, 3, 2, 4));
513+
var format = 'yyyy-MM-dd HH-mm-ssZ';
514+
515+
expect(date(dateObj, format, '+0500')).toEqual('2003-09-10 08-02-04+0500');
516+
expect(date(dateObj, format, '+05:00')).toEqual('2003-09-10 08-02-04+0500');
517+
expect(date(dateObj, format, 'GMT+0500')).toEqual('2003-09-10 08-02-04+0500');
518+
expect(date(dateObj, format, 'GMT+05:00')).toEqual('2003-09-10 08-02-04+0500');
518519
});
519520

520521
it('should fallback to default timezone in case an unknown timezone was passed', function() {

0 commit comments

Comments
 (0)