From cc6fc199f5abaacdf781aa03634337d776eb0fc9 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Mon, 25 Aug 2014 11:10:59 -0700 Subject: [PATCH] feat(input): allow to define the timezone for parsing dates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/ng/directive/input.js | 27 +++++++++++++-- test/ng/directive/inputSpec.js | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 45f41bd3..b6a267b2 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -113,6 +113,9 @@ var inputType = { * modern browsers do not yet support this input type, it is important to provide cues to users on the * expected input format via a placeholder or label. The model must always be a Date object. * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @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 = { * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 * local datetime format (yyyy-MM-ddTHH:mm), for example: `2010-12-28T14:57`. The model must be a Date object. * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @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 = { * local time format (HH:mm), for example: `14:57`. Model must be a Date object. This binding will always output a * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm)`. * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @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 = { * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 * week format (yyyy-W##), for example: `2013-W02`. The model must always be a Date object. * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @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 = { * month format (yyyy-MM), for example: `2009-01`. The model must always be a Date object. In the event the model is * not set to the first of the month, the first of that model's month is assumed. * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @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) { function createDateInputType(type, regexp, parseDate, format) { return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); + var timezone = ctrl && ctrl.$options && ctrl.$options.timezone; ctrl.$parsers.push(function(value) { if(ctrl.$isEmpty(value)) { @@ -1070,7 +1086,11 @@ function createDateInputType(type, regexp, parseDate, format) { if(regexp.test(value)) { ctrl.$setValidity(type, true); - return parseDate(value); + var parsedDate = parseDate(value); + if (timezone === 'UTC') { + parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset()); + } + return parsedDate; } ctrl.$setValidity(type, false); @@ -1079,7 +1099,7 @@ function createDateInputType(type, regexp, parseDate, format) { ctrl.$formatters.push(function(value) { if(isDate(value)) { - return $filter('date')(value, format); + return $filter('date')(value, format, timezone); } return ''; }); @@ -2604,6 +2624,9 @@ var ngValueDirective = function() { * `ngModelOptions="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"` * - `getterSetter`: boolean value which determines whether or not to treat functions bound to `ngModel` as getters/setters. + * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for + * ``, ``, ... . Right now, the only supported value is `'UTC'`, + * otherwise the default timezone of the browser will be used. * * @example diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 37b35dfc..a35b85b0 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -1627,6 +1627,18 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + it('should use UTC if specified in the options', function() { + compileInput(''); + + changeInputValueTo('2013-07'); + expect(+scope.value).toBe(Date.UTC(2013, 6, 1)); + + scope.$apply(function() { + scope.value = new Date(Date.UTC(2014, 6, 1)); + }); + expect(inputElm.val()).toBe('2014-07'); + }); + describe('min', function (){ beforeEach(function (){ @@ -1746,6 +1758,18 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + it('should use UTC if specified in the options', function() { + compileInput(''); + + changeInputValueTo('2013-W03'); + expect(+scope.value).toBe(Date.UTC(2013, 0, 17)); + + scope.$apply(function() { + scope.value = new Date(Date.UTC(2014, 0, 17)); + }); + expect(inputElm.val()).toBe('2014-W03'); + }); + describe('min', function (){ beforeEach(function (){ compileInput(''); @@ -1863,6 +1887,18 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + it('should use UTC if specified in the options', function() { + compileInput(''); + + changeInputValueTo('2000-01-01T01:02'); + expect(+scope.value).toBe(Date.UTC(2000, 0, 1, 1, 2)); + + scope.$apply(function() { + scope.value = new Date(Date.UTC(2001, 0, 1, 1, 2)); + }); + expect(inputElm.val()).toBe('2001-01-01T01:02'); + }); + describe('min', function (){ beforeEach(function (){ compileInput(''); @@ -2008,6 +2044,18 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + it('should use UTC if specified in the options', function() { + compileInput(''); + + changeInputValueTo('23:02'); + expect(+scope.value).toBe(Date.UTC(1970, 0, 1, 23, 2)); + + scope.$apply(function() { + scope.value = new Date(Date.UTC(1971, 0, 1, 23, 2)); + }); + expect(inputElm.val()).toBe('23:02'); + }); + describe('min', function (){ beforeEach(function (){ compileInput(''); @@ -2153,6 +2201,18 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + it('should use UTC if specified in the options', function() { + compileInput(''); + + changeInputValueTo('2000-01-01'); + expect(+scope.value).toBe(Date.UTC(2000, 0, 1)); + + scope.$apply(function() { + scope.value = new Date(Date.UTC(2001, 0, 1)); + }); + expect(inputElm.val()).toBe('2001-01-01'); + }); + describe('min', function (){ beforeEach(function (){ compileInput('');