Skip to content

Commit 0960096

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#16181 Closes angular#13382
1 parent 41d5c90 commit 0960096

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,16 +1431,19 @@ function createDateInputType(type, regexp, parseDate, format) {
14311431
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
14321432
badInputChecker(scope, element, attr, ctrl, type);
14331433
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
1434-
var timezone = ctrl && ctrl.$options.getOption('timezone');
14351434
var previousDate;
14361435

14371436
ctrl.$parsers.push(function(value) {
1438-
if (ctrl.$isEmpty(value)) return null;
1437+
if (ctrl.$isEmpty(value)) {
1438+
previousDate = null;
1439+
return null;
1440+
}
14391441
if (regexp.test(value)) {
14401442
// Note: We cannot read ctrl.$modelValue, as there might be a different
14411443
// parser/formatter in the processing chain so that the model
14421444
// contains some different data format!
14431445
var parsedDate = parseDate(value, previousDate);
1446+
var timezone = ctrl.$options.getOption('timezone');
14441447
if (timezone) {
14451448
parsedDate = convertTimezoneToLocal(parsedDate, timezone);
14461449
}
@@ -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
@@ -693,6 +693,21 @@ describe('input', function() {
693693
});
694694

695695

696+
it('should be possible to override the timezone', function() {
697+
var inputElm = helper.compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
698+
699+
helper.changeInputValueTo('2013-07');
700+
expect(+$rootScope.value).toBe(Date.UTC(2013, 6, 1));
701+
702+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'});
703+
704+
$rootScope.$apply(function() {
705+
$rootScope.value = new Date(Date.UTC(2014, 6, 1));
706+
});
707+
expect(inputElm.val()).toBe('2014-06');
708+
});
709+
710+
696711
they('should use any timezone if specified in the options (format: $prop)',
697712
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
698713
function(tz) {
@@ -962,6 +977,21 @@ describe('input', function() {
962977
});
963978

964979

980+
it('should be possible to override the timezone', function() {
981+
var inputElm = helper.compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
982+
983+
helper.changeInputValueTo('2013-W03');
984+
expect(+$rootScope.value).toBe(Date.UTC(2013, 0, 17));
985+
986+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '+5000'});
987+
988+
$rootScope.$apply(function() {
989+
$rootScope.value = new Date(Date.UTC(2014, 0, 17));
990+
});
991+
expect(inputElm.val()).toBe('2014-W04');
992+
});
993+
994+
965995
they('should use any timezone if specified in the options (format: $prop)',
966996
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
967997
function(tz) {
@@ -1168,6 +1198,25 @@ describe('input', function() {
11681198
});
11691199

11701200

1201+
it('should be possible to override the timezone', function() {
1202+
var inputElm = helper.compileInput('<input type="datetime-local" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1203+
1204+
helper.changeInputValueTo('2000-01-01T01:02');
1205+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 0));
1206+
1207+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '+0500'});
1208+
$rootScope.$apply(function() {
1209+
$rootScope.value = new Date(Date.UTC(2001, 0, 1, 1, 2, 0));
1210+
});
1211+
expect(inputElm.val()).toBe('2001-01-01T06:02:00.000');
1212+
1213+
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'});
1214+
1215+
helper.changeInputValueTo('2000-01-01T01:02');
1216+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 0));
1217+
});
1218+
1219+
11711220
they('should use any timezone if specified in the options (format: $prop)',
11721221
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
11731222
function(tz) {
@@ -1513,6 +1562,25 @@ describe('input', function() {
15131562
});
15141563

15151564

1565+
it('should be possible to override the timezone', function() {
1566+
var inputElm = helper.compileInput('<input type="time" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1567+
1568+
helper.changeInputValueTo('23:02:00');
1569+
expect(+$rootScope.value).toBe(Date.UTC(1970, 0, 1, 23, 2, 0));
1570+
1571+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'});
1572+
$rootScope.$apply(function() {
1573+
$rootScope.value = new Date(Date.UTC(1971, 0, 1, 23, 2, 0));
1574+
});
1575+
expect(inputElm.val()).toBe('18:02:00.000');
1576+
1577+
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'});
1578+
helper.changeInputValueTo('23:02:00');
1579+
// The year is still set from the previous date
1580+
expect(+$rootScope.value).toBe(Date.UTC(1971, 0, 1, 23, 2, 0));
1581+
});
1582+
1583+
15161584
they('should use any timezone if specified in the options (format: $prop)',
15171585
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
15181586
function(tz) {
@@ -1825,6 +1893,24 @@ describe('input', function() {
18251893
});
18261894

18271895

1896+
it('should be possible to override the timezone', function() {
1897+
var inputElm = helper.compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1898+
1899+
helper.changeInputValueTo('2000-01-01');
1900+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1));
1901+
1902+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'});
1903+
$rootScope.$apply(function() {
1904+
$rootScope.value = new Date(Date.UTC(2001, 0, 1));
1905+
});
1906+
expect(inputElm.val()).toBe('2000-12-31');
1907+
1908+
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'});
1909+
helper.changeInputValueTo('2000-01-01');
1910+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 19));
1911+
});
1912+
1913+
18281914
they('should use any timezone if specified in the options (format: $prop)',
18291915
{'+HHmm': '+0500', '+HH:mm': '+05:00'},
18301916
function(tz) {
@@ -1910,6 +1996,30 @@ describe('input', function() {
19101996
dealoc(formElm);
19111997
});
19121998

1999+
it('should not reuse the hour part of a previous date object after emptying the input', function() {
2000+
var inputElm = helper.compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
2001+
2002+
helper.changeInputValueTo('2000-01-01');
2003+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1));
2004+
2005+
// Change the timezone offset so that the display date is a day earlier
2006+
// This does not change the model, but our implementation
2007+
// internally caches a Date object with this offset
2008+
// and re-uses it if part of the date changes
2009+
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'});
2010+
$rootScope.$apply(function() {
2011+
$rootScope.value = new Date(Date.UTC(2001, 0, 1));
2012+
});
2013+
expect(inputElm.val()).toBe('2000-12-31');
2014+
2015+
// Emptying the input should clear the cached date object
2016+
helper.changeInputValueTo('');
2017+
2018+
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'});
2019+
helper.changeInputValueTo('2000-01-01');
2020+
expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 0));
2021+
});
2022+
19132023
describe('min', function() {
19142024

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

0 commit comments

Comments
 (0)