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

Commit cc6fc19

Browse files
committed
feat(input): allow to define the timezone for parsing dates
Angular used to always use the browser timezone when parsing `input[date]`, `input[time]`, … The timezone can now be changed to `UTC` via `ngModelOptions`. Closes #8447.
1 parent 29f0b56 commit cc6fc19

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

src/ng/directive/input.js

+25-2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ var inputType = {
113113
* modern browsers do not yet support this input type, it is important to provide cues to users on the
114114
* expected input format via a placeholder or label. The model must always be a Date object.
115115
*
116+
* The timezone to be used to read/write the `Date` instance in the model can be defined using
117+
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
118+
*
116119
* @param {string} ngModel Assignable angular expression to data-bind to.
117120
* @param {string=} name Property name of the form under which the control is published.
118121
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
@@ -198,6 +201,9 @@ var inputType = {
198201
* the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
199202
* local datetime format (yyyy-MM-ddTHH:mm), for example: `2010-12-28T14:57`. The model must be a Date object.
200203
*
204+
* The timezone to be used to read/write the `Date` instance in the model can be defined using
205+
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
206+
*
201207
* @param {string} ngModel Assignable angular expression to data-bind to.
202208
* @param {string=} name Property name of the form under which the control is published.
203209
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
@@ -284,6 +290,9 @@ var inputType = {
284290
* local time format (HH:mm), for example: `14:57`. Model must be a Date object. This binding will always output a
285291
* Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm)`.
286292
*
293+
* The timezone to be used to read/write the `Date` instance in the model can be defined using
294+
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
295+
*
287296
* @param {string} ngModel Assignable angular expression to data-bind to.
288297
* @param {string=} name Property name of the form under which the control is published.
289298
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
@@ -369,6 +378,9 @@ var inputType = {
369378
* the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
370379
* week format (yyyy-W##), for example: `2013-W02`. The model must always be a Date object.
371380
*
381+
* The timezone to be used to read/write the `Date` instance in the model can be defined using
382+
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
383+
*
372384
* @param {string} ngModel Assignable angular expression to data-bind to.
373385
* @param {string=} name Property name of the form under which the control is published.
374386
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
@@ -453,6 +465,9 @@ var inputType = {
453465
* month format (yyyy-MM), for example: `2009-01`. The model must always be a Date object. In the event the model is
454466
* not set to the first of the month, the first of that model's month is assumed.
455467
*
468+
* The timezone to be used to read/write the `Date` instance in the model can be defined using
469+
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
470+
*
456471
* @param {string} ngModel Assignable angular expression to data-bind to.
457472
* @param {string=} name Property name of the form under which the control is published.
458473
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
@@ -1061,6 +1076,7 @@ function createDateParser(regexp, mapping) {
10611076
function createDateInputType(type, regexp, parseDate, format) {
10621077
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
10631078
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1079+
var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
10641080

10651081
ctrl.$parsers.push(function(value) {
10661082
if(ctrl.$isEmpty(value)) {
@@ -1070,7 +1086,11 @@ function createDateInputType(type, regexp, parseDate, format) {
10701086

10711087
if(regexp.test(value)) {
10721088
ctrl.$setValidity(type, true);
1073-
return parseDate(value);
1089+
var parsedDate = parseDate(value);
1090+
if (timezone === 'UTC') {
1091+
parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
1092+
}
1093+
return parsedDate;
10741094
}
10751095

10761096
ctrl.$setValidity(type, false);
@@ -1079,7 +1099,7 @@ function createDateInputType(type, regexp, parseDate, format) {
10791099

10801100
ctrl.$formatters.push(function(value) {
10811101
if(isDate(value)) {
1082-
return $filter('date')(value, format);
1102+
return $filter('date')(value, format, timezone);
10831103
}
10841104
return '';
10851105
});
@@ -2604,6 +2624,9 @@ var ngValueDirective = function() {
26042624
* `ngModelOptions="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
26052625
* - `getterSetter`: boolean value which determines whether or not to treat functions bound to
26062626
`ngModel` as getters/setters.
2627+
* - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
2628+
* `<input type="date">`, `<input type="time">`, ... . Right now, the only supported value is `'UTC'`,
2629+
* otherwise the default timezone of the browser will be used.
26072630
*
26082631
* @example
26092632

test/ng/directive/inputSpec.js

+60
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,18 @@ describe('input', function() {
16271627
expect(inputElm).toBeValid();
16281628
});
16291629

1630+
it('should use UTC if specified in the options', function() {
1631+
compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1632+
1633+
changeInputValueTo('2013-07');
1634+
expect(+scope.value).toBe(Date.UTC(2013, 6, 1));
1635+
1636+
scope.$apply(function() {
1637+
scope.value = new Date(Date.UTC(2014, 6, 1));
1638+
});
1639+
expect(inputElm.val()).toBe('2014-07');
1640+
});
1641+
16301642

16311643
describe('min', function (){
16321644
beforeEach(function (){
@@ -1746,6 +1758,18 @@ describe('input', function() {
17461758
expect(inputElm).toBeValid();
17471759
});
17481760

1761+
it('should use UTC if specified in the options', function() {
1762+
compileInput('<input type="week" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1763+
1764+
changeInputValueTo('2013-W03');
1765+
expect(+scope.value).toBe(Date.UTC(2013, 0, 17));
1766+
1767+
scope.$apply(function() {
1768+
scope.value = new Date(Date.UTC(2014, 0, 17));
1769+
});
1770+
expect(inputElm.val()).toBe('2014-W03');
1771+
});
1772+
17491773
describe('min', function (){
17501774
beforeEach(function (){
17511775
compileInput('<input type="week" ng-model="value" name="alias" min="2013-W01" />');
@@ -1863,6 +1887,18 @@ describe('input', function() {
18631887
expect(inputElm).toBeValid();
18641888
});
18651889

1890+
it('should use UTC if specified in the options', function() {
1891+
compileInput('<input type="datetime-local" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
1892+
1893+
changeInputValueTo('2000-01-01T01:02');
1894+
expect(+scope.value).toBe(Date.UTC(2000, 0, 1, 1, 2));
1895+
1896+
scope.$apply(function() {
1897+
scope.value = new Date(Date.UTC(2001, 0, 1, 1, 2));
1898+
});
1899+
expect(inputElm.val()).toBe('2001-01-01T01:02');
1900+
});
1901+
18661902
describe('min', function (){
18671903
beforeEach(function (){
18681904
compileInput('<input type="datetime-local" ng-model="value" name="alias" min="2000-01-01T12:30" />');
@@ -2008,6 +2044,18 @@ describe('input', function() {
20082044
expect(inputElm).toBeValid();
20092045
});
20102046

2047+
it('should use UTC if specified in the options', function() {
2048+
compileInput('<input type="time" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
2049+
2050+
changeInputValueTo('23:02');
2051+
expect(+scope.value).toBe(Date.UTC(1970, 0, 1, 23, 2));
2052+
2053+
scope.$apply(function() {
2054+
scope.value = new Date(Date.UTC(1971, 0, 1, 23, 2));
2055+
});
2056+
expect(inputElm.val()).toBe('23:02');
2057+
});
2058+
20112059
describe('min', function (){
20122060
beforeEach(function (){
20132061
compileInput('<input type="time" ng-model="value" name="alias" min="09:30" />');
@@ -2153,6 +2201,18 @@ describe('input', function() {
21532201
expect(inputElm).toBeValid();
21542202
});
21552203

2204+
it('should use UTC if specified in the options', function() {
2205+
compileInput('<input type="date" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
2206+
2207+
changeInputValueTo('2000-01-01');
2208+
expect(+scope.value).toBe(Date.UTC(2000, 0, 1));
2209+
2210+
scope.$apply(function() {
2211+
scope.value = new Date(Date.UTC(2001, 0, 1));
2212+
});
2213+
expect(inputElm.val()).toBe('2001-01-01');
2214+
});
2215+
21562216
describe('min', function (){
21572217
beforeEach(function (){
21582218
compileInput('<input type="date" ng-model="value" name="alias" min="2000-01-01" />');

0 commit comments

Comments
 (0)