AMBARI-13451. Timezone select list needs to be user friendly (onechiporenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/34082a5a Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/34082a5a Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/34082a5a Branch: refs/heads/branch-dev-patch-upgrade Commit: 34082a5a7aa90f4c937b46c133629b26aba5e909 Parents: 2fbbadf Author: Oleg Nechiporenko <[email protected]> Authored: Fri Oct 16 15:51:44 2015 +0300 Committer: Oleg Nechiporenko <[email protected]> Committed: Fri Oct 16 15:51:44 2015 +0300 ---------------------------------------------------------------------- ambari-web/app/assets/test/tests.js | 3 +- .../global/user_settings_controller.js | 166 +++++----- .../charts/heatmap_metrics/heatmap_metric.js | 4 +- .../app/mappers/service_metrics_mapper.js | 4 +- .../app/models/alerts/alert_definition.js | 2 +- ambari-web/app/models/alerts/alert_instance.js | 2 +- .../models/configs/service_config_version.js | 2 +- ambari-web/app/templates/common/settings.hbs | 4 +- ambari-web/app/utils/data_manipulation.js | 2 +- ambari-web/app/utils/date.js | 242 --------------- ambari-web/app/utils/date/date.js | 215 +++++++++++++ ambari-web/app/utils/date/timezone.js | 226 ++++++++++++++ ambari-web/app/utils/helper.js | 4 +- ambari-web/app/utils/host_progress_popup.js | 2 +- .../app/views/common/chart/linear_time.js | 2 +- .../app/views/main/alert_definitions_view.js | 2 +- ambari-web/app/views/main/alerts.js | 2 +- .../views/main/charts/heatmap/heatmap_host.js | 4 +- .../main/dashboard/widgets/links_widget.js | 2 +- .../widgets/resource_manager_uptime.js | 2 +- .../views/main/dashboard/widgets/text_widget.js | 2 +- .../dashboard/widgets/uptime_text_widget.js | 2 +- ambari-web/app/views/main/host.js | 2 +- ambari-web/app/views/main/host/configs.js | 2 +- ambari-web/app/views/main/host/details.js | 2 +- .../app/views/main/service/services/flume.js | 2 +- .../app/views/main/service/services/hbase.js | 2 +- .../app/views/main/service/services/hdfs.js | 2 +- .../app/views/main/service/services/storm.js | 2 +- .../app/views/main/service/services/yarn.js | 2 +- .../views/wizard/step9/hostLogPopupBody_view.js | 4 +- ambari-web/app/views/wizard/step9_view.js | 2 +- .../heatmap_metrics/heatmap_metric_test.js | 2 +- ambari-web/test/utils/date/date_test.js | 185 ++++++++++++ ambari-web/test/utils/date/timezone_test.js | 175 +++++++++++ ambari-web/test/utils/date_test.js | 185 ------------ .../test/views/common/chart/linear_time_test.js | 2 +- .../moment-timezone-with-data-2010-2020.js | 299 +------------------ 38 files changed, 911 insertions(+), 855 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/assets/test/tests.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js index b59f91f..f79658c 100644 --- a/ambari-web/app/assets/test/tests.js +++ b/ambari-web/app/assets/test/tests.js @@ -163,7 +163,8 @@ var files = [ 'test/utils/batch_scheduled_requests_test', 'test/utils/blueprint_test', 'test/utils/config_test', - 'test/utils/date_test', + 'test/utils/date/date_test', + 'test/utils/date/timezone_test', 'test/utils/data_manipulation_test', 'test/utils/config_test', 'test/utils/form_field_test', http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/controllers/global/user_settings_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/global/user_settings_controller.js b/ambari-web/app/controllers/global/user_settings_controller.js index ef53d81..4ad7786 100644 --- a/ambari-web/app/controllers/global/user_settings_controller.js +++ b/ambari-web/app/controllers/global/user_settings_controller.js @@ -18,7 +18,7 @@ var App = require('app'); -var dateUtils = require('utils/date'); +var timezoneUtils = require('utils/date/timezone'); /** * Controller for user settings @@ -49,47 +49,11 @@ App.UserSettingsController = Em.Controller.extend(App.UserPref, { }, timezone: { name: prefix + 'timezone-' + loginName, - defaultValue: dateUtils.detectUserTimezone() + defaultValue: timezoneUtils.detectUserTimezone() } }; }.property('App.router.loginName'), - init: function () { - this.set('timezonesFormatted', this._parseTimezones()); - this._super(); - }, - - /** - * - * @private - * @method _parseTimezones - * @return {{utcOffset: number, label: string, value: string}[]} - */ - _parseTimezones: function () { - return dateUtils.getAllTimezoneNames().map(function (timeZoneName) { - var zone = moment(new Date()).tz(timeZoneName); - var offset = zone.format('Z'); - return { - utcOffset: zone.utcOffset(), - label: '(UTC' + offset + ') ' + timeZoneName, - value: timeZoneName - }; - }).sort(function (zoneA, zoneB) { - if (zoneA.utcOffset === zoneB.utcOffset) { - if (zoneA.value === zoneB.value) { - return 0; - } - return zoneA.value < zoneB.value ? -1 : 1; - } - else { - if(zoneA.utcOffset === zoneB.utcOffset) { - return 0; - } - return zoneA.utcOffset < zoneB.utcOffset ? -1 : 1; - } - }); - }, - /** * Load some user's setting from the persist * If <code>persistKey</code> is not provided, all settings are loaded @@ -167,7 +131,8 @@ App.UserSettingsController = Em.Controller.extend(App.UserPref, { * @returns {*} */ postUserPref: function (key, value) { - return this._super(this.get('userSettingsKeys.' + key + '.name'), value); + var k = key.startsWith('userSettingsKeys.') ? key : 'userSettingsKeys.' + key + '.name'; + return this._super(this.get(k), value); }, /** @@ -179,7 +144,9 @@ App.UserSettingsController = Em.Controller.extend(App.UserPref, { }, /** - * Open popup with user settings + * Check if popup may be opened (based on <code>upgrade_ADMIN</code>) + * Open popup with user settings after settings-request is complete + * * @method showSettingsPopup */ showSettingsPopup: function() { @@ -187,66 +154,73 @@ App.UserSettingsController = Em.Controller.extend(App.UserPref, { if (!App.isAccessible('upgrade_ADMIN')) { return; } - var self = this; + this.dataLoading().done(this._showSettingsPopup.bind(this)); + }, + + /** + * Show popup with settings for user + * Don't call this method directly! Use <code>showSettingsPopup</code> + * + * @param {object} response + * @returns {App.ModalPopup} + * @method _showSettingsPopup + * @private + */ + _showSettingsPopup: function (response) { var curValue = null; + var self = this; var keys = this.get('userSettingsKeys'); - var timezonesFormatted = this.get('timezonesFormatted'); - - this.dataLoading().done(function (response) { - var initValue = JSON.parse(response[keys.show_bg.name]); - var initTimezone = timezonesFormatted.findProperty('value', JSON.parse(response[keys.timezone.name])); - return App.ModalPopup.show({ - - header: Em.I18n.t('common.userSettings'), - - bodyClass: Em.View.extend({ - - templateName: require('templates/common/settings'), - - isNotShowBgChecked: !initValue, - - updateValue: function () { - curValue = !this.get('isNotShowBgChecked'); - }.observes('isNotShowBgChecked'), - - /** - * @type {{label: string, value: string}} - */ - timezonesList: timezonesFormatted - - }), - - /** - * @type {string} - */ - selectedTimezone: initTimezone, - - primary: Em.I18n.t('common.save'), - - onPrimary: function() { - if (Em.isNone(curValue)) { - curValue = initValue; - } - if (!App.get('testMode')) { - self.postUserPref('show_bg', curValue); - self.postUserPref('timezone', this.get('selectedTimezone.value')); - } - if (this.needsPageRefresh()) { - location.reload(); - } - this._super(); - }, - - /** - * Determines if page should be refreshed after user click "Save" - * @returns {boolean} - */ - needsPageRefresh: function () { - return initTimezone !== this.get('selectedTimezone'); + var timezonesFormatted = timezoneUtils.get('timezones'); + var initValue = JSON.parse(response[keys.show_bg.name]); + var initTimezone = timezonesFormatted.findProperty('value', JSON.parse(response[keys.timezone.name])); + return App.ModalPopup.show({ + + header: Em.I18n.t('common.userSettings'), + + bodyClass: Em.View.extend({ + + templateName: require('templates/common/settings'), + + isNotShowBgChecked: !initValue, + + updateValue: function () { + curValue = !this.get('isNotShowBgChecked'); + }.observes('isNotShowBgChecked'), + + timezonesList: timezonesFormatted + + }), + + /** + * @type {string} + */ + selectedTimezone: initTimezone, + + primary: Em.I18n.t('common.save'), + + onPrimary: function() { + if (Em.isNone(curValue)) { + curValue = initValue; + } + if (!App.get('testMode')) { + self.postUserPref('show_bg', curValue); + self.postUserPref('timezone', this.get('selectedTimezone.value')); + } + if (this.needsPageRefresh()) { + location.reload(); } + this._super(); + }, - }) - }); + /** + * Determines if page should be refreshed after user click "Save" + * @returns {boolean} + */ + needsPageRefresh: function () { + return initTimezone !== this.get('selectedTimezone'); + } + + }) } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/controllers/main/charts/heatmap_metrics/heatmap_metric.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/charts/heatmap_metrics/heatmap_metric.js b/ambari-web/app/controllers/main/charts/heatmap_metrics/heatmap_metric.js index 58874ef..630c5c2 100644 --- a/ambari-web/app/controllers/main/charts/heatmap_metrics/heatmap_metric.js +++ b/ambari-web/app/controllers/main/charts/heatmap_metrics/heatmap_metric.js @@ -16,7 +16,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); var heatmap = require('utils/heatmap'); @@ -256,4 +256,4 @@ App.MainChartHeatmapMetric = Em.Object.extend({ } return num; } -}); \ No newline at end of file +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/mappers/service_metrics_mapper.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mappers/service_metrics_mapper.js b/ambari-web/app/mappers/service_metrics_mapper.js index 5910574..b68d28b 100644 --- a/ambari-web/app/mappers/service_metrics_mapper.js +++ b/ambari-web/app/mappers/service_metrics_mapper.js @@ -18,7 +18,7 @@ var App = require('app'); var misc = require('utils/misc'); var stringUtils = require('utils/string_utils'); -var dateUtils = require('utils/date'); +var dateUtils = require('utils/date/date'); var previousMasterComponentIds = []; App.serviceMetricsMapper = App.QuickDataMapper.create({ @@ -669,4 +669,4 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({ item.restApiComponent = restApiMetrics; return this.parseIt(item, finalConfig); } -}); \ No newline at end of file +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/models/alerts/alert_definition.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/models/alerts/alert_definition.js b/ambari-web/app/models/alerts/alert_definition.js index d1310ff..e33324b 100644 --- a/ambari-web/app/models/alerts/alert_definition.js +++ b/ambari-web/app/models/alerts/alert_definition.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var dateUtils = require('utils/date'); +var dateUtils = require('utils/date/date'); App.AlertDefinition = DS.Model.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/models/alerts/alert_instance.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/models/alerts/alert_instance.js b/ambari-web/app/models/alerts/alert_instance.js index 33df293..60a2723 100644 --- a/ambari-web/app/models/alerts/alert_instance.js +++ b/ambari-web/app/models/alerts/alert_instance.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var dateUtils = require('utils/date'); +var dateUtils = require('utils/date/date'); App.AlertInstance = DS.Model.extend({ id: DS.attr('number'), http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/models/configs/service_config_version.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/models/configs/service_config_version.js b/ambari-web/app/models/configs/service_config_version.js index fcaa45c..0f66f75 100644 --- a/ambari-web/app/models/configs/service_config_version.js +++ b/ambari-web/app/models/configs/service_config_version.js @@ -18,7 +18,7 @@ var App = require('app'); -var dateUtil = require('utils/date'); +var dateUtil = require('utils/date/date'); App.ServiceConfigVersion = DS.Model.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/templates/common/settings.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/common/settings.hbs b/ambari-web/app/templates/common/settings.hbs index 2254c17..226b264 100644 --- a/ambari-web/app/templates/common/settings.hbs +++ b/ambari-web/app/templates/common/settings.hbs @@ -32,11 +32,11 @@ {{t app.settings.selectTimezone}} {{view Em.Select contentBinding="view.timezonesList" - optionLabelPath="content.label" + optionLabelPath="content.value" optionValuePath="content.value" selectionBinding="view.parentView.selectedTimezone" class="group-select select-group-box" }} </label> </div> -</div> \ No newline at end of file +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/utils/data_manipulation.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/data_manipulation.js b/ambari-web/app/utils/data_manipulation.js index 09ef79e..29788ff 100644 --- a/ambari-web/app/utils/data_manipulation.js +++ b/ambari-web/app/utils/data_manipulation.js @@ -78,7 +78,7 @@ module.exports = { * </code> * @param {Object[]} collection array of objects * @param {String} key property name of each object to be grouped - * @returns {*} + * @returns {object} */ groupPropertyValues: function(collection, key) { var group = {}; http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/utils/date.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/date.js b/ambari-web/app/utils/date.js deleted file mode 100644 index 84b67f9..0000000 --- a/ambari-web/app/utils/date.js +++ /dev/null @@ -1,242 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var validator = require('utils/validator'); -var App = require('app'); - -module.exports = { - - /** - * List of monthes short names - * @type {string[]} - */ - dateMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], - - /** - * List of days short names - * @type {string[]} - */ - dateDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], - - /** - * Add leading zero - * - * @param {string} time - * @returns {string} - * @method dateFormatZeroFirst - */ - dateFormatZeroFirst: function (time) { - return (time < 10 ? '0' : '') + time; - }, - - /** - * Convert timestamp to date-string - * default format - 'DAY_OF_THE_WEEK, MONTH DAY, YEAR HOURS:MINUTES' - * - * @param {number} timestamp - * @param {bool} format - * @return {*} date - * @method dateFormat - */ - dateFormat: function (timestamp, format) { - if (!validator.isValidInt(timestamp)) { - return timestamp; - } - format = format || 'ddd, MMM DD, YYYY HH:mm'; - - return moment((new Date(timestamp))).format(format); - }, - - /** - * Convert timestamp to date-string 'DAY_OF_THE_WEEK MONTH DAY YEAR' - * - * @param {string} timestamp - * @return {string} - * @method dateFormatShort - */ - dateFormatShort: function (timestamp) { - if (!validator.isValidInt(timestamp)) { - return timestamp; - } - var format = 'ddd MMM DD YYYY'; - var date = moment((new Date(timestamp))).format(format); - var today = moment((new Date())).format(format); - if (date === today) { - return 'Today ' + (new Date(timestamp)).toLocaleTimeString(); - } - return date; - }, - - /** - * Convert starTimestamp to 'DAY_OF_THE_WEEK, MONTH DAY, YEAR HOURS:MINUTES', except for the case: year equals 1969 - * - * @param {string} startTimestamp - * @return {string} startTimeSummary - * @method startTime - */ - startTime: function (startTimestamp) { - if (!validator.isValidInt(startTimestamp)) { - return ''; - } - var startDate = new Date(startTimestamp); - var months = this.dateMonths; - var days = this.dateDays; - // generate start time - if (startDate.getFullYear() == 1969 || startTimestamp < 1) { - return 'Not started'; - } - var startTimeSummary = ''; - if (new Date(startTimestamp).setHours(0, 0, 0, 0) == new Date().setHours(0, 0, 0, 0)) { //today - startTimeSummary = 'Today ' + this.dateFormatZeroFirst(startDate.getHours()) + ':' + this.dateFormatZeroFirst(startDate.getMinutes()); - } else { - startTimeSummary = days[startDate.getDay()] + ' ' + months[startDate.getMonth()] + ' ' + - this.dateFormatZeroFirst(startDate.getDate()) + ' ' + startDate.getFullYear() + ' ' - + this.dateFormatZeroFirst(startDate.getHours()) + ':' + this.dateFormatZeroFirst(startDate.getMinutes()); - } - return startTimeSummary; - }, - - /** - * Provides the duration between the given start and end timestamp. If start time - * not valid, duration will be ''. If end time is not valid, duration will - * be till now, showing 'Lasted for xxx secs'. - * - * @param {string} startTimestamp - * @param {string} endTimestamp - * @return {string} durationSummary - * @method durationSummary - */ - durationSummary: function (startTimestamp, endTimestamp) { - // generate duration - var durationSummary = ''; - var startDate = new Date(startTimestamp); - var endDate = new Date(endTimestamp); - if (startDate.getFullYear() == 1969 || startTimestamp < 1) { - // not started - return Em.I18n.t('common.na'); - } - if (endDate.getFullYear() != 1969 && endTimestamp > 0) { - return '' + this.timingFormat(endTimestamp - startTimestamp, 1); //lasted for xx secs - } else { - // still running, duration till now - var time = (App.dateTime() - startTimestamp) < 0 ? 0 : (App.dateTime() - startTimestamp); - durationSummary = '' + this.timingFormat(time, 1); - } - return durationSummary; - }, - - /** - * Convert time in mseconds to - * 30 ms = 30 ms - * 300 ms = 300 ms - * 999 ms = 999 ms - * 1000 ms = 1.00 secs - * 3000 ms = 3.00 secs - * 35000 ms = 35.00 secs - * 350000 ms = 350.00 secs - * 999999 ms = 999.99 secs - * 1000000 ms = 16.66 mins - * 3500000 secs = 58.33 mins - * - * @param {number} time - * @param {bool} [zeroValid] for the case to show 0 when time is 0, not null - * @return {string|null} formatted date - * @method timingFormat - */ - timingFormat: function (time, /* optional */ zeroValid) { - var intTime = parseInt(time); - if (zeroValid && intTime == 0) { - return 0 + ' secs'; - } - if (!intTime) { - return null; - } - var timeStr = intTime.toString(); - var lengthOfNumber = timeStr.length; - var oneMinMs = 60000; - var oneHourMs = 3600000; - var oneDayMs = 86400000; - - if (lengthOfNumber < 4) { - return time + ' ms'; - } - if (lengthOfNumber < 7) { - time = (time / 1000).toFixed(2); - return time + ' secs'; - } - if (time < oneHourMs) { - time = (time / oneMinMs).toFixed(2); - return time + ' mins'; - } - if (time < oneDayMs) { - time = (time / oneHourMs).toFixed(2); - return time + ' hours'; - } - time = (time / oneDayMs).toFixed(2); - return time + ' days'; - }, - - /** - * Provides the duration between the given start and end time. If start time - * is not given, duration will be 0. If end time is not given, duration will - * be till now. - * - * @param {Number} startTime Start time from epoch - * @param {Number} endTime End time from epoch - * @return {Number} duration - * @method duration - */ - duration: function (startTime, endTime) { - var duration = 0; - if (startTime && startTime > 0) { - if (!endTime || endTime < 1) { - endTime = App.dateTime(); - } - duration = endTime - startTime; - } - return duration; - }, - - /** - * Load list of timezones from moment.tz - * Zones "Etc/*" are excluded - * @returns {object[]} - */ - getAllTimezoneNames: function () { - return moment.tz.names().filter(function (timeZoneName) { - return !timeZoneName.startsWith('Etc/'); - }); - }, - - /** - * Try detect user's timezone using timezoneOffset and moment.tz - * @returns {string} - */ - detectUserTimezone: function () { - var timezoneOffset = new Date().getTimezoneOffset(); - var timezoneNames = this.getAllTimezoneNames(); - for (var i = 0; i < timezoneNames.length; i++) { - var zone = moment.tz.zone(timezoneNames[i]); - if (zone.offsets.contains(timezoneOffset)) { - return timezoneNames[i]; - } - } - return ''; - } - -}; http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/utils/date/date.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/date/date.js b/ambari-web/app/utils/date/date.js new file mode 100644 index 0000000..c51f043 --- /dev/null +++ b/ambari-web/app/utils/date/date.js @@ -0,0 +1,215 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var validator = require('utils/validator'); +var App = require('app'); + +module.exports = { + + /** + * List of monthes short names + * @type {string[]} + */ + dateMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + + /** + * List of days short names + * @type {string[]} + */ + dateDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + + /** + * Add leading zero + * + * @param {string} time + * @returns {string} + * @method dateFormatZeroFirst + */ + dateFormatZeroFirst: function (time) { + return (time < 10 ? '0' : '') + time; + }, + + /** + * Convert timestamp to date-string + * default format - 'DAY_OF_THE_WEEK, MONTH DAY, YEAR HOURS:MINUTES' + * + * @param {number} timestamp + * @param {bool} format + * @return {*} date + * @method dateFormat + */ + dateFormat: function (timestamp, format) { + if (!validator.isValidInt(timestamp)) { + return timestamp; + } + format = format || 'ddd, MMM DD, YYYY HH:mm'; + + return moment((new Date(timestamp))).format(format); + }, + + /** + * Convert timestamp to date-string 'DAY_OF_THE_WEEK MONTH DAY YEAR' + * + * @param {string} timestamp + * @return {string} + * @method dateFormatShort + */ + dateFormatShort: function (timestamp) { + if (!validator.isValidInt(timestamp)) { + return timestamp; + } + var format = 'ddd MMM DD YYYY'; + var date = moment((new Date(timestamp))).format(format); + var today = moment((new Date())).format(format); + if (date === today) { + return 'Today ' + (new Date(timestamp)).toLocaleTimeString(); + } + return date; + }, + + /** + * Convert starTimestamp to 'DAY_OF_THE_WEEK, MONTH DAY, YEAR HOURS:MINUTES', except for the case: year equals 1969 + * + * @param {string} startTimestamp + * @return {string} startTimeSummary + * @method startTime + */ + startTime: function (startTimestamp) { + if (!validator.isValidInt(startTimestamp)) { + return ''; + } + var startDate = new Date(startTimestamp); + var months = this.dateMonths; + var days = this.dateDays; + // generate start time + if (startDate.getFullYear() == 1969 || startTimestamp < 1) { + return 'Not started'; + } + var startTimeSummary = ''; + if (new Date(startTimestamp).setHours(0, 0, 0, 0) == new Date().setHours(0, 0, 0, 0)) { //today + startTimeSummary = 'Today ' + this.dateFormatZeroFirst(startDate.getHours()) + ':' + this.dateFormatZeroFirst(startDate.getMinutes()); + } else { + startTimeSummary = days[startDate.getDay()] + ' ' + months[startDate.getMonth()] + ' ' + + this.dateFormatZeroFirst(startDate.getDate()) + ' ' + startDate.getFullYear() + ' ' + + this.dateFormatZeroFirst(startDate.getHours()) + ':' + this.dateFormatZeroFirst(startDate.getMinutes()); + } + return startTimeSummary; + }, + + /** + * Provides the duration between the given start and end timestamp. If start time + * not valid, duration will be ''. If end time is not valid, duration will + * be till now, showing 'Lasted for xxx secs'. + * + * @param {string} startTimestamp + * @param {string} endTimestamp + * @return {string} durationSummary + * @method durationSummary + */ + durationSummary: function (startTimestamp, endTimestamp) { + // generate duration + var durationSummary = ''; + var startDate = new Date(startTimestamp); + var endDate = new Date(endTimestamp); + if (startDate.getFullYear() == 1969 || startTimestamp < 1) { + // not started + return Em.I18n.t('common.na'); + } + if (endDate.getFullYear() != 1969 && endTimestamp > 0) { + return '' + this.timingFormat(endTimestamp - startTimestamp, 1); //lasted for xx secs + } else { + // still running, duration till now + var time = (App.dateTime() - startTimestamp) < 0 ? 0 : (App.dateTime() - startTimestamp); + durationSummary = '' + this.timingFormat(time, 1); + } + return durationSummary; + }, + + /** + * Convert time in mseconds to + * 30 ms = 30 ms + * 300 ms = 300 ms + * 999 ms = 999 ms + * 1000 ms = 1.00 secs + * 3000 ms = 3.00 secs + * 35000 ms = 35.00 secs + * 350000 ms = 350.00 secs + * 999999 ms = 999.99 secs + * 1000000 ms = 16.66 mins + * 3500000 secs = 58.33 mins + * + * @param {number} time + * @param {bool} [zeroValid] for the case to show 0 when time is 0, not null + * @return {string|null} formatted date + * @method timingFormat + */ + timingFormat: function (time, /* optional */ zeroValid) { + var intTime = parseInt(time); + if (zeroValid && intTime == 0) { + return 0 + ' secs'; + } + if (!intTime) { + return null; + } + var timeStr = intTime.toString(); + var lengthOfNumber = timeStr.length; + var oneMinMs = 60000; + var oneHourMs = 3600000; + var oneDayMs = 86400000; + + if (lengthOfNumber < 4) { + return time + ' ms'; + } + if (lengthOfNumber < 7) { + time = (time / 1000).toFixed(2); + return time + ' secs'; + } + if (time < oneHourMs) { + time = (time / oneMinMs).toFixed(2); + return time + ' mins'; + } + if (time < oneDayMs) { + time = (time / oneHourMs).toFixed(2); + return time + ' hours'; + } + time = (time / oneDayMs).toFixed(2); + return time + ' days'; + }, + + /** + * Provides the duration between the given start and end time. If start time + * is not given, duration will be 0. If end time is not given, duration will + * be till now. + * + * @param {Number} startTime Start time from epoch + * @param {Number} endTime End time from epoch + * @return {Number} duration + * @method duration + */ + duration: function (startTime, endTime) { + var duration = 0; + if (startTime && startTime > 0) { + if (!endTime || endTime < 1) { + endTime = App.dateTime(); + } + duration = endTime - startTime; + } + return duration; + } + +}; http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/utils/date/timezone.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/date/timezone.js b/ambari-web/app/utils/date/timezone.js new file mode 100644 index 0000000..2d8a550 --- /dev/null +++ b/ambari-web/app/utils/date/timezone.js @@ -0,0 +1,226 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var dataUtils = require('utils/data_manipulation'); + +/** + * Transitional list of timezones (used to create list of shownTimezone @see shownTimezone) + * + * <code>utcOffset</code> - offset-value (0, 180, 240 etc) + * <code>formattedOffset</code> - formatted offset-value ('+00:00', '-02:00' etc) + * <code>value</code> - timezone's name (like 'Europe/Athens') + * <code>region</code> - timezone's region (for 'Europe/Athens' it will be 'Europe') + * <code>city</code> - timezone's city (for 'Europe/Athens' it will be 'Athens') + * + * @typedef {{utcOffset: number, formattedOffset: string, value: string, region: string, city: string}} formattedTimezone + */ + +/** + * List of timezones used in the user's settings popup + * + * <code>utcOffset</code> - offset-value (0, 180, 240 etc) + * <code>value</code> - string like '(UTC+02:00) Europe / Athens, Kiev, Minsk' + * <code>zones</code> - list of zone-objects from <code>moment.tz</code> included to the <code>value</code> + * + * @typedef {{utcOffset: number, label: string, value: string, zones: object[]}} shownTimezone + */ + +module.exports = Em.Object.create({ + + /** + * @type {shownTimezone[]} + * @readOnly + */ + timezones: [], + + /** + * Map of <code>timezones</code> + * Key - timezone value (like '(UTC+01:00) Region / City1, City2') + * Value - zone-object + * + * @type {object} + * @readOnly + */ + timezonesMappedByLabel: function () { + var ret = {}; + this.get('timezones').forEach(function (tz) { + ret[tz.value] = tz; + }); + return ret; + }.property('timezones.[]'), + + init: function () { + this.set('timezones', this._parseTimezones()); + return this._super(); + }, + + /** + * Load list of timezones from moment.tz + * Zones "Etc/*" and abbreviations are excluded + * + * @returns {string[]} + */ + getAllTimezoneNames: function () { + return moment.tz.names().filter(function (timeZoneName) { + return timeZoneName.indexOf('Etc/') !== 0 && timeZoneName !== timeZoneName.toUpperCase(); + }); + }, + + /** + * Try detect user's timezone using timezoneOffset and moment.tz + * Checking current year January and July offsets + * If <code>region</code> is provided, timezone for it is returned and not first valid + * + * @param {string} [region] preferred region (may be 'Europe', 'America', 'Africa', 'Asia' etc) + * @returns {string} + */ + detectUserTimezone: function (region) { + region = (region || '').toLowerCase(); + var currentYear = new Date().getFullYear(); + var jan = new Date(currentYear, 0, 1); + var jul = new Date(currentYear, 6, 1); + var janOffset = jan.getTimezoneOffset(); + var julOffset = jul.getTimezoneOffset(); + var timezones = this.get('timezones'); + + var validZones = []; + + for (var i = 0; i < timezones.length; i++) { + var zones = timezones[i].zones; + for (var j = 0; j < zones.length; j++) { + var zone = moment.tz.zone(zones[j].value); + if ((zone.offset(jan) === janOffset) && (zone.offset(jul) === julOffset)) { + validZones.pushObject(timezones[i].value); + } + } + } + if (validZones.length) { + if (region) { + for (i = 0; i < validZones.length; i++) { + if (validZones[i].toLowerCase().indexOf(region) !== -1) { + return validZones[i]; + } + } + // Timezone for `region` wasn't found + return validZones[0]; + } + // `region` isn't provided, so return first valid timezone + return validZones[0]; + } + // are you from Venus? + return ''; + }, + + /** + * Reformat timezones list and sort it by utcOffset and timeZoneName + * + * @private + * @method _parseTimezones + * @returns {shownTimezone[]} + */ + _parseTimezones: function () { + var currentYear = new Date().getFullYear(); + var jan = new Date(currentYear, 0, 1); + var jul = new Date(currentYear, 6, 1); + var zones = this.getAllTimezoneNames().map(function (timeZoneName) { + var zone = moment(new Date()).tz(timeZoneName); + var z = moment.tz.zone(timeZoneName); + var offset = zone.format('Z'); + var regionCity = timeZoneName.split('/'); + var region = regionCity[0]; + var city = regionCity.length === 2 ? regionCity[1] : ''; + return { + groupByKey: z.offset(jan) + '' + z.offset(jul), + utcOffset: zone.utcOffset(), + formattedOffset: offset, + value: timeZoneName, + region: region, + city: city.replace(/_/g, ' ') + }; + }).sort(function (zoneA, zoneB) { + if (zoneA.utcOffset === zoneB.utcOffset) { + if (zoneA.value === zoneB.value) { + return 0; + } + return zoneA.value < zoneB.value ? -1 : 1; + } else { + if(zoneA.utcOffset === zoneB.utcOffset) { + return 0; + } + return zoneA.utcOffset < zoneB.utcOffset ? -1 : 1; + } + }); + + return this._groupTimezones(zones); + }, + + /** + * Group timezones by <code>groupByKey</code> + * Group timezones in the each group by <code>region</code> + * <code>city</code> for each regions are joined into string 'city1, city2, city3' (empty cities and abbreviations are ignored) + * Example: + * <pre> + * var zones = [ + * {groupByKey: 1, formattedOffset: '+01:00', value: 'a/Aa', region: 'a', city: 'Aa'}, + * {groupByKey: 1, formattedOffset: '+01:00', value: 'a/Bb', region: 'a', city: 'Bb'}, + * {groupByKey: 2, formattedOffset: '+02:00', value: 'a/Cc', region: 'a', city: 'Cc'}, + * {groupByKey: 2, formattedOffset: '+02:00', value: 'a/Dd', region: 'a', city: 'Dd'}, + * {groupByKey: 1, formattedOffset: '+01:00', value: 'b/Ee', region: 'b', city: 'Ee'}, + * {groupByKey: 1, formattedOffset: '+01:00', value: 'b/Ff', region: 'b', city: 'Ff'}, + * {groupByKey: 2, formattedOffset: '+02:00', value: 'b/Gg', region: 'b', city: 'Gg'}, + * {groupByKey: 2, formattedOffset: '+02:00', value: 'b/Hh', region: 'b', city: 'Hh'}, + * {groupByKey: 2, formattedOffset: '+02:00', value: 'b/II', region: 'b', city: 'II'}, // will be ignored, because city is abbreviation + * {groupByKey: 2, formattedOffset: '+02:00', value: 'b', region: 'b', city: '' } // will be ignored, because city is empty + * ]; + * var groupedZones = _groupTimezones(zones); + * // groupedZones is: + * [ + * {utcOffset: 1, value: '(UTC+01:00) a / Aa, Bb'}, + * {utcOffset: 1, value: '(UTC+01:00) b / Ee, Ff'}, + * {utcOffset: 2, value: '(UTC+02:00) a / Cc, Dd'}, + * {utcOffset: 2, value: '(UTC+02:00) b / Gg, Hh'} + * ] + * </pre> + * + * @param {formattedTimezone[]} zones + * @returns {shownTimezone[]} + * @method _groupTimezones + * @private + */ + _groupTimezones: function (zones) { + var z = dataUtils.groupPropertyValues(zones, 'groupByKey'); + var newZones = []; + Object.keys(z).forEach(function (offset) { + var groupedByRegionZones = dataUtils.groupPropertyValues(z[offset], 'region'); + Object.keys(groupedByRegionZones).forEach(function (region) { + var cities = groupedByRegionZones[region].mapProperty('city').filter(function (city) { + return city !== '' && city !== city.toUpperCase(); + }).uniq().join(', '); + var formattedOffset = Em.get(groupedByRegionZones[region], 'firstObject.formattedOffset'); + var utcOffset = Em.get(groupedByRegionZones[region], 'firstObject.utcOffset'); + newZones.pushObject({ + utcOffset: utcOffset, + value: '(UTC' + formattedOffset + ') ' + region + (cities ? ' / ' + cities : ''), + zones: groupedByRegionZones[region] + }); + }); + }); + return newZones.sortProperty('utcOffset'); + } + +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/utils/helper.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/helper.js b/ambari-web/app/utils/helper.js index 0baaba1..03bbce0 100644 --- a/ambari-web/app/utils/helper.js +++ b/ambari-web/app/utils/helper.js @@ -16,6 +16,7 @@ * limitations under the License. */ var stringUtils = require('utils/string_utils'); +var timezoneUtils = require('utils/date/timezone'); /** * Remove spaces at beginning and ending of line. @@ -634,7 +635,8 @@ App.dateTime = function() { App.dateTimeWithTimeZone = function (x) { var timezone = App.router.get('userSettingsController.userSettings.timezone'); if (timezone) { - return moment(moment.tz(x ? new Date(x) : new Date(), timezone).toArray()).toDate().getTime(); + var tz = Em.getWithDefault(timezoneUtils.get('timezonesMappedByLabel')[timezone], 'zones.0.value', ''); + return moment(moment.tz(x ? new Date(x) : new Date(), tz).toArray()).toDate().getTime(); } return x || new Date().getTime(); }; http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/utils/host_progress_popup.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/host_progress_popup.js b/ambari-web/app/utils/host_progress_popup.js index 120bbfb..6be7dd2 100644 --- a/ambari-web/app/utils/host_progress_popup.js +++ b/ambari-web/app/utils/host_progress_popup.js @@ -18,7 +18,7 @@ var App = require('app'); var batchUtils = require('utils/batch_scheduled_requests'); -var date = require('utils/date'); +var date = require('utils/date/date'); /** * App.HostPopup is for the popup that shows up upon clicking already-performed or currently-in-progress operations http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/common/chart/linear_time.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/common/chart/linear_time.js b/ambari-web/app/views/common/chart/linear_time.js index 4c29af7..18f80c3 100644 --- a/ambari-web/app/views/common/chart/linear_time.js +++ b/ambari-web/app/views/common/chart/linear_time.js @@ -17,7 +17,7 @@ var App = require('app'); var string_utils = require('utils/string_utils'); -var dateUtils = require('utils/date'); +var dateUtils = require('utils/date/date'); /** * @class http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/alert_definitions_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/alert_definitions_view.js b/ambari-web/app/views/main/alert_definitions_view.js index fe7bf52..6b5e751 100644 --- a/ambari-web/app/views/main/alert_definitions_view.js +++ b/ambari-web/app/views/main/alert_definitions_view.js @@ -19,7 +19,7 @@ var App = require('app'); var filters = require('views/common/filter_view'), sort = require('views/common/sort_view'), - date = require('utils/date'); + date = require('utils/date/date'); App.MainAlertDefinitionsView = App.TableView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/alerts.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/alerts.js b/ambari-web/app/views/main/alerts.js index d84062e..137f4a5 100644 --- a/ambari-web/app/views/main/alerts.js +++ b/ambari-web/app/views/main/alerts.js @@ -19,7 +19,7 @@ var App = require('app'); var filters = require('views/common/filter_view'); var sort = require('views/common/sort_view'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.MainAlertsView = App.TableView.extend(App.TableServerViewMixin, { templateName:require('templates/main/alerts'), http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/charts/heatmap/heatmap_host.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/charts/heatmap/heatmap_host.js b/ambari-web/app/views/main/charts/heatmap/heatmap_host.js index d42255e..de9d878 100644 --- a/ambari-web/app/views/main/charts/heatmap/heatmap_host.js +++ b/ambari-web/app/views/main/charts/heatmap/heatmap_host.js @@ -15,7 +15,7 @@ * the License. */ -var date = require('utils/date'); +var date = require('utils/date/date'); var App = require('app'); @@ -167,4 +167,4 @@ App.MainChartsHeatmapHostView = Em.View.extend({ } return ''; }.property('controller.hostToSlotMap') -}); \ No newline at end of file +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/dashboard/widgets/links_widget.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/dashboard/widgets/links_widget.js b/ambari-web/app/views/main/dashboard/widgets/links_widget.js index e9dfdca..5647617 100644 --- a/ambari-web/app/views/main/dashboard/widgets/links_widget.js +++ b/ambari-web/app/views/main/dashboard/widgets/links_widget.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.LinkDashboardWidgetView = App.DashboardWidgetView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js index 370ed6e..e67cf39 100644 --- a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js +++ b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.ResourceManagerUptimeView = App.UptimeTextDashboardWidgetView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/dashboard/widgets/text_widget.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/dashboard/widgets/text_widget.js b/ambari-web/app/views/main/dashboard/widgets/text_widget.js index 85b2d54..b51ca6e 100644 --- a/ambari-web/app/views/main/dashboard/widgets/text_widget.js +++ b/ambari-web/app/views/main/dashboard/widgets/text_widget.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.TextDashboardWidgetView = App.DashboardWidgetView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js index a3290de..726ebb8 100644 --- a/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js +++ b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.UptimeTextDashboardWidgetView = App.TextDashboardWidgetView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/host.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/host.js b/ambari-web/app/views/main/host.js index 82bda20..f821a90 100644 --- a/ambari-web/app/views/main/host.js +++ b/ambari-web/app/views/main/host.js @@ -19,7 +19,7 @@ var App = require('app'); var filters = require('views/common/filter_view'); var sort = require('views/common/sort_view'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.MainHostView = App.TableView.extend(App.TableServerViewMixin, { templateName:require('templates/main/host'), http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/host/configs.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/host/configs.js b/ambari-web/app/views/main/host/configs.js index 1b8054b..6a6803f 100644 --- a/ambari-web/app/views/main/host/configs.js +++ b/ambari-web/app/views/main/host/configs.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.MainHostConfigsView = Em.View.extend({ templateName: require('templates/main/host/configs'), http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/host/details.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/host/details.js b/ambari-web/app/views/main/host/details.js index ea60181..838502f 100644 --- a/ambari-web/app/views/main/host/details.js +++ b/ambari-web/app/views/main/host/details.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.MainHostDetailsView = Em.View.extend({ templateName: require('templates/main/host/details'), http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/service/services/flume.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/services/flume.js b/ambari-web/app/views/main/service/services/flume.js index 1ab89f9..f0283e9 100644 --- a/ambari-web/app/views/main/service/services/flume.js +++ b/ambari-web/app/views/main/service/services/flume.js @@ -16,7 +16,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); var sort = require('views/common/sort_view'); App.MainDashboardServiceFlumeView = App.TableView.extend(App.MainDashboardServiceViewWrapper, { http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/service/services/hbase.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/services/hbase.js b/ambari-web/app/views/main/service/services/hbase.js index d17d2a0..8b6b9f7 100644 --- a/ambari-web/app/views/main/service/services/hbase.js +++ b/ambari-web/app/views/main/service/services/hbase.js @@ -16,7 +16,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); var numberUtils = require('utils/number_utils'); App.MainDashboardServiceHbaseView = App.MainDashboardServiceView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/service/services/hdfs.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/services/hdfs.js b/ambari-web/app/views/main/service/services/hdfs.js index f469a89..f53e345 100644 --- a/ambari-web/app/views/main/service/services/hdfs.js +++ b/ambari-web/app/views/main/service/services/hdfs.js @@ -16,7 +16,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); var numberUtils = require('utils/number_utils'); App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/service/services/storm.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/services/storm.js b/ambari-web/app/views/main/service/services/storm.js index 34242aa..6cd17ac 100644 --- a/ambari-web/app/views/main/service/services/storm.js +++ b/ambari-web/app/views/main/service/services/storm.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.MainDashboardServiceStormView = App.MainDashboardServiceView.extend({ templateName: require('templates/main/service/services/storm'), http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/main/service/services/yarn.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/service/services/yarn.js b/ambari-web/app/views/main/service/services/yarn.js index 55d0e53..b80fb76 100644 --- a/ambari-web/app/views/main/service/services/yarn.js +++ b/ambari-web/app/views/main/service/services/yarn.js @@ -16,7 +16,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); var numberUtils = require('utils/number_utils'); App.MainDashboardServiceYARNView = App.MainDashboardServiceView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js b/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js index 301c403..f9e6e94 100644 --- a/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js +++ b/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.WizardStep9HostLogPopupBodyView = Em.View.extend({ @@ -270,4 +270,4 @@ App.WizardStep9HostLogPopupBodyView = Em.View.extend({ $(".task-detail-log-clipboard").remove(); $(".task-detail-log-maintext").css("display", "block"); } -}); \ No newline at end of file +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/app/views/wizard/step9_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/wizard/step9_view.js b/ambari-web/app/views/wizard/step9_view.js index 4864c8e..0e52636 100644 --- a/ambari-web/app/views/wizard/step9_view.js +++ b/ambari-web/app/views/wizard/step9_view.js @@ -17,7 +17,7 @@ */ var App = require('app'); -var date = require('utils/date'); +var date = require('utils/date/date'); App.WizardStep9View = App.TableView.extend({ http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js index 77fef25..b057a80 100644 --- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js +++ b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js @@ -17,7 +17,7 @@ var App = require('app'); require('controllers/main/charts/heatmap_metrics/heatmap_metric'); -var date = require('utils/date'); +var date = require('utils/date/date'); describe('MainChartHeatmapMetric', function () { var mainChartHeatmapMetric = App.MainChartHeatmapMetric.create({}); http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/test/utils/date/date_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/date/date_test.js b/ambari-web/test/utils/date/date_test.js new file mode 100644 index 0000000..630b3a8 --- /dev/null +++ b/ambari-web/test/utils/date/date_test.js @@ -0,0 +1,185 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Ember = require('ember'); +var App = require('app'); + +var validator = require('utils/validator'); +var date = require('utils/date/date'); + +describe('date', function () { + + var correct_tests = Em.A([ + {t: 1349752195000, e: 'Tue, Oct 09, 2012 03:09', e2: 'Tue Oct 09 2012'}, + {t: 1367752195000, e: 'Sun, May 05, 2013 11:09', e2: 'Sun May 05 2013'}, + {t: 1369952195000, e: 'Thu, May 30, 2013 22:16', e2: 'Thu May 30 2013'} + ]); + + var incorrect_tests = Em.A([ + {t: null}, + {t: ''}, + {t: false}, + {t: []}, + {t: {}}, + {t: undefined}, + {t: function(){}} + ]); + + describe('#dateFormatZeroFirst()', function() { + var tests = [ + { + t: 2, + e: '02', + m: 'should convert to `02`' + }, + { + t: 10, + e: '10', + m: 'should convert to `10`' + } + ]; + tests.forEach(function(test) { + it(test.m, function() { + expect(date.dateFormatZeroFirst(test.t)).to.eql(test.e); + }); + }); + }); + + describe('#startTime()', function() { + var today = new Date(); + var testDate = new Date(1349752195000); + var tests = [ + { t: 1349752195000, e: testDate.toDateString() + ' {0}:{1}'.format(date.dateFormatZeroFirst(testDate.getHours()), date.dateFormatZeroFirst(testDate.getMinutes())) }, + { t: -10000000, e: 'Not started' }, + { t: today.getTime(), e: 'Today {0}:{1}'.format(date.dateFormatZeroFirst(today.getHours()), date.dateFormatZeroFirst(today.getMinutes())) }, + { t: today, e: ''} + ]; + tests.forEach(function(test) { + var testMessage = 'should convert {0} to {1}'.format(test.t, test.e); + it(testMessage, function() { + expect(date.startTime(test.t)).to.be.eql(test.e); + }); + }); + }); + + describe('#timingFormat', function() { + var tests = Em.A([ + {i: '30', e:'30 ms'}, + {i: '300', e:'300 ms'}, + {i: '999', e:'999 ms'}, + {i: '1000', e:'1.00 secs'}, + {i: '3000', e:'3.00 secs'}, + {i: '35000', e:'35.00 secs'}, + {i: '350000', e:'350.00 secs'}, + {i: '999999', e:'1000.00 secs'}, + {i: '1000000', e:'16.67 mins'}, + {i: '3500000', e:'58.33 mins'}, + {i: '35000000', e:'9.72 hours'}, + {i: '350000000', e:'4.05 days'}, + {i: '3500000000', e:'40.51 days'}, + {i: '35000000000', e:'405.09 days'} + ]); + + describe('Correct data', function(){ + tests.forEach(function(test) { + it(test.t, function() { + expect(date.timingFormat(test.i)).to.equal(test.e); + }); + }); + }); + + describe('Incorrect data', function(){ + incorrect_tests.forEach(function(test) { + it(test.t, function() { + expect(date.timingFormat(test.t)).to.equal(null); + }); + }); + }); + + }); + + describe('#duration', function() { + var tests = Em.A([ + {startTime: 1, endTime: 2, e: 1}, + {startTime: 0, endTime: 2000, e: 0}, + {startTime: 200, endTime: 0, e: 19800} + ]); + + beforeEach(function() { + sinon.stub(App, 'dateTime', function () { return 20000; }); + }); + + tests.forEach(function(test) { + it(test.startTime + ' ' + test.endTime, function() { + expect(date.duration(test.startTime, test.endTime)).to.equal(test.e); + }); + }); + + afterEach(function() { + App.dateTime.restore(); + }); + }); + + describe('#durationSummary()', function() { + var tests = [ + { + startTimestamp: 1349752195000, + endTimestamp: 1349752199000, + e: '4.00 secs' + }, + { + startTimestamp: 1349752195000, + endTimestamp: 1367752195000, + e: '208.33 days' + }, + { + startTimestamp: -10000000, + endTimestamp: 1367752195000, + e: Em.I18n.t('common.na') + }, + { + startTimestamp: 1349752195000, + endTimestamp: -1, + stubbed: true, + e: '0 secs' + }, + { + startTimestamp: 100000000, + endTimestamp: -1, + stubbed: true, + e: '19.00 secs' + } + ]; + + beforeEach(function() { + sinon.stub(App, 'dateTime', function () { return 100019000; }); + }); + + tests.forEach(function(test) { + var testMessage = 'duration between {0} and {1} is {2}'.format(test.startTimestamp, test.endTimestamp, test.e) + (test.stubbed ? " App.dateTime() is stubbed" : ""); + it(testMessage, function() { + expect(date.durationSummary(test.startTimestamp, test.endTimestamp)).to.be.eql(test.e); + }); + }); + + afterEach(function() { + App.dateTime.restore(); + }); + }); + +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/test/utils/date/timezone_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/date/timezone_test.js b/ambari-web/test/utils/date/timezone_test.js new file mode 100644 index 0000000..4b9027a --- /dev/null +++ b/ambari-web/test/utils/date/timezone_test.js @@ -0,0 +1,175 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var timezoneUtils = require('utils/date/timezone'); + +describe('timezoneUtils', function () { + + describe('#_groupTimezones', function () { + + var formattedTimezones = [ + {utcOffset: 1, groupByKey: 1, formattedOffset: '+01:00', value: 'a/Aa', region: 'a', city: 'Aa'}, + {utcOffset: 1, groupByKey: 1, formattedOffset: '+01:00', value: 'a/Bb', region: 'a', city: 'Bb'}, + {utcOffset: 2, groupByKey: 2, formattedOffset: '+02:00', value: 'a/Cc', region: 'a', city: 'Cc'}, + {utcOffset: 2, groupByKey: 2, formattedOffset: '+02:00', value: 'a/Dd', region: 'a', city: 'Dd'}, + {utcOffset: 1, groupByKey: 1, formattedOffset: '+01:00', value: 'b/Ee', region: 'b', city: 'Ee'}, + {utcOffset: 1, groupByKey: 1, formattedOffset: '+01:00', value: 'b/Ff', region: 'b', city: 'Ff'}, + {utcOffset: 2, groupByKey: 2, formattedOffset: '+02:00', value: 'b/Gg', region: 'b', city: 'Gg'}, + {utcOffset: 2, groupByKey: 2, formattedOffset: '+02:00', value: 'b/Hh', region: 'b', city: 'Hh'}, + {utcOffset: 2, groupByKey: 2, formattedOffset: '+02:00', value: 'b/II', region: 'b', city: 'II'}, + {utcOffset: 2, groupByKey: 2, formattedOffset: '+02:00', value: 'b', region: 'b', city: ''} + ]; + + before(function () { + this.result = timezoneUtils._groupTimezones(formattedTimezones); + }); + + it('should group to 4 groups', function () { + expect(this.result.length).to.equal(4); + }); + + it('UTCOffset should be [1,1,2,2]', function () { + expect(this.result.mapProperty('utcOffset')).to.eql([1, 1, 2, 2]); + }); + + it('should map regions and cities correctly', function () { + var expected = ['(UTC+01:00) a / Aa, Bb', + '(UTC+01:00) b / Ee, Ff', + '(UTC+02:00) a / Cc, Dd', + '(UTC+02:00) b / Gg, Hh']; + var values = this.result.mapProperty('value'); + expect(values).to.eql(expected); + expect(values.join('')).to.not.contain('II'); + expect(values.join('')).to.not.contain(', ,'); + }); + + }); + + describe('#_parseTimezones', function () { + + beforeEach(function () { + sinon.stub(timezoneUtils, 'getAllTimezoneNames').returns([ + 'Europe/Helsinki', + 'Asia/Magadan', + 'America/Lima' + ]); + sinon.stub(timezoneUtils, '_groupTimezones', function (list) { + return list; + }); + this.result = timezoneUtils._parseTimezones(); + }); + + afterEach(function () { + timezoneUtils.getAllTimezoneNames.restore(); + timezoneUtils._groupTimezones.restore(); + }); + + it('should sort by offset and name', function () { + expect(this.result.mapProperty('value')).to.eql(['America/Lima', 'Europe/Helsinki', 'Asia/Magadan']); + }); + + it('should split regions and cities', function () { + expect(this.result.mapProperty('region')).to.eql(['America', 'Europe', 'Asia']); + expect(this.result.mapProperty('city')).to.eql(['Lima', 'Helsinki', 'Magadan']); + }); + + }); + + describe('#getAllTimezoneNames', function () { + + before(function () { + this.result = timezoneUtils.getAllTimezoneNames(); + }); + + after(function () { + this.result = undefined; + }); + + it('timezone names are parsed', function () { + expect(this.result).to.have.length.above(0); + }); + + it('Etc/* are excluded', function () { + this.result.forEach(function (tz) { + expect(tz.indexOf('Etc/')).to.equal(-1); + }); + }); + + it('Abbreviations are excluded', function () { + this.result.forEach(function (tz) { + expect(tz).to.not.equal(tz.toUpperCase()); + }); + }); + + }); + + describe('#detectUserTimezone', function () { + + var getTimezoneOffset = Date.prototype.getTimezoneOffset; + + function mockTimezoneOffset(jan, jul) { + Date.prototype.getTimezoneOffset = function () { + var month = this.getMonth(); + if (month > 3 && month < 9) { + return -jul; + } + return -jan; + }; + } + + afterEach(function () { + Date.prototype.getTimezoneOffset = getTimezoneOffset; + }); + + it('Detect UTC+1', function () { + mockTimezoneOffset(0, 60); + var tz = timezoneUtils.detectUserTimezone(); + expect(tz).to.contain('(UTC+01:00) Atlantic'); + }); + + it('Detect UTC+1 for Europe', function () { + mockTimezoneOffset(0, 60); + var tz = timezoneUtils.detectUserTimezone('Europe'); + expect(tz).to.contain('(UTC+01:00) Europe'); + expect(tz).to.contain('London'); + }); + + it('Detect UTC-4', function () { + mockTimezoneOffset(-300, -240); + var tz = timezoneUtils.detectUserTimezone(); + expect(tz).to.contain('(UTC-04:00) America'); + expect(tz).to.contain('New York'); + }); + + it('Detect UTC+3 for Asia', function () { + mockTimezoneOffset(120, 180); + var tz = timezoneUtils.detectUserTimezone(); + expect(tz).to.contain('(UTC+03:00) Asia'); + expect(tz).to.contain('Damascus'); + }); + + it('Detect UTC-7', function () { + mockTimezoneOffset(-480, -420); + var tz = timezoneUtils.detectUserTimezone(); + expect(tz).to.contain('(UTC-07:00) America'); + expect(tz).to.contain('Los Angeles'); + }); + + }); + +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/test/utils/date_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/date_test.js b/ambari-web/test/utils/date_test.js deleted file mode 100644 index 8e7167d..0000000 --- a/ambari-web/test/utils/date_test.js +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var Ember = require('ember'); -var App = require('app'); - -var validator = require('utils/validator'); -var date = require('utils/date'); - -describe('date', function () { - - var correct_tests = Em.A([ - {t: 1349752195000, e: 'Tue, Oct 09, 2012 03:09', e2: 'Tue Oct 09 2012'}, - {t: 1367752195000, e: 'Sun, May 05, 2013 11:09', e2: 'Sun May 05 2013'}, - {t: 1369952195000, e: 'Thu, May 30, 2013 22:16', e2: 'Thu May 30 2013'} - ]); - - var incorrect_tests = Em.A([ - {t: null}, - {t: ''}, - {t: false}, - {t: []}, - {t: {}}, - {t: undefined}, - {t: function(){}} - ]); - - describe('#dateFormatZeroFirst()', function() { - var tests = [ - { - t: 2, - e: '02', - m: 'should convert to `02`' - }, - { - t: 10, - e: '10', - m: 'should convert to `10`' - } - ]; - tests.forEach(function(test) { - it(test.m, function() { - expect(date.dateFormatZeroFirst(test.t)).to.eql(test.e); - }); - }); - }); - - describe('#startTime()', function() { - var today = new Date(); - var testDate = new Date(1349752195000); - var tests = [ - { t: 1349752195000, e: testDate.toDateString() + ' {0}:{1}'.format(date.dateFormatZeroFirst(testDate.getHours()), date.dateFormatZeroFirst(testDate.getMinutes())) }, - { t: -10000000, e: 'Not started' }, - { t: today.getTime(), e: 'Today {0}:{1}'.format(date.dateFormatZeroFirst(today.getHours()), date.dateFormatZeroFirst(today.getMinutes())) }, - { t: today, e: ''} - ]; - tests.forEach(function(test) { - var testMessage = 'should convert {0} to {1}'.format(test.t, test.e); - it(testMessage, function() { - expect(date.startTime(test.t)).to.be.eql(test.e); - }); - }); - }); - - describe('#timingFormat', function() { - var tests = Em.A([ - {i: '30', e:'30 ms'}, - {i: '300', e:'300 ms'}, - {i: '999', e:'999 ms'}, - {i: '1000', e:'1.00 secs'}, - {i: '3000', e:'3.00 secs'}, - {i: '35000', e:'35.00 secs'}, - {i: '350000', e:'350.00 secs'}, - {i: '999999', e:'1000.00 secs'}, - {i: '1000000', e:'16.67 mins'}, - {i: '3500000', e:'58.33 mins'}, - {i: '35000000', e:'9.72 hours'}, - {i: '350000000', e:'4.05 days'}, - {i: '3500000000', e:'40.51 days'}, - {i: '35000000000', e:'405.09 days'} - ]); - - describe('Correct data', function(){ - tests.forEach(function(test) { - it(test.t, function() { - expect(date.timingFormat(test.i)).to.equal(test.e); - }); - }); - }); - - describe('Incorrect data', function(){ - incorrect_tests.forEach(function(test) { - it(test.t, function() { - expect(date.timingFormat(test.t)).to.equal(null); - }); - }); - }); - - }); - - describe('#duration', function() { - var tests = Em.A([ - {startTime: 1, endTime: 2, e: 1}, - {startTime: 0, endTime: 2000, e: 0}, - {startTime: 200, endTime: 0, e: 19800} - ]); - - beforeEach(function() { - sinon.stub(App, 'dateTime', function () { return 20000; }); - }); - - tests.forEach(function(test) { - it(test.startTime + ' ' + test.endTime, function() { - expect(date.duration(test.startTime, test.endTime)).to.equal(test.e); - }); - }); - - afterEach(function() { - App.dateTime.restore(); - }); - }); - - describe('#durationSummary()', function() { - var tests = [ - { - startTimestamp: 1349752195000, - endTimestamp: 1349752199000, - e: '4.00 secs' - }, - { - startTimestamp: 1349752195000, - endTimestamp: 1367752195000, - e: '208.33 days' - }, - { - startTimestamp: -10000000, - endTimestamp: 1367752195000, - e: Em.I18n.t('common.na') - }, - { - startTimestamp: 1349752195000, - endTimestamp: -1, - stubbed: true, - e: '0 secs' - }, - { - startTimestamp: 100000000, - endTimestamp: -1, - stubbed: true, - e: '19.00 secs' - } - ]; - - beforeEach(function() { - sinon.stub(App, 'dateTime', function () { return 100019000; }); - }); - - tests.forEach(function(test) { - var testMessage = 'duration between {0} and {1} is {2}'.format(test.startTimestamp, test.endTimestamp, test.e) + (test.stubbed ? " App.dateTime() is stubbed" : ""); - it(testMessage, function() { - expect(date.durationSummary(test.startTimestamp, test.endTimestamp)).to.be.eql(test.e); - }); - }); - - afterEach(function() { - App.dateTime.restore(); - }); - }); - -}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/34082a5a/ambari-web/test/views/common/chart/linear_time_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/views/common/chart/linear_time_test.js b/ambari-web/test/views/common/chart/linear_time_test.js index 74ea89c..0d4b200 100644 --- a/ambari-web/test/views/common/chart/linear_time_test.js +++ b/ambari-web/test/views/common/chart/linear_time_test.js @@ -30,7 +30,7 @@ describe('App.ChartLinearTimeView', function () { beforeEach(function () { var data = [[1, 1200000000], [2, 1200000000], [3, 1200000000]]; var name = 'abc'; - sinon.stub(App.router, 'get').withArgs('userSettingsController.userSettings.timezone').returns('Canada/Atlantic'); + sinon.stub(App.router, 'get').withArgs('userSettingsController.userSettings.timezone').returns('(UTC+00:00) Greenwich'); result = chartLinearTimeView.transformData(data, name); });
