Merge "AgularJS pages display dates using Horizon's Settings Timezone"

This commit is contained in:
Zuul 2019-12-09 13:24:48 +00:00 committed by Gerrit Code Review
commit bd5642dc73
6 changed files with 191 additions and 11 deletions

View File

@ -17,7 +17,7 @@
'use strict'; 'use strict';
angular angular
.module('horizon.framework.util.filters') .module('horizon.framework.util.filters', ['ngCookies'])
.filter('yesno', yesNoFilter) .filter('yesno', yesNoFilter)
.filter('simpleDate', simpleDateFilter) .filter('simpleDate', simpleDateFilter)
.filter('mediumDate', mediumDateFilter) .filter('mediumDate', mediumDateFilter)
@ -53,10 +53,18 @@
* @description * @description
* Evaluates given for display as a short date, returning '-' if empty. * Evaluates given for display as a short date, returning '-' if empty.
*/ */
simpleDateFilter.$inject = ['$filter']; simpleDateFilter.$inject = [
function simpleDateFilter($filter) { '$cookies',
'$filter',
'horizon.framework.util.timezones.service'
];
function simpleDateFilter($cookies, $filter, timeZoneService) {
return function (input) { return function (input) {
return $filter('noValue')($filter('date')(input, 'short')); var currentTimeZone = $cookies.get('django_timezone') || 'UTC';
currentTimeZone = currentTimeZone.replace(/^"(.*)"$/, '$1');
return timeZoneService.getTimeZoneOffset(currentTimeZone).then(function (timeZoneOffset) {
return $filter('noValue')($filter('date')(input, 'short', timeZoneOffset));
});
}; };
} }
@ -66,10 +74,23 @@
* @description * @description
* Evaluates given for display as a medium date, returning '-' if empty. * Evaluates given for display as a medium date, returning '-' if empty.
*/ */
mediumDateFilter.$inject = ['$filter']; mediumDateFilter.$inject = [
function mediumDateFilter($filter) { '$cookies',
'$filter',
'horizon.framework.util.timezones.service'
];
function mediumDateFilter($cookies, $filter, timeZoneService) {
return function (input) { return function (input) {
return $filter('noValue')($filter('date')(input, 'medium')); /*
* For the input time, we need to add "Z" to fit iso8601 time format
* so the filter can confirm that the input time is in UTC timezone.
*/
input = input + 'Z';
var currentTimeZone = $cookies.get('django_timezone') || 'UTC';
currentTimeZone = currentTimeZone.replace(/^"(.*)"$/, '$1');
return timeZoneService.getTimeZoneOffset(currentTimeZone).then(function (timeZoneOffset) {
return $filter('noValue')($filter('date')(input, 'medium', timeZoneOffset));
});
}; };
} }

View File

@ -59,11 +59,19 @@
})); }));
it('returns blank if nothing', function () { it('returns blank if nothing', function () {
expect(simpleDateFilter()).toBe('-'); simpleDateFilter().then(getResult);
function getResult(result) {
expect(result).toBe('-');
}
}); });
it('returns the expected time', function() { it('returns the expected time', function() {
expect(simpleDateFilter('2016-06-24T04:19:07')).toBe('6/24/16 4:19 AM'); simpleDateFilter().then(getResult);
function getResult(result) {
expect(result).toBe('9/3/19 9:19 AM');
}
}); });
}); });
@ -74,11 +82,19 @@
})); }));
it('returns blank if nothing', function () { it('returns blank if nothing', function () {
expect(mediumDateFilter()).toBe('-'); mediumDateFilter().then(getResult);
function getResult(result) {
expect(result).toBe('-');
}
}); });
it('returns the expected time', function() { it('returns the expected time', function() {
expect(mediumDateFilter('2001-02-03T16:05:06')).toBe('Feb 3, 2001 4:05:06 PM'); mediumDateFilter().then(getResult);
function getResult(result) {
expect(result).toBe('Sep 3, 2019 9:19:07 AM');
}
}); });
}); });

