mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-14 06:57:35 +08:00
support set timezone
This commit is contained in:
Generated
+8
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
@@ -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)})`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user