diff --git a/dist/date.js b/dist/date.js index 0993ddb..a60c774 100644 --- a/dist/date.js +++ b/dist/date.js @@ -82,6 +82,30 @@ return /******/ (function(modules) { // webpackBootstrap dateToString: dateToString }; + //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207 + function timezoneToOffset(timezone, fallback) { + timezone = timezone.replace(/:/g, ''); + var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; + return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; + } + + function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; + } + + function convertTimezoneToLocal(date, timezone, reverse) { + reverse = reverse ? -1 : 1; + var dateTimezoneOffset = date.getTimezoneOffset(); + var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); + } + + function doTZ(date, timezone, reverse) { + return timezone ? convertTimezoneToLocal(date, timezone, reverse) : date; + } + function dateToString(uiDateFormat, value) { var dateFormat = uiDateFormat || uiDateFormatConfig; if (value) { @@ -100,25 +124,25 @@ return /******/ (function(modules) { // webpackBootstrap return null; } - function stringToDate(dateFormat, valueToParse) { + function stringToDate(dateFormat, valueToParse, timezone) { dateFormat = dateFormat || uiDateFormatConfig; if (_angular2.default.isDate(valueToParse) && !isNaN(valueToParse)) { - return valueToParse; + return doTZ(valueToParse, timezone); } if (_angular2.default.isString(valueToParse)) { if (dateFormat) { - return _jquery2.default.datepicker.parseDate(dateFormat, valueToParse); + return doTZ(_jquery2.default.datepicker.parseDate(dateFormat, valueToParse), timezone); } var isoDate = new Date(valueToParse); - return isNaN(isoDate.getTime()) ? null : isoDate; + return isNaN(isoDate.getTime()) ? null : doTZ(isoDate, timezone); } if (_angular2.default.isNumber(valueToParse)) { // presumably timestamp to date object - return new Date(valueToParse); + return doTZ(new Date(valueToParse), timezone); } return null; @@ -137,6 +161,7 @@ return /******/ (function(modules) { // webpackBootstrap var initDateWidget = function initDateWidget() { var showing = false; var opts = getOptions(); + var timezone = controller ? controller.$options.getOption('timezone') : null; function setVal(forcedUpdate) { var keys = ['Hours', 'Minutes', 'Seconds', 'Milliseconds']; @@ -210,14 +235,14 @@ return /******/ (function(modules) { // webpackBootstrap }; controller.$parsers.push(function uiDateParser(valueToParse) { - return uiDateConverter.stringToDate(attrs.uiDateFormat, valueToParse); + return uiDateConverter.stringToDate(attrs.uiDateFormat, valueToParse, timezone); }); // Update the date picker when the model changes controller.$render = function () { // Force a render to override whatever is in the input text box if (_angular2.default.isDate(controller.$modelValue) === false && _angular2.default.isString(controller.$modelValue)) { - controller.$modelValue = uiDateConverter.stringToDate(attrs.uiDateFormat, controller.$modelValue); + controller.$modelValue = uiDateConverter.stringToDate(attrs.uiDateFormat, controller.$modelValue, timezone); } $element.datepicker('setDate', controller.$modelValue); }; diff --git a/dist/date.js.map b/dist/date.js.map index 1c3b4c7..23c0a93 100644 --- a/dist/date.js.map +++ b/dist/date.js.map @@ -1 +1 @@ -{"version":3,"file":"date.js","sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap 2d883be9d8db580d258e","webpack:///./src/date.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"jquery\"), require(\"angular\"), require(\"jquery-ui/datepicker\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"jquery\", \"angular\", \"jquery-ui/datepicker\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"angularUiDate\"] = factory(require(\"jquery\"), require(\"angular\"), require(\"jquery-ui/datepicker\"));\n\telse\n\t\troot[\"angularUiDate\"] = factory(root[\"jQuery\"], root[\"angular\"], root[\"jquery-ui/datepicker\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_3__) {\nreturn \n\n\n/** WEBPACK FOOTER **\n ** webpack/universalModuleDefinition\n **/"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"assets\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 2d883be9d8db580d258e\n **/","'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _jquery = require('jquery');\n\nvar _jquery2 = _interopRequireDefault(_jquery);\n\nvar _angular = require('angular');\n\nvar _angular2 = _interopRequireDefault(_angular);\n\nvar _datepicker = require('jquery-ui/datepicker');\n\nvar _datepicker2 = _interopRequireDefault(_datepicker);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n// sets up jQuery with the datepicker plugin\n\nexports.default = _angular2.default.module('ui.date', []).constant('uiDateConfig', {}).constant('uiDateFormatConfig', '').factory('uiDateConverter', ['uiDateFormatConfig', function (uiDateFormatConfig) {\n return {\n stringToDate: stringToDate,\n dateToString: dateToString\n };\n\n function dateToString(uiDateFormat, value) {\n var dateFormat = uiDateFormat || uiDateFormatConfig;\n if (value) {\n if (dateFormat) {\n try {\n return _jquery2.default.datepicker.formatDate(dateFormat, value);\n } catch (formatException) {\n return undefined;\n }\n }\n\n if (value.toISOString) {\n return value.toISOString();\n }\n }\n return null;\n }\n\n function stringToDate(dateFormat, valueToParse) {\n dateFormat = dateFormat || uiDateFormatConfig;\n\n if (_angular2.default.isDate(valueToParse) && !isNaN(valueToParse)) {\n return valueToParse;\n }\n\n if (_angular2.default.isString(valueToParse)) {\n if (dateFormat) {\n return _jquery2.default.datepicker.parseDate(dateFormat, valueToParse);\n }\n\n var isoDate = new Date(valueToParse);\n return isNaN(isoDate.getTime()) ? null : isoDate;\n }\n\n if (_angular2.default.isNumber(valueToParse)) {\n // presumably timestamp to date object\n return new Date(valueToParse);\n }\n\n return null;\n }\n}]).directive('uiDate', ['uiDateConfig', 'uiDateConverter', function uiDateDirective(uiDateConfig, uiDateConverter) {\n\n return {\n require: '?ngModel',\n link: function link(scope, element, attrs, controller) {\n\n var $element = (0, _jquery2.default)(element);\n\n var getOptions = function getOptions() {\n return _angular2.default.extend({}, uiDateConfig, scope.$eval(attrs.uiDate));\n };\n var initDateWidget = function initDateWidget() {\n var showing = false;\n var opts = getOptions();\n\n function setVal(forcedUpdate) {\n var keys = ['Hours', 'Minutes', 'Seconds', 'Milliseconds'];\n var isDate = _angular2.default.isDate(controller.$modelValue);\n var preserve = {};\n\n if (!forcedUpdate && isDate && controller.$modelValue.toDateString() === $element.datepicker('getDate').toDateString()) {\n return;\n }\n\n if (isDate) {\n _angular2.default.forEach(keys, function (key) {\n preserve[key] = controller.$modelValue['get' + key]();\n });\n }\n\n var newViewValue = $element.datepicker('getDate');\n\n if (isDate) {\n _angular2.default.forEach(keys, function (key) {\n newViewValue['set' + key](preserve[key]);\n });\n }\n\n controller.$setViewValue(newViewValue);\n }\n\n // If we have a controller (i.e. ngModelController) then wire it up\n if (controller) {\n // Set the view value in a $apply block when users selects\n // (calling directive user's function too if provided)\n var _onSelect = opts.onSelect || _angular2.default.noop;\n opts.onSelect = function (value, picker) {\n scope.$apply(function () {\n showing = true;\n setVal();\n $element.blur();\n _onSelect(value, picker, $element);\n });\n };\n\n var _beforeShow = opts.beforeShow || _angular2.default.noop;\n opts.beforeShow = function (input, picker) {\n showing = true;\n _beforeShow(input, picker, $element);\n };\n\n var _onClose = opts.onClose || _angular2.default.noop;\n opts.onClose = function (value, picker) {\n showing = false;\n _onClose(value, picker, $element);\n };\n\n element.on('focus', function (focusEvent) {\n if (attrs.readonly) {\n focusEvent.stopImmediatePropagation();\n }\n });\n\n $element.off('blur.datepicker').on('blur.datepicker', function () {\n if (!showing) {\n scope.$apply(function () {\n $element.datepicker('setDate', $element.datepicker('getDate'));\n setVal();\n });\n }\n });\n\n controller.$validators.uiDateValidator = function uiDateValidator(modelValue, viewValue) {\n return viewValue === null || viewValue === '' || _angular2.default.isDate(uiDateConverter.stringToDate(attrs.uiDateFormat, viewValue));\n };\n\n controller.$parsers.push(function uiDateParser(valueToParse) {\n return uiDateConverter.stringToDate(attrs.uiDateFormat, valueToParse);\n });\n\n // Update the date picker when the model changes\n controller.$render = function () {\n // Force a render to override whatever is in the input text box\n if (_angular2.default.isDate(controller.$modelValue) === false && _angular2.default.isString(controller.$modelValue)) {\n controller.$modelValue = uiDateConverter.stringToDate(attrs.uiDateFormat, controller.$modelValue);\n }\n $element.datepicker('setDate', controller.$modelValue);\n };\n }\n // Check if the $element already has a datepicker.\n //\n\n if ($element.data('datepicker')) {\n // Updates the datepicker options\n $element.datepicker('option', opts);\n $element.datepicker('refresh');\n } else {\n // Creates the new datepicker widget\n $element.datepicker(opts);\n\n // Cleanup on destroy, prevent memory leaking\n $element.on('$destroy', function () {\n $element.datepicker('hide');\n $element.datepicker('destroy');\n });\n }\n\n if (controller) {\n controller.$render();\n // Update the model with the value from the datepicker after parsed\n setVal(true);\n }\n };\n\n // Watch for changes to the directives options\n scope.$watch(getOptions, initDateWidget, true);\n }\n };\n}]).directive('uiDateFormat', ['uiDateConverter', function (uiDateConverter) {\n return {\n require: 'ngModel',\n link: function link(scope, element, attrs, modelCtrl) {\n var dateFormat = attrs.uiDateFormat;\n\n // Use the datepicker with the attribute value as the dateFormat string to convert to and from a string\n modelCtrl.$formatters.unshift(function (value) {\n return uiDateConverter.stringToDate(dateFormat, value);\n });\n\n modelCtrl.$parsers.push(function (value) {\n return uiDateConverter.dateToString(dateFormat, value);\n });\n }\n };\n}]);\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./src/date.js\n ** module id = 0\n ** module chunks = 0\n **/"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACtCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;","sourceRoot":""} \ No newline at end of file +{"version":3,"file":"date.js","sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap 84f149d5c6fc85eedd54","webpack:///./src/date.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"jquery\"), require(\"angular\"), require(\"jquery-ui/datepicker\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"jquery\", \"angular\", \"jquery-ui/datepicker\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"angularUiDate\"] = factory(require(\"jquery\"), require(\"angular\"), require(\"jquery-ui/datepicker\"));\n\telse\n\t\troot[\"angularUiDate\"] = factory(root[\"jQuery\"], root[\"angular\"], root[\"jquery-ui/datepicker\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_3__) {\nreturn \n\n\n/** WEBPACK FOOTER **\n ** webpack/universalModuleDefinition\n **/"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"assets\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 84f149d5c6fc85eedd54\n **/","'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _jquery = require('jquery');\n\nvar _jquery2 = _interopRequireDefault(_jquery);\n\nvar _angular = require('angular');\n\nvar _angular2 = _interopRequireDefault(_angular);\n\nvar _datepicker = require('jquery-ui/datepicker');\n\nvar _datepicker2 = _interopRequireDefault(_datepicker);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n// sets up jQuery with the datepicker plugin\n\nexports.default = _angular2.default.module('ui.date', []).constant('uiDateConfig', {}).constant('uiDateFormatConfig', '').factory('uiDateConverter', ['uiDateFormatConfig', function (uiDateFormatConfig) {\n return {\n stringToDate: stringToDate,\n dateToString: dateToString\n };\n\n //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207\n function timezoneToOffset(timezone, fallback) {\n timezone = timezone.replace(/:/g, '');\n var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;\n return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;\n }\n\n function addDateMinutes(date, minutes) {\n date = new Date(date.getTime());\n date.setMinutes(date.getMinutes() + minutes);\n return date;\n }\n\n function convertTimezoneToLocal(date, timezone, reverse) {\n reverse = reverse ? -1 : 1;\n var dateTimezoneOffset = date.getTimezoneOffset();\n var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);\n return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));\n }\n\n function doTZ(date, timezone, reverse) {\n return timezone ? convertTimezoneToLocal(date, timezone, reverse) : date;\n }\n\n function dateToString(uiDateFormat, value) {\n var dateFormat = uiDateFormat || uiDateFormatConfig;\n if (value) {\n if (dateFormat) {\n try {\n return _jquery2.default.datepicker.formatDate(dateFormat, value);\n } catch (formatException) {\n return undefined;\n }\n }\n\n if (value.toISOString) {\n return value.toISOString();\n }\n }\n return null;\n }\n\n function stringToDate(dateFormat, valueToParse, timezone) {\n dateFormat = dateFormat || uiDateFormatConfig;\n\n if (_angular2.default.isDate(valueToParse) && !isNaN(valueToParse)) {\n return doTZ(valueToParse, timezone);\n }\n\n if (_angular2.default.isString(valueToParse)) {\n if (dateFormat) {\n return doTZ(_jquery2.default.datepicker.parseDate(dateFormat, valueToParse), timezone);\n }\n\n var isoDate = new Date(valueToParse);\n return isNaN(isoDate.getTime()) ? null : doTZ(isoDate, timezone);\n }\n\n if (_angular2.default.isNumber(valueToParse)) {\n // presumably timestamp to date object\n return doTZ(new Date(valueToParse), timezone);\n }\n\n return null;\n }\n}]).directive('uiDate', ['uiDateConfig', 'uiDateConverter', function uiDateDirective(uiDateConfig, uiDateConverter) {\n\n return {\n require: '?ngModel',\n link: function link(scope, element, attrs, controller) {\n\n var $element = (0, _jquery2.default)(element);\n\n var getOptions = function getOptions() {\n return _angular2.default.extend({}, uiDateConfig, scope.$eval(attrs.uiDate));\n };\n var initDateWidget = function initDateWidget() {\n var showing = false;\n var opts = getOptions();\n var timezone = controller ? controller.$options.getOption('timezone') : null;\n\n function setVal(forcedUpdate) {\n var keys = ['Hours', 'Minutes', 'Seconds', 'Milliseconds'];\n var isDate = _angular2.default.isDate(controller.$modelValue);\n var preserve = {};\n\n if (!forcedUpdate && isDate && controller.$modelValue.toDateString() === $element.datepicker('getDate').toDateString()) {\n return;\n }\n\n if (isDate) {\n _angular2.default.forEach(keys, function (key) {\n preserve[key] = controller.$modelValue['get' + key]();\n });\n }\n\n var newViewValue = $element.datepicker('getDate');\n\n if (isDate) {\n _angular2.default.forEach(keys, function (key) {\n newViewValue['set' + key](preserve[key]);\n });\n }\n\n controller.$setViewValue(newViewValue);\n }\n\n // If we have a controller (i.e. ngModelController) then wire it up\n if (controller) {\n // Set the view value in a $apply block when users selects\n // (calling directive user's function too if provided)\n var _onSelect = opts.onSelect || _angular2.default.noop;\n opts.onSelect = function (value, picker) {\n scope.$apply(function () {\n showing = true;\n setVal();\n $element.blur();\n _onSelect(value, picker, $element);\n });\n };\n\n var _beforeShow = opts.beforeShow || _angular2.default.noop;\n opts.beforeShow = function (input, picker) {\n showing = true;\n _beforeShow(input, picker, $element);\n };\n\n var _onClose = opts.onClose || _angular2.default.noop;\n opts.onClose = function (value, picker) {\n showing = false;\n _onClose(value, picker, $element);\n };\n\n element.on('focus', function (focusEvent) {\n if (attrs.readonly) {\n focusEvent.stopImmediatePropagation();\n }\n });\n\n $element.off('blur.datepicker').on('blur.datepicker', function () {\n if (!showing) {\n scope.$apply(function () {\n $element.datepicker('setDate', $element.datepicker('getDate'));\n setVal();\n });\n }\n });\n\n controller.$validators.uiDateValidator = function uiDateValidator(modelValue, viewValue) {\n return viewValue === null || viewValue === '' || _angular2.default.isDate(uiDateConverter.stringToDate(attrs.uiDateFormat, viewValue));\n };\n\n controller.$parsers.push(function uiDateParser(valueToParse) {\n return uiDateConverter.stringToDate(attrs.uiDateFormat, valueToParse, timezone);\n });\n\n // Update the date picker when the model changes\n controller.$render = function () {\n // Force a render to override whatever is in the input text box\n if (_angular2.default.isDate(controller.$modelValue) === false && _angular2.default.isString(controller.$modelValue)) {\n controller.$modelValue = uiDateConverter.stringToDate(attrs.uiDateFormat, controller.$modelValue, timezone);\n }\n $element.datepicker('setDate', controller.$modelValue);\n };\n }\n // Check if the $element already has a datepicker.\n //\n\n if ($element.data('datepicker')) {\n // Updates the datepicker options\n $element.datepicker('option', opts);\n $element.datepicker('refresh');\n } else {\n // Creates the new datepicker widget\n $element.datepicker(opts);\n\n // Cleanup on destroy, prevent memory leaking\n $element.on('$destroy', function () {\n $element.datepicker('hide');\n $element.datepicker('destroy');\n });\n }\n\n if (controller) {\n controller.$render();\n // Update the model with the value from the datepicker after parsed\n setVal(true);\n }\n };\n\n // Watch for changes to the directives options\n scope.$watch(getOptions, initDateWidget, true);\n }\n };\n}]).directive('uiDateFormat', ['uiDateConverter', function (uiDateConverter) {\n return {\n require: 'ngModel',\n link: function link(scope, element, attrs, modelCtrl) {\n var dateFormat = attrs.uiDateFormat;\n\n // Use the datepicker with the attribute value as the dateFormat string to convert to and from a string\n modelCtrl.$formatters.unshift(function (value) {\n return uiDateConverter.stringToDate(dateFormat, value);\n });\n\n modelCtrl.$parsers.push(function (value) {\n return uiDateConverter.dateToString(dateFormat, value);\n });\n }\n };\n}]);\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./src/date.js\n ** module id = 0\n ** module chunks = 0\n **/"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACtCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;","sourceRoot":""} \ No newline at end of file diff --git a/src/date.js b/src/date.js index c2457c0..f70d2a8 100644 --- a/src/date.js +++ b/src/date.js @@ -11,6 +11,30 @@ export default angular.module('ui.date', []) dateToString: dateToString, }; + //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207 + function timezoneToOffset(timezone, fallback) { + timezone = timezone.replace(/:/g, ''); + var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; + return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; + } + + function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; + } + + function convertTimezoneToLocal(date, timezone, reverse) { + reverse = reverse ? -1 : 1; + var dateTimezoneOffset = date.getTimezoneOffset(); + var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); + } + + function doTZ(date,timezone,reverse) { + return timezone ? convertTimezoneToLocal(date, timezone, reverse) : date; + } + function dateToString(uiDateFormat, value) { var dateFormat = uiDateFormat || uiDateFormatConfig; if (value) { @@ -30,26 +54,26 @@ export default angular.module('ui.date', []) return null; } - function stringToDate(dateFormat, valueToParse) { + function stringToDate(dateFormat, valueToParse, timezone) { dateFormat = dateFormat || uiDateFormatConfig; if (angular.isDate(valueToParse) && !isNaN(valueToParse)) { - return valueToParse; + return doTZ(valueToParse, timezone); } if (angular.isString(valueToParse)) { if (dateFormat) { - return jQuery.datepicker.parseDate(dateFormat, valueToParse); + return doTZ(jQuery.datepicker.parseDate(dateFormat, valueToParse), timezone); } var isoDate = new Date(valueToParse); - return isNaN(isoDate.getTime()) ? null : isoDate; + return isNaN(isoDate.getTime()) ? null : doTZ(isoDate, timezone); } if (angular.isNumber(valueToParse)) { // presumably timestamp to date object - return new Date(valueToParse); + return doTZ(new Date(valueToParse), timezone); } return null; @@ -70,6 +94,7 @@ export default angular.module('ui.date', []) var initDateWidget = function() { var showing = false; var opts = getOptions(); + var timezone = controller ? controller.$options.getOption('timezone') : null; function setVal(forcedUpdate) { var keys = ['Hours', 'Minutes', 'Seconds', 'Milliseconds']; @@ -145,14 +170,14 @@ export default angular.module('ui.date', []) }; controller.$parsers.push(function uiDateParser(valueToParse) { - return uiDateConverter.stringToDate(attrs.uiDateFormat, valueToParse); + return uiDateConverter.stringToDate(attrs.uiDateFormat, valueToParse, timezone); }); // Update the date picker when the model changes controller.$render = function() { // Force a render to override whatever is in the input text box if (angular.isDate(controller.$modelValue) === false && angular.isString(controller.$modelValue)) { - controller.$modelValue = uiDateConverter.stringToDate(attrs.uiDateFormat, controller.$modelValue); + controller.$modelValue = uiDateConverter.stringToDate(attrs.uiDateFormat, controller.$modelValue, timezone); } $element.datepicker('setDate', controller.$modelValue); }; diff --git a/src/date.spec.js b/src/date.spec.js index 1f575c1..9a771f9 100644 --- a/src/date.spec.js +++ b/src/date.spec.js @@ -163,6 +163,26 @@ describe('uiDate', function() { }); }); + + it('should observe timezone option', function() { + inject(function($compile, $rootScope) { + var zone, expected; + $rootScope.x = new Date(2012, 9, 12, 12); + zone = $rootScope.x.getTimezoneOffset(); + if (zone === 0) { + zone = '+0400'; + expected = 8; + } else { + expected = 12 - zone / 60; + zone = 'UTC'; + } + var element = $compile('')($rootScope); + $rootScope.$apply(); + selectDate(element, $rootScope.x); + expect($rootScope.x.getHours()).toBe(expected); + }); + }); + it('should convert a model string to a Date immediately after applied', function() { inject(function($compile, $rootScope) { $rootScope.x = '2015-09-13';