support set timezone

This commit is contained in:
MaysWind
2021-03-07 23:59:13 +08:00
parent 7c47b3253e
commit fc2023fba2
14 changed files with 127 additions and 27 deletions
+8
View File
@@ -9017,6 +9017,14 @@
"lodash.difference": "^4.5.0"
}
},
"moment-timezone": {
"version": "0.5.33",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz",
"integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==",
"requires": {
"moment": ">= 2.9.0"
}
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+1
View File
@@ -18,6 +18,7 @@
"js-cookie": "^2.2.1",
"line-awesome": "^1.3.0",
"moment": "^2.29.1",
"moment-timezone": "^0.5.33",
"register-service-worker": "^1.7.2",
"ua-parser-js": "^0.7.24",
"vue": "^2.6.12",
@@ -2,6 +2,7 @@ package exchangerates
import (
"encoding/xml"
"time"
"github.com/mayswind/lab/pkg/core"
"github.com/mayswind/lab/pkg/errs"
@@ -14,6 +15,9 @@ const euroCentralBankExchangeRateReferenceUrl = "https://www.ecb.europa.eu/stats
const euroCentralBankDataSource = "European Central Bank"
const euroCentralBankBaseCurrency = "EUR"
const euroCentralBankDataUpdateDateFormat = "2006-01-02 15"
const euroCentralBankDataUpdateDateTimezone = "Etc/GMT-1" // UTC+01:00
// EuroCentralBankDataSource defines the structure of exchange rates data source of euro central bank
type EuroCentralBankDataSource struct {
ExchangeRatesDataSource
@@ -55,10 +59,23 @@ func (e *EuroCentralBankExchangeRateData) ToLatestExchangeRateResponse() *models
exchangeRates[i] = latestEuroCentralBankExchangeRate.ExchangeRates[i].ToLatestExchangeRate()
}
timezone, err := time.LoadLocation(euroCentralBankDataUpdateDateTimezone)
if err != nil {
return nil
}
updateDateTime := latestEuroCentralBankExchangeRate.Date + " 16" // The reference rates are usually updated around 16:00 CET on every working day
updateTime, err := time.ParseInLocation(euroCentralBankDataUpdateDateFormat, updateDateTime, timezone)
if err != nil {
return nil
}
latestExchangeRateResp := &models.LatestExchangeRateResponse{
DataSource: euroCentralBankDataSource,
ReferenceUrl: euroCentralBankExchangeRateReferenceUrl,
Date: latestEuroCentralBankExchangeRate.Date,
UpdateTime: updateTime.Unix(),
BaseCurrency: euroCentralBankBaseCurrency,
ExchangeRates: exchangeRates,
}
+1 -1
View File
@@ -4,7 +4,7 @@ package models
type LatestExchangeRateResponse struct {
DataSource string `json:"dataSource"`
ReferenceUrl string `json:"referenceUrl"`
Date string `json:"date"`
UpdateTime int64 `json:"updateTime"`
BaseCurrency string `json:"baseCurrency"`
ExchangeRates []*LatestExchangeRate `json:"exchangeRates"`
}
@@ -52,7 +52,7 @@ export default {
data() {
const self = this;
let minDate = self.$utilities.getTodayFirstUnixTime();
let maxDate = self.$utilities.getUnixTime(new Date());
let maxDate = self.$utilities.getCurrentUnixTime();
if (self.minTime) {
minDate = self.minTime;
@@ -70,12 +70,12 @@ export default {
watch: {
'currentMinDate': function (newValue) {
if (!newValue) {
this.currentMinDate = this.$utilities.formatDate(new Date(), 'YYYY-MM-DDTHH:mm');
this.currentMinDate = this.$utilities.formatUnixTime(this.$utilities.getCurrentUnixTime(), 'YYYY-MM-DDTHH:mm');
}
},
'currentMaxDate': function (newValue) {
if (!newValue) {
this.currentMaxDate = this.$utilities.formatDate(new Date(), 'YYYY-MM-DDTHH:mm');
this.currentMaxDate = this.$utilities.formatUnixTime(this.$utilities.getCurrentUnixTime(), 'YYYY-MM-DDTHH:mm');
}
}
},
+3
View File
@@ -8,6 +8,7 @@ const serverSettingsCookieKey = 'lab_server_settings';
const defaultSettings = {
lang: 'en',
timeZone: '',
debug: false,
applicationLock: false,
applicationLockWebAuthn: false,
@@ -128,6 +129,8 @@ export default {
isProduction: () => process.env.NODE_ENV === 'production',
getLanguage: () => getOriginalOption('lang'),
setLanguage: value => setOption('lang', value),
getTimezone: () => getOption('timeZone'),
setTimezone: value => setOption('timeZone', value),
isEnableDebug: () => getOption('debug'),
setEnableDebug: value => setOption('debug', value),
isEnableApplicationLock: () => getOption('applicationLock'),
+15 -6
View File
@@ -34,12 +34,20 @@ function isBoolean(val) {
return typeof(val) === 'boolean';
}
function parseDateFromUnixTime(unixTime) {
return moment.unix(unixTime);
function getTimezoneOffset(timezone) {
if (timezone) {
return moment().tz(timezone).format('Z');
} else {
return moment().format('Z');
}
}
function formatDate(date, format) {
return moment(date).format(format);
function getCurrentUnixTime() {
return moment().unix();
}
function parseDateFromUnixTime(unixTime) {
return moment.unix(unixTime);
}
function formatUnixTime(unixTime, format) {
@@ -107,7 +115,7 @@ function getMinuteLastUnixTime(date) {
}
function getTodayFirstUnixTime() {
return moment({ hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
return moment().set({ hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
}
function getTodayLastUnixTime() {
@@ -562,8 +570,9 @@ export default {
isString,
isNumber,
isBoolean,
getTimezoneOffset,
getCurrentUnixTime,
parseDateFromUnixTime,
formatDate,
formatUnixTime,
getUnixTime,
getYear,
+2
View File
@@ -680,6 +680,8 @@ export default {
'Filter Transaction Categories': 'Filter Transaction Categories',
'User Profile': 'User Profile',
'Language': 'Language',
'Timezone': 'Timezone',
'System Default': 'System Default',
'Auto Update Exchange Rates Data': 'Auto Update Exchange Rates Data',
'Enable Thousands Separator': 'Enable Thousands Separator',
'Currency Display Mode': 'Currency Display Mode',
+2
View File
@@ -680,6 +680,8 @@ export default {
'Filter Transaction Categories': '过滤交易类型',
'User Profile': '用户信息',
'Language': '语言',
'Timezone': '时区',
'System Default': '系统默认',
'Auto Update Exchange Rates Data': '自动更新汇率数据',
'Enable Thousands Separator': '启用千位分隔符',
'Currency Display Mode': '货币显示模式',
+34 -1
View File
@@ -6,7 +6,7 @@ import PincodeInput from 'vue-pincode-input';
import VueMoment from 'vue-moment';
import VueClipboard from 'vue-clipboard2';
import moment from 'moment';
import moment from 'moment-timezone';
import Framework7 from 'framework7/framework7-lite.esm.js';
import Framework7Dialog from 'framework7/components/dialog/dialog';
@@ -186,6 +186,7 @@ Vue.prototype.$logger = logger;
Vue.prototype.$webauthn = webauthn;
Vue.prototype.$settings = settings;
Vue.prototype.$locale = {
defaultTimezoneOffset: utils.getTimezoneOffset(),
getDefaultLanguage: getDefaultLanguage,
getAllLanguages: getAllLanguages,
getLanguage: getLanguage,
@@ -200,6 +201,31 @@ Vue.prototype.$locale = {
document.querySelector('html').setAttribute('lang', locale);
return locale;
},
getTimezone: function () {
return settings.getTimezone();
},
setTimezone: function (timezone) {
if (timezone) {
settings.setTimezone(timezone);
moment.tz.setDefault(timezone);
} else {
settings.setTimezone('');
moment.tz.setDefault();
}
},
getAllTimezones: function () {
const allTimezoneNames = moment.tz.names();
const allTimezoneInfos = [];
for (let i = 0; i < allTimezoneNames.length; i++) {
allTimezoneInfos.push({
name: allTimezoneNames[i],
displayName: `(UTC${utils.getTimezoneOffset(allTimezoneNames[i])}) ${allTimezoneNames[i]}`
});
}
return allTimezoneInfos;
},
getAllCurrencies: function () {
const allCurrencyCodes = currency.all;
const allCurrencies = [];
@@ -229,6 +255,13 @@ Vue.prototype.$locale = {
logger.info(`No language is set, use browser default ${getDefaultLanguage()}`);
this.setLanguage(getDefaultLanguage());
}
if (settings.getTimezone()) {
logger.info(`Current timezone is ${settings.getTimezone()}`);
this.setTimezone(settings.getTimezone());
} else {
logger.info(`No timezone is set, use browser default ${utils.getTimezoneOffset()} (maybe ${moment.tz.guess(true)})`);
}
}
};
+6 -6
View File
@@ -10,16 +10,16 @@ const exchangeRatesLocalStorageKey = 'lab_app_exchange_rates';
export function getLatestExchangeRates(context, { silent, force }) {
const currentExchangeRateData = context.state.latestExchangeRates;
const now = new Date();
const now = utils.getCurrentUnixTime();
if (!force) {
if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data &&
currentExchangeRateData.data.date === utils.formatDate(now, 'YYYY-MM-DD')) {
utils.formatUnixTime(currentExchangeRateData.data.updateTime, 'YYYY-MM-DD') === utils.formatUnixTime(now, 'YYYY-MM-DD')) {
return currentExchangeRateData.data;
}
if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data &&
utils.formatUnixTime(currentExchangeRateData.time, 'YYYY-MM-DD HH') === utils.formatDate(now, 'YYYY-MM-DD HH')) {
utils.formatUnixTime(currentExchangeRateData.time, 'YYYY-MM-DD HH') === utils.formatUnixTime(now, 'YYYY-MM-DD HH')) {
return currentExchangeRateData.data;
}
}
@@ -36,7 +36,7 @@ export function getLatestExchangeRates(context, { silent, force }) {
}
context.commit(STORE_LATEST_EXCHANGE_RATES, {
time: utils.getUnixTime(now),
time: now,
data: data.result
});
@@ -55,9 +55,9 @@ export function getLatestExchangeRates(context, { silent, force }) {
});
}
export function exchangeRatesLastUpdateDate(state) {
export function exchangeRatesLastUpdateTime(state) {
const exchangeRates = state.latestExchangeRates || {};
return exchangeRates && exchangeRates.data ? exchangeRates.data.date : null;
return exchangeRates && exchangeRates.data ? exchangeRates.data.updateTime : null;
}
export function getExchangedAmount(state) {
+2 -2
View File
@@ -85,7 +85,7 @@ import {
import {
getLatestExchangeRates,
exchangeRatesLastUpdateDate,
exchangeRatesLastUpdateTime,
getExchangedAmount,
getExchangeRatesFromLocalStorage,
setExchangeRatesToLocalStorage,
@@ -198,7 +198,7 @@ const stores = {
currentUserFirstDayOfWeek,
// exchange rates
exchangeRatesLastUpdateDate,
exchangeRatesLastUpdateTime,
getExchangedAmount,
// account
+28 -3
View File
@@ -24,7 +24,7 @@
<f7-list-item
:key="currentLocale + '_lang'"
:title="$t('Language')"
smart-select :smart-select-params="{ openIn: 'sheet', closeOnSelect: true, sheetCloseLinkText: $t('Done') }">
smart-select :smart-select-params="{ openIn: 'sheet', closeOnSelect: true, sheetCloseLinkText: $t('Done'), scrollToSelectedItem: true }">
<select v-model="currentLocale">
<option v-for="(lang, locale) in allLanguages"
:key="locale"
@@ -32,6 +32,17 @@
</select>
</f7-list-item>
<f7-list-item
:title="$t('Timezone')"
smart-select :smart-select-params="{ openIn: 'popup', searchbar: true, searchbarPlaceholder: $t('Timezone'), searchbarDisableText: $t('Cancel'), closeOnSelect: true, popupCloseLinkText: $t('Done'), scrollToSelectedItem: true }">
<select v-model="currentTimezone">
<option value="">{{ `(UTC${defaultTimezoneOffset}) ${$t('System Default')}` }}</option>
<option v-for="timezone in allTimezones"
:key="timezone.name"
:value="timezone.name">{{ timezone.displayName }}</option>
</select>
</f7-list-item>
<f7-list-item :title="$t('Application Lock')" :after="isEnableApplicationLock ? $t('Enabled') : $t('Disabled')" link="/app_lock"></f7-list-item>
<f7-list-item :title="$t('Exchange Rates Data')" :after="exchangeRatesLastUpdateDate" link="/exchange_rates"></f7-list-item>
@@ -104,6 +115,12 @@ export default {
allLanguages() {
return this.$locale.getAllLanguages();
},
allTimezones() {
return this.$locale.getAllTimezones();
},
defaultTimezoneOffset() {
return this.$locale.defaultTimezoneOffset;
},
currentLocale: {
get: function () {
return this.$i18n.locale;
@@ -112,6 +129,14 @@ export default {
this.$locale.setLanguage(value);
}
},
currentTimezone: {
get: function () {
return this.$locale.getTimezone();
},
set: function (value) {
this.$locale.setTimezone(value);
}
},
currentNickName() {
return this.$store.getters.currentUserNickname || this.$t('User');
},
@@ -119,8 +144,8 @@ export default {
return this.$settings.isDataExportingEnabled();
},
exchangeRatesLastUpdateDate() {
const exchangeRatesLastUpdateDate = this.$store.getters.exchangeRatesLastUpdateDate;
return exchangeRatesLastUpdateDate ? this.$utilities.formatDate(exchangeRatesLastUpdateDate, this.$t('format.date.long')) : '';
const exchangeRatesLastUpdateTime = this.$store.getters.exchangeRatesLastUpdateTime;
return exchangeRatesLastUpdateTime ? this.$utilities.formatUnixTime(exchangeRatesLastUpdateTime, this.$t('format.date.long')) : '';
},
isAutoUpdateExchangeRatesData: {
get: function () {
+4 -4
View File
@@ -250,7 +250,7 @@ export default {
data() {
const self = this;
const query = self.$f7route.query;
const now = new Date();
const now = self.$utilities.getCurrentUnixTime();
let defaultType = self.$constants.transaction.allTransactionTypes.Expense;
@@ -265,8 +265,8 @@ export default {
editTransactionId: null,
transaction: {
type: defaultType,
unixTime: self.$utilities.getUnixTime(now),
time: self.$utilities.formatDate(now, 'YYYY-MM-DDTHH:mm'),
unixTime: now,
time: self.$utilities.formatUnixTime(now, 'YYYY-MM-DDTHH:mm'),
expenseCategory: '',
incomeCategory: '',
transferCategory: '',
@@ -488,7 +488,7 @@ export default {
},
'transaction.time': function (newValue) {
if (!newValue) {
newValue = this.$utilities.formatDate(new Date(), 'YYYY-MM-DDTHH:mm');
newValue = this.$utilities.formatUnixTime(this.$utilities.getCurrentUnixTime(), 'YYYY-MM-DDTHH:mm');
this.transaction.time = newValue;
}