AgularJS pages display dates using Horizon's Settings Timezone

Horizon's AngularJS pages (for example Images and Keypairs)
display dates using browser's timezone now. This change makes
AngularJS pages use Horizon's Settings Timezone instead of
browser's timezone and if Timezone is not set under Settings,
AngularJS pages will display dates in 'UTC' timezone.

Closes-Bug: 1832768
Change-Id: Ibbed19600bfe6b13c43b9f09fa484cb78524b0d6
This commit is contained in:
hutianhao 2019-08-23 16:01:05 +08:00 committed by hutianhao
parent 85a1dddf12
commit a464a94655
6 changed files with 191 additions and 11 deletions

View File

@ -17,7 +17,7 @@
'use strict';
angular
.module('horizon.framework.util.filters')
.module('horizon.framework.util.filters', ['ngCookies'])
.filter('yesno', yesNoFilter)
.filter('simpleDate', simpleDateFilter)
.filter('mediumDate', mediumDateFilter)
@ -53,10 +53,18 @@
* @description
* Evaluates given for display as a short date, returning '-' if empty.
*/
simpleDateFilter.$inject = ['$filter'];
function simpleDateFilter($filter) {
simpleDateFilter.$inject = [
'$cookies',
'$filter',
'horizon.framework.util.timezones.service'
];
function simpleDateFilter($cookies, $filter, timeZoneService) {
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
* Evaluates given for display as a medium date, returning '-' if empty.
*/
mediumDateFilter.$inject = ['$filter'];
function mediumDateFilter($filter) {
mediumDateFilter.$inject = [
'$cookies',
'$filter',
'horizon.framework.util.timezones.service'
];
function mediumDateFilter($cookies, $filter, timeZoneService) {
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 () {
expect(simpleDateFilter()).toBe('-');
simpleDateFilter().then(getResult);
function getResult(result) {
expect(result).toBe('-');
}
});
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 () {
expect(mediumDateFilter()).toBe('-');
mediumDateFilter().then(getResult);
function getResult(result) {
expect(result).toBe('-');
}
});
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.q',
'horizon.framework.util.tech-debt',
'horizon.framework.util.timezones',
'horizon.framework.util.uuid',
'horizon.framework.util.workflow',
'horizon.framework.util.validators',

View File

@ -13,8 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime
from django.conf import settings
from django.http import JsonResponse
from django.views import generic
import pytz
from openstack_dashboard import api
from openstack_dashboard.api.rest import urls
@ -55,3 +59,15 @@ class Settings(generic.View):
in settings_allowed if k not in self.SPECIALS}
plain_settings.update(self.SPECIALS)
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})