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" "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": { "move-concurrently": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "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", "js-cookie": "^2.2.1",
"line-awesome": "^1.3.0", "line-awesome": "^1.3.0",
"moment": "^2.29.1", "moment": "^2.29.1",
"moment-timezone": "^0.5.33",
"register-service-worker": "^1.7.2", "register-service-worker": "^1.7.2",
"ua-parser-js": "^0.7.24", "ua-parser-js": "^0.7.24",
"vue": "^2.6.12", "vue": "^2.6.12",
@@ -2,6 +2,7 @@ package exchangerates
import ( import (
"encoding/xml" "encoding/xml"
"time"
"github.com/mayswind/lab/pkg/core" "github.com/mayswind/lab/pkg/core"
"github.com/mayswind/lab/pkg/errs" "github.com/mayswind/lab/pkg/errs"
@@ -14,6 +15,9 @@ const euroCentralBankExchangeRateReferenceUrl = "https://www.ecb.europa.eu/stats
const euroCentralBankDataSource = "European Central Bank" const euroCentralBankDataSource = "European Central Bank"
const euroCentralBankBaseCurrency = "EUR" 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 // EuroCentralBankDataSource defines the structure of exchange rates data source of euro central bank
type EuroCentralBankDataSource struct { type EuroCentralBankDataSource struct {
ExchangeRatesDataSource ExchangeRatesDataSource
@@ -55,10 +59,23 @@ func (e *EuroCentralBankExchangeRateData) ToLatestExchangeRateResponse() *models
exchangeRates[i] = latestEuroCentralBankExchangeRate.ExchangeRates[i].ToLatestExchangeRate() 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{ latestExchangeRateResp := &models.LatestExchangeRateResponse{
DataSource: euroCentralBankDataSource, DataSource: euroCentralBankDataSource,
ReferenceUrl: euroCentralBankExchangeRateReferenceUrl, ReferenceUrl: euroCentralBankExchangeRateReferenceUrl,
Date: latestEuroCentralBankExchangeRate.Date, UpdateTime: updateTime.Unix(),
BaseCurrency: euroCentralBankBaseCurrency, BaseCurrency: euroCentralBankBaseCurrency,
ExchangeRates: exchangeRates, ExchangeRates: exchangeRates,
} }
+1 -1
View File
@@ -4,7 +4,7 @@ package models
type LatestExchangeRateResponse struct { type LatestExchangeRateResponse struct {
DataSource string `json:"dataSource"` DataSource string `json:"dataSource"`
ReferenceUrl string `json:"referenceUrl"` ReferenceUrl string `json:"referenceUrl"`
Date string `json:"date"` UpdateTime int64 `json:"updateTime"`
BaseCurrency string `json:"baseCurrency"` BaseCurrency string `json:"baseCurrency"`
ExchangeRates []*LatestExchangeRate `json:"exchangeRates"` ExchangeRates []*LatestExchangeRate `json:"exchangeRates"`
} }
@@ -52,7 +52,7 @@ export default {
data() { data() {
const self = this; const self = this;
let minDate = self.$utilities.getTodayFirstUnixTime(); let minDate = self.$utilities.getTodayFirstUnixTime();
let maxDate = self.$utilities.getUnixTime(new Date()); let maxDate = self.$utilities.getCurrentUnixTime();
if (self.minTime) { if (self.minTime) {
minDate = self.minTime; minDate = self.minTime;
@@ -70,12 +70,12 @@ export default {
watch: { watch: {
'currentMinDate': function (newValue) { 'currentMinDate': function (newValue) {
if (!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) { 'currentMaxDate': function (newValue) {
if (!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 = { const defaultSettings = {
lang: 'en', lang: 'en',
timeZone: '',
debug: false, debug: false,
applicationLock: false, applicationLock: false,
applicationLockWebAuthn: false, applicationLockWebAuthn: false,
@@ -128,6 +129,8 @@ export default {
isProduction: () => process.env.NODE_ENV === 'production', isProduction: () => process.env.NODE_ENV === 'production',
getLanguage: () => getOriginalOption('lang'), getLanguage: () => getOriginalOption('lang'),
setLanguage: value => setOption('lang', value), setLanguage: value => setOption('lang', value),
getTimezone: () => getOption('timeZone'),
setTimezone: value => setOption('timeZone', value),
isEnableDebug: () => getOption('debug'), isEnableDebug: () => getOption('debug'),
setEnableDebug: value => setOption('debug', value), setEnableDebug: value => setOption('debug', value),
isEnableApplicationLock: () => getOption('applicationLock'), isEnableApplicationLock: () => getOption('applicationLock'),
+15 -6
View File
@@ -34,12 +34,20 @@ function isBoolean(val) {
return typeof(val) === 'boolean'; return typeof(val) === 'boolean';
} }
function parseDateFromUnixTime(unixTime) { function getTimezoneOffset(timezone) {
return moment.unix(unixTime); if (timezone) {
return moment().tz(timezone).format('Z');
} else {
return moment().format('Z');
}
} }
function formatDate(date, format) { function getCurrentUnixTime() {
return moment(date).format(format); return moment().unix();
}
function parseDateFromUnixTime(unixTime) {
return moment.unix(unixTime);
} }
function formatUnixTime(unixTime, format) { function formatUnixTime(unixTime, format) {
@@ -107,7 +115,7 @@ function getMinuteLastUnixTime(date) {
} }
function getTodayFirstUnixTime() { 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() { function getTodayLastUnixTime() {
@@ -562,8 +570,9 @@ export default {
isString, isString,
isNumber, isNumber,
isBoolean, isBoolean,
getTimezoneOffset,
getCurrentUnixTime,
parseDateFromUnixTime, parseDateFromUnixTime,
formatDate,
formatUnixTime, formatUnixTime,
getUnixTime, getUnixTime,
getYear, getYear,
+2
View File
@@ -680,6 +680,8 @@ export default {
'Filter Transaction Categories': 'Filter Transaction Categories', 'Filter Transaction Categories': 'Filter Transaction Categories',
'User Profile': 'User Profile', 'User Profile': 'User Profile',
'Language': 'Language', 'Language': 'Language',
'Timezone': 'Timezone',
'System Default': 'System Default',
'Auto Update Exchange Rates Data': 'Auto Update Exchange Rates Data', 'Auto Update Exchange Rates Data': 'Auto Update Exchange Rates Data',
'Enable Thousands Separator': 'Enable Thousands Separator', 'Enable Thousands Separator': 'Enable Thousands Separator',
'Currency Display Mode': 'Currency Display Mode', 'Currency Display Mode': 'Currency Display Mode',
+2
View File
@@ -680,6 +680,8 @@ export default {
'Filter Transaction Categories': '过滤交易类型', 'Filter Transaction Categories': '过滤交易类型',
'User Profile': '用户信息', 'User Profile': '用户信息',
'Language': '语言', 'Language': '语言',
'Timezone': '时区',
'System Default': '系统默认',
'Auto Update Exchange Rates Data': '自动更新汇率数据', 'Auto Update Exchange Rates Data': '自动更新汇率数据',
'Enable Thousands Separator': '启用千位分隔符', 'Enable Thousands Separator': '启用千位分隔符',
'Currency Display Mode': '货币显示模式', 'Currency Display Mode': '货币显示模式',
+34 -1
View File
@@ -6,7 +6,7 @@ import PincodeInput from 'vue-pincode-input';
import VueMoment from 'vue-moment'; import VueMoment from 'vue-moment';
import VueClipboard from 'vue-clipboard2'; import VueClipboard from 'vue-clipboard2';
import moment from 'moment'; import moment from 'moment-timezone';
import Framework7 from 'framework7/framework7-lite.esm.js'; import Framework7 from 'framework7/framework7-lite.esm.js';
import Framework7Dialog from 'framework7/components/dialog/dialog'; import Framework7Dialog from 'framework7/components/dialog/dialog';
@@ -186,6 +186,7 @@ Vue.prototype.$logger = logger;
Vue.prototype.$webauthn = webauthn; Vue.prototype.$webauthn = webauthn;
Vue.prototype.$settings = settings; Vue.prototype.$settings = settings;
Vue.prototype.$locale = { Vue.prototype.$locale = {
defaultTimezoneOffset: utils.getTimezoneOffset(),
getDefaultLanguage: getDefaultLanguage, getDefaultLanguage: getDefaultLanguage,
getAllLanguages: getAllLanguages, getAllLanguages: getAllLanguages,
getLanguage: getLanguage, getLanguage: getLanguage,
@@ -200,6 +201,31 @@ Vue.prototype.$locale = {
document.querySelector('html').setAttribute('lang', locale); document.querySelector('html').setAttribute('lang', locale);
return 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 () { getAllCurrencies: function () {
const allCurrencyCodes = currency.all; const allCurrencyCodes = currency.all;
const allCurrencies = []; const allCurrencies = [];
@@ -229,6 +255,13 @@ Vue.prototype.$locale = {
logger.info(`No language is set, use browser default ${getDefaultLanguage()}`); logger.info(`No language is set, use browser default ${getDefaultLanguage()}`);
this.setLanguage(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 }) { export function getLatestExchangeRates(context, { silent, force }) {
const currentExchangeRateData = context.state.latestExchangeRates; const currentExchangeRateData = context.state.latestExchangeRates;
const now = new Date(); const now = utils.getCurrentUnixTime();
if (!force) { if (!force) {
if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data && 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; return currentExchangeRateData.data;
} }
if (currentExchangeRateData && currentExchangeRateData.time && 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; return currentExchangeRateData.data;
} }
} }
@@ -36,7 +36,7 @@ export function getLatestExchangeRates(context, { silent, force }) {
} }
context.commit(STORE_LATEST_EXCHANGE_RATES, { context.commit(STORE_LATEST_EXCHANGE_RATES, {
time: utils.getUnixTime(now), time: now,
data: data.result 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 || {}; const exchangeRates = state.latestExchangeRates || {};
return exchangeRates && exchangeRates.data ? exchangeRates.data.date : null; return exchangeRates && exchangeRates.data ? exchangeRates.data.updateTime : null;
} }
export function getExchangedAmount(state) { export function getExchangedAmount(state) {
+2 -2
View File
@@ -85,7 +85,7 @@ import {
import { import {
getLatestExchangeRates, getLatestExchangeRates,
exchangeRatesLastUpdateDate, exchangeRatesLastUpdateTime,
getExchangedAmount, getExchangedAmount,
getExchangeRatesFromLocalStorage, getExchangeRatesFromLocalStorage,
setExchangeRatesToLocalStorage, setExchangeRatesToLocalStorage,
@@ -198,7 +198,7 @@ const stores = {
currentUserFirstDayOfWeek, currentUserFirstDayOfWeek,
// exchange rates // exchange rates
exchangeRatesLastUpdateDate, exchangeRatesLastUpdateTime,
getExchangedAmount, getExchangedAmount,
// account // account
+28 -3
View File
@@ -24,7 +24,7 @@
<f7-list-item <f7-list-item
:key="currentLocale + '_lang'" :key="currentLocale + '_lang'"
:title="$t('Language')" :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"> <select v-model="currentLocale">
<option v-for="(lang, locale) in allLanguages" <option v-for="(lang, locale) in allLanguages"
:key="locale" :key="locale"
@@ -32,6 +32,17 @@
</select> </select>
</f7-list-item> </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('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> <f7-list-item :title="$t('Exchange Rates Data')" :after="exchangeRatesLastUpdateDate" link="/exchange_rates"></f7-list-item>
@@ -104,6 +115,12 @@ export default {
allLanguages() { allLanguages() {
return this.$locale.getAllLanguages(); return this.$locale.getAllLanguages();
}, },
allTimezones() {
return this.$locale.getAllTimezones();
},
defaultTimezoneOffset() {
return this.$locale.defaultTimezoneOffset;
},
currentLocale: { currentLocale: {
get: function () { get: function () {
return this.$i18n.locale; return this.$i18n.locale;
@@ -112,6 +129,14 @@ export default {
this.$locale.setLanguage(value); this.$locale.setLanguage(value);
} }
}, },
currentTimezone: {
get: function () {
return this.$locale.getTimezone();
},
set: function (value) {
this.$locale.setTimezone(value);
}
},
currentNickName() { currentNickName() {
return this.$store.getters.currentUserNickname || this.$t('User'); return this.$store.getters.currentUserNickname || this.$t('User');
}, },
@@ -119,8 +144,8 @@ export default {
return this.$settings.isDataExportingEnabled(); return this.$settings.isDataExportingEnabled();
}, },
exchangeRatesLastUpdateDate() { exchangeRatesLastUpdateDate() {
const exchangeRatesLastUpdateDate = this.$store.getters.exchangeRatesLastUpdateDate; const exchangeRatesLastUpdateTime = this.$store.getters.exchangeRatesLastUpdateTime;
return exchangeRatesLastUpdateDate ? this.$utilities.formatDate(exchangeRatesLastUpdateDate, this.$t('format.date.long')) : ''; return exchangeRatesLastUpdateTime ? this.$utilities.formatUnixTime(exchangeRatesLastUpdateTime, this.$t('format.date.long')) : '';
}, },
isAutoUpdateExchangeRatesData: { isAutoUpdateExchangeRatesData: {
get: function () { get: function () {
+4 -4
View File
@@ -250,7 +250,7 @@ export default {
data() { data() {
const self = this; const self = this;
const query = self.$f7route.query; const query = self.$f7route.query;
const now = new Date(); const now = self.$utilities.getCurrentUnixTime();
let defaultType = self.$constants.transaction.allTransactionTypes.Expense; let defaultType = self.$constants.transaction.allTransactionTypes.Expense;
@@ -265,8 +265,8 @@ export default {
editTransactionId: null, editTransactionId: null,
transaction: { transaction: {
type: defaultType, type: defaultType,
unixTime: self.$utilities.getUnixTime(now), unixTime: now,
time: self.$utilities.formatDate(now, 'YYYY-MM-DDTHH:mm'), time: self.$utilities.formatUnixTime(now, 'YYYY-MM-DDTHH:mm'),
expenseCategory: '', expenseCategory: '',
incomeCategory: '', incomeCategory: '',
transferCategory: '', transferCategory: '',
@@ -488,7 +488,7 @@ export default {
}, },
'transaction.time': function (newValue) { 'transaction.time': function (newValue) {
if (!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; this.transaction.time = newValue;
} }