View File

@ -0,0 +1,74 @@
/*
* Copyright 2019 99Cloud Inc.
*
* Licensed 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.
*/
(function () {
'use strict';
angular
.module('horizon.framework.util.timezones', [])
.factory('horizon.framework.util.timezones.service', timeZoneService);
timeZoneService.$inject = [
'$q',
'horizon.framework.util.http.service'
];
/**
* @ngdoc service
* @name timeZoneService
* @param {Object} $q
* @param {Object} ApiService
* @description
* Horizon's AngularJS pages(for example Images and Keypairs) display dates
* using browser's timezone now. This service get timezone offset from
* Horizon's Settings and if Timezone is not set under Settings, AngularJS
* pages will display dates in 'UTC' timezone.
* @returns {Object} The service
*/
function timeZoneService($q, ApiService) {
var service = {
getTimeZones: getTimeZones,
getTimeZoneOffset: getTimeZoneOffset
};
return service;
/////////
function getTimeZones() {
return ApiService.get('/api/timezones/', {cache: true});
}
function getTimeZoneOffset(timezone) {
var deferred = $q.defer();
function onTimezonesLoaded(response) {
var offsetDict = response.data.timezone_dict;
timezone = timezone || 'UTC';
deferred.resolve(offsetDict[timezone]);
}
function onTimezonesFailure(message) {
deferred.reject(message);
}
service.getTimeZones()
.then(onTimezonesLoaded, onTimezonesFailure);
return deferred.promise;
}
}
}());

View File

@ -0,0 +1,52 @@
/*
* Copyright 2019 99Cloud Inc.
*
* Licensed 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.
*/
(function () {
'use strict';
describe('horizon.framework.util.timezones.service', function () {
var service;
beforeEach(module('horizon.framework'));
beforeEach(inject(function($injector) {
service = $injector.get('horizon.framework.util.timezones.service');
}));
it('defines the service', function() {
expect(service).toBeDefined();
});
describe('get timezone offset', function () {
it('returns +0000(UTC offset) if nothing', function () {
function getResult(result) {
expect(result).toBe('+0000');
}
service.getTimeZoneOffset().then(getResult);
});
it('returns the timezone offset', function() {
function getResult(result) {
expect(result).toBe('+0800');
}
service.getTimeZoneOffset('Asia/Shanghai').then(getResult);
});
});
}); // end of horizon.framework.util.timezones
})();

View File

@ -27,6 +27,7 @@
'horizon.framework.util.promise-toggle', 'horizon.framework.util.promise-toggle',
'horizon.framework.util.q', 'horizon.framework.util.q',
'horizon.framework.util.tech-debt', 'horizon.framework.util.tech-debt',
'horizon.framework.util.timezones',
'horizon.framework.util.uuid', 'horizon.framework.util.uuid',
'horizon.framework.util.workflow', 'horizon.framework.util.workflow',
'horizon.framework.util.validators', 'horizon.framework.util.validators',

View File

@ -13,8 +13,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from datetime import datetime
from django.conf import settings from django.conf import settings
from django.http import JsonResponse
from django.views import generic from django.views import generic
import pytz
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.api.rest import urls from openstack_dashboard.api.rest import urls
@ -55,3 +59,15 @@ class Settings(generic.View):
in settings_allowed if k not in self.SPECIALS} in settings_allowed if k not in self.SPECIALS}
plain_settings.update(self.SPECIALS) plain_settings.update(self.SPECIALS)
return plain_settings return plain_settings
@urls.register
class Timezones(generic.View):
"""API for timezone service."""
url_regex = r'timezones/$'
@rest_utils.ajax()
def get(self, request):
zones = {tz: datetime.now(pytz.timezone(tz)).strftime('%z')
for tz in pytz.common_timezones}
return JsonResponse({'timezone_dict': zones})