Skip to content

Commit 0da52ef

Browse files
committed
fix(input): allow overriding timezone for date input types
This commit also fixes a bug where part of the Date object was re-used even after the input was emptied. Fixes angular#13382 Closes angular#16181
1 parent aa3f951 commit 0da52ef

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

src/ng/directive/input.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1431,17 +1431,20 @@ function createDateInputType(type, regexp, parseDate, format) {
14311431
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
14321432
badInputChecker(scope, element, attr, ctrl);
14331433
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
1434-
var timezone = ctrl && ctrl.$options.getOption('timezone');
14351434
var previousDate;
14361435

14371436
ctrl.$$parserName = type;
14381437
ctrl.$parsers.push(function(value) {
1439-
if (ctrl.$isEmpty(value)) return null;
1438+
if (ctrl.$isEmpty(value)) {
1439+
previousDate = null;
1440+
return null;
1441+
}
14401442
if (regexp.test(value)) {
14411443
// Note: We cannot read ctrl.$modelValue, as there might be a different
14421444
// parser/formatter in the processing chain so that the model
14431445
// contains some different data format!
14441446
var parsedDate = parseDate(value, previousDate);
1447+
var timezone = ctrl.$options.getOption('timezone');
14451448
if (timezone) {
14461449
parsedDate = convertTimezoneToLocal(parsedDate, timezone);
14471450
}
@@ -1456,6 +1459,7 @@ function createDateInputType(type, regexp, parseDate, format) {
14561459
}
14571460
if (isValidDate(value)) {
14581461
previousDate = value;
1462+
var timezone = ctrl.$options.getOption('timezone');
14591463
if (previousDate && timezone) {
14601464
previousDate = convertTimezoneToLocal(previousDate, timezone, true);
14611465
}

test/ng/directive/inputSpec.js

+110
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,21 @@ describe('input', function() {
662662
});
663663

664664

665+
it('should be possible to override the timezone', function() {
666+
var inputElm = helper.compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
667+
668+
helper.changeInputValueTo('2013-07');
669+
expect(+$rootScope.value).toBe(Date.UTC(2013, 6, 1));
670+
671+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'});
672+
673+
$rootScope.$apply(function() {
674+
$rootScope.value = new Date(Date.UTC(2014, 6, 1));
675+
});
676+
expect(inputElm.val()).toBe('2014-06');
677+
});
678+
679+
665680
they('should use any timezone if specified in the options (format: $prop)',
666681
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
667682
function(tz) {
@@ -931,6 +946,21 @@ describe('input', function() {
931946
});
932947

933948

949+
it('should be possible to override the timezone', function() {
950+
var inputElm = helper.compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
951+
952+
helper.changeInputValueTo('2013-W03');
953+
expect(+$rootScope.value).toBe(Date.UTC(2013, 0, 17));
954+
955+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '+5000'});
956+
957+
$rootScope.$apply(function() {
958+
$rootScope.value = new Date(Date.UTC(2014, 0, 17));
959+
});
960+
expect(inputElm.val()).toBe('2014-W04');
961+
});
962+
963+
934964
they('should use any timezone if specified in the options (format: $prop)',
935965
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
936966
function(tz) {
@@ -1137,6 +1167,25 @@ describe('input', function() {
11371167
});
11381168

11391169

1170+
it('should be possible to override the timezone', function() {
1171+
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1172+
1173+
helper.changeInputValueTo('2000-01-01T01:02');
1174+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 0));
1175+
1176+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '+0500'});
1177+
$rootScope.$apply(function() {
1178+
$rootScope.value = new Date(Date.UTC(2001, 0, 1, 1, 2, 0));
1179+
});
1180+
expect(inputElm.val()).toBe('2001-01-01T06:02:00.000');
1181+
1182+
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'});
1183+
1184+
helper.changeInputValueTo('2000-01-01T01:02');
1185+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 0));
1186+
});
1187+
1188+
11401189
they('should use any timezone if specified in the options (format: $prop)',
11411190
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
11421191
function(tz) {
@@ -1482,6 +1531,25 @@ describe('input', function() {
14821531
});
14831532

14841533

1534+
it('should be possible to override the timezone', function() {
1535+
var inputElm = helper.compileInput('<input type="time" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1536+
1537+
helper.changeInputValueTo('23:02:00');
1538+
expect(+$rootScope.value).toBe(Date.UTC(1970, 0, 1, 23, 2, 0));
1539+
1540+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'});
1541+
$rootScope.$apply(function() {
1542+
$rootScope.value = new Date(Date.UTC(1971, 0, 1, 23, 2, 0));
1543+
});
1544+
expect(inputElm.val()).toBe('18:02:00.000');
1545+
1546+
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'});
1547+
helper.changeInputValueTo('23:02:00');
1548+
// The year is still set from the previous date
1549+
expect(+$rootScope.value).toBe(Date.UTC(1971, 0, 1, 23, 2, 0));
1550+
});
1551+
1552+
14851553
they('should use any timezone if specified in the options (format: $prop)',
14861554
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
14871555
function(tz) {
@@ -1794,6 +1862,24 @@ describe('input', function() {
17941862
});
17951863

17961864

1865+
it('should be possible to override the timezone', function() {
1866+
var inputElm = helper.compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1867+
1868+
helper.changeInputValueTo('2000-01-01');
1869+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1));
1870+
1871+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'});
1872+
$rootScope.$apply(function() {
1873+
$rootScope.value = new Date(Date.UTC(2001, 0, 1));
1874+
});
1875+
expect(inputElm.val()).toBe('2000-12-31');
1876+
1877+
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'});
1878+
helper.changeInputValueTo('2000-01-01');
1879+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 19));
1880+
});
1881+
1882+
17971883
they('should use any timezone if specified in the options (format: $prop)',
17981884
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
17991885
function(tz) {
@@ -1879,6 +1965,30 @@ describe('input', function() {
18791965
dealoc(formElm);
18801966
});
18811967

1968+
it('should not reuse the hour part of a previous date object after emptying the input', function() {
1969+
var inputElm = helper.compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1970+
1971+
helper.changeInputValueTo('2000-01-01');
1972+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1));
1973+
1974+
// Change the timezone offset so that the display date is a day earlier
1975+
// This does not change the model, but our implementation
1976+
// internally caches a Date object with this offset
1977+
// and re-uses it if part of the date changes
1978+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'});
1979+
$rootScope.$apply(function() {
1980+
$rootScope.value = new Date(Date.UTC(2001, 0, 1));
1981+
});
1982+
expect(inputElm.val()).toBe('2000-12-31');
1983+
1984+
// Emptying the input should clear the cached date object
1985+
helper.changeInputValueTo('');
1986+
1987+
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'});
1988+
helper.changeInputValueTo('2000-01-01');
1989+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 0));
1990+
});
1991+
18821992
describe('min', function() {
18831993

18841994
it('should invalidate', function() {

0 commit comments

Comments
 (0)