use local instance timezone everywhere #151

This commit is contained in:
lesion
2022-04-27 11:58:58 +02:00
parent 6776b1b1b1
commit 35e44a8a80
7 changed files with 72 additions and 65 deletions

View File

@@ -9,6 +9,7 @@
@update:from-page='updatePage' @update:from-page='updatePage'
:locale='$i18n.locale' :locale='$i18n.locale'
:attributes='attributes' :attributes='attributes'
:timezone='settings.instance_timezone'
transition='fade' transition='fade'
aria-label='Calendar' aria-label='Calendar'
is-expanded is-expanded
@@ -26,8 +27,8 @@ export default {
events: { type: Array, default: () => [] } events: { type: Array, default: () => [] }
}, },
data () { data () {
const month = dayjs().month() + 1 const month = dayjs.tz().month() + 1
const year = dayjs().year() const year = dayjs.tz().year()
return { return {
selectedDate: null, selectedDate: null,
page: { month, year } page: { month, year }
@@ -42,9 +43,7 @@ export default {
methods: { methods: {
...mapActions(['updateEvents', 'showPastEvents']), ...mapActions(['updateEvents', 'showPastEvents']),
updatePage (page) { updatePage (page) {
return new Promise((resolve, reject) => {
this.$emit('monthchange', page) this.$emit('monthchange', page)
})
}, },
click (day) { click (day) {
this.$emit('dayclick', day) this.$emit('dayclick', day)

View File

@@ -78,8 +78,7 @@ export default {
todayEvents () { todayEvents () {
const start = dayjs(this.value.from).startOf('day').unix() const start = dayjs(this.value.from).startOf('day').unix()
const end = dayjs(this.value.from).endOf('day').unix() const end = dayjs(this.value.from).endOf('day').unix()
const events = this.events.filter(e => e.start_datetime >= start && e.start_datetime <= end) return this.events.filter(e => e.start_datetime >= start && e.start_datetime <= end)
return events
}, },
attributes () { attributes () {
return attributesFromEvents(this.events, this.tags) return attributesFromEvents(this.events, this.tags)
@@ -92,10 +91,10 @@ export default {
}, },
fromHour () { fromHour () {
return this.value.from && this.value.fromHour ? dayjs(this.value.from).format('HH:mm') : null return this.value.from && this.value.fromHour ? dayjs.tz(this.value.from).format('HH:mm') : null
}, },
dueHour () { dueHour () {
return this.value.due && this.value.dueHour ? dayjs(this.value.due).format('HH:mm') : null return this.value.due && this.value.dueHour ? dayjs.tz(this.value.due).format('HH:mm') : null
}, },
hourList () { hourList () {
const hourList = [] const hourList = []
@@ -196,7 +195,7 @@ export default {
} else if (what === 'fromHour') { } else if (what === 'fromHour') {
if (value) { if (value) {
const [hour, minute] = value.split(':') const [hour, minute] = value.split(':')
const from = dayjs(this.value.from).hour(hour).minute(minute).second(0) const from = dayjs.tz(this.value.from).hour(hour).minute(minute).second(0)
this.$emit('input', { ...this.value, from, fromHour: true }) this.$emit('input', { ...this.value, from, fromHour: true })
} else { } else {
this.$emit('input', { ...this.value, fromHour: false }) this.$emit('input', { ...this.value, fromHour: false })
@@ -204,7 +203,7 @@ export default {
} else if (what === 'dueHour') { } else if (what === 'dueHour') {
if (value) { if (value) {
const [hour, minute] = value.split(':') const [hour, minute] = value.split(':')
const fromHour = dayjs(this.value.from).hour() const fromHour = dayjs.tz(this.value.from).hour()
// add a day // add a day
let due = dayjs(this.value.from) let due = dayjs(this.value.from)
@@ -226,20 +225,20 @@ export default {
let from = value.start let from = value.start
let due = value.end let due = value.end
if (this.value.fromHour) { if (this.value.fromHour) {
from = dayjs(value.start).hour(dayjs(this.value.from).hour()) from = dayjs.tz(value.start).hour(dayjs.tz(this.value.from).hour())
} }
if (this.value.dueHour) { if (this.value.dueHour) {
due = dayjs(value.end).hour(dayjs(this.value.due).hour()) due = dayjs.tz(value.end).hour(dayjs.tz(this.value.due).hour())
} }
this.$emit('input', { ...this.value, from, due }) this.$emit('input', { ...this.value, from, due })
} else { } else {
let from = value let from = value
let due = this.value.due let due = this.value.due
if (this.value.fromHour) { if (this.value.fromHour) {
from = dayjs(value).hour(dayjs(this.value.from).hour()) from = dayjs.tz(value).hour(dayjs.tz(this.value.from).hour())
} }
if (this.value.dueHour && this.value.due) { if (this.value.dueHour && this.value.due) {
due = dayjs(value).hour(dayjs(this.value.due).hour()) due = dayjs.tz(value).hour(dayjs.tz(this.value.due).hour())
} }
this.$emit('input', { ...this.value, from, due }) this.$emit('input', { ...this.value, from, due })
} }

View File

@@ -84,7 +84,7 @@ export default {
validate ({ store }) { validate ({ store }) {
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event) return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
}, },
async asyncData ({ params, $axios, error, store }) { async asyncData ({ params, $axios, error }) {
if (params.edit) { if (params.edit) {
const data = { event: { place: {}, media: [] } } const data = { event: { place: {}, media: [] } }
data.id = params.edit data.id = params.edit
@@ -101,8 +101,8 @@ export default {
data.event.place.address = event.place.address || '' data.event.place.address = event.place.address || ''
data.date = { data.date = {
recurrent: event.recurrent, recurrent: event.recurrent,
from: new Date(dayjs.unix(event.start_datetime)), from: dayjs.unix(event.start_datetime).toDate(),
due: new Date(dayjs.unix(event.end_datetime)), due: dayjs.unix(event.end_datetime).toDate(),
multidate: event.multidate, multidate: event.multidate,
fromHour: true, fromHour: true,
dueHour: true dueHour: true
@@ -118,8 +118,8 @@ export default {
return {} return {}
}, },
data () { data () {
const month = dayjs().month() + 1 const month = dayjs.tz().month() + 1
const year = dayjs().year() const year = dayjs.tz().year()
return { return {
mdiFileImport, mdiFormatTitle, mdiTagMultiple, mdiCloseCircle, mdiFileImport, mdiFormatTitle, mdiTagMultiple, mdiCloseCircle,
valid: false, valid: false,

View File

@@ -1,26 +1,25 @@
<template lang="pug"> <template lang="pug">
v-container#home(fluid) v-container#home(fluid)
//- Announcements //- Announcements
#announcements.mx-1.mt-1(v-if='announcements.length') #announcements.mx-1.mt-1(v-if='announcements.length')
Announcement(v-for='announcement in announcements' :key='`a_${announcement.id}`' :announcement='announcement') Announcement(v-for='announcement in announcements' :key='`a_${announcement.id}`' :announcement='announcement')
//- Calendar and search bar //- Calendar and search bar
v-row.pt-0.pt-sm-2.pl-0.pl-sm-2 v-row.pt-0.pt-sm-2.pl-0.pl-sm-2
#calh.col-xl-5.col-lg-5.col-md-7.col-sm-12.col-xs-12.pa-4.pa-sm-3 #calh.col-xl-5.col-lg-5.col-md-7.col-sm-12.col-xs-12.pa-4.pa-sm-3
//- this is needed as v-calendar does not support SSR //- this is needed as v-calendar does not support SSR
//- https://github.com/nathanreyes/v-calendar/issues/336 //- https://github.com/nathanreyes/v-calendar/issues/336
client-only(placeholder='Calendar unavailable without js') client-only(placeholder='Calendar unavailable without js')
Calendar(@dayclick='dayChange' @monthchange='monthChange' :events='filteredEvents') Calendar(@dayclick='dayChange' @monthchange='monthChange' :events='filteredEvents')
.col.pt-0.pt-md-2 .col.pt-0.pt-md-2
Search(:filters='filters' @update='updateFilters') Search(:filters='filters' @update='updateFilters')
v-chip(v-if='selectedDay' close :close-icon='mdiCloseCircle' @click:close='dayChange()') {{selectedDay}} v-chip(v-if='selectedDay' close :close-icon='mdiCloseCircle' @click:close='dayChange()') {{selectedDay}}
//- Events
#events.mb-2.mt-1.pl-1.pl-sm-2
Event(:event='event' @destroy='destroy' v-for='(event, idx) in visibleEvents' :lazy='idx>2' :key='event.id' @tagclick='tagClick' @placeclick='placeClick')
//- Events
#events.mb-2.mt-1.pl-1.pl-sm-2
Event(:event='event' @destroy='destroy' v-for='(event, idx) in visibleEvents' :lazy='idx>2' :key='event.id' @tagclick='tagClick' @placeclick='placeClick')
</template> </template>
<script> <script>
@@ -37,7 +36,7 @@ export default {
name: 'Index', name: 'Index',
components: { Event, Search, Announcement, Calendar }, components: { Event, Search, Announcement, Calendar },
middleware: 'setup', middleware: 'setup',
async asyncData ({ params, $api, store }) { async asyncData ({ $api }) {
const events = await $api.getEvents({ const events = await $api.getEvents({
start: dayjs().startOf('month').unix(), start: dayjs().startOf('month').unix(),
end: null, end: null,
@@ -45,13 +44,13 @@ export default {
}) })
return { events } return { events }
}, },
data ({ $store }) { data () {
return { return {
mdiCloseCircle, mdiCloseCircle,
first: true, first: true,
isCurrentMonth: true, isCurrentMonth: true,
now: dayjs().unix(), now: dayjs().unix(),
date: dayjs().format('YYYY-MM-DD'), date: dayjs.tz().format('YYYY-MM-DD'),
events: [], events: [],
start: dayjs().startOf('month').unix(), start: dayjs().startOf('month').unix(),
end: null, end: null,
@@ -113,10 +112,8 @@ export default {
} }
}, },
methods: { methods: {
// onIntersect (isIntersecting, eventId) {
// this.intersecting[eventId] = isIntersecting
// },
...mapActions(['setFilters']), ...mapActions(['setFilters']),
destroy (id) { destroy (id) {
this.events = this.events.filter(e => e.id !== id) this.events = this.events.filter(e => e.id !== id)
}, },
@@ -158,16 +155,15 @@ export default {
this.selectedDay = null this.selectedDay = null
// check if current month is selected // check if current month is selected
if (month - 1 === dayjs().month() && year === dayjs().year()) { if (month - 1 === dayjs.tz().month() && year === dayjs.tz().year()) {
this.isCurrentMonth = true this.isCurrentMonth = true
this.start = dayjs().startOf('month').unix() this.start = dayjs().startOf('month').unix()
this.date = dayjs().format('YYYY-MM-DD') this.date = dayjs.tz().format('YYYY-MM-DD')
} else { } else {
this.isCurrentMonth = false this.isCurrentMonth = false
this.date = '' this.date = ''
this.start = dayjs().year(year).month(month - 1).startOf('month').unix() // .startOf('week').unix() this.start = dayjs().year(year).month(month - 1).startOf('month').unix() // .startOf('week').unix()
} }
// TODO: check if calendar view is double
this.end = dayjs().year(year).month(month).endOf('month').unix() // .endOf('week').unix() this.end = dayjs().year(year).month(month).endOf('month').unix() // .endOf('week').unix()
this.updateEvents() this.updateEvents()
}, },
@@ -175,7 +171,7 @@ export default {
this.setFilters(filters) this.setFilters(filters)
}, },
dayChange (day) { dayChange (day) {
this.selectedDay = day ? dayjs(day).format('YYYY-MM-DD') : null this.selectedDay = day ? dayjs.tz(day).format('YYYY-MM-DD') : null
} }
} }
} }

View File

@@ -7,12 +7,15 @@ import localizedFormat from 'dayjs/plugin/localizedFormat'
import 'dayjs/locale/it' import 'dayjs/locale/it'
import 'dayjs/locale/en'
import 'dayjs/locale/es' import 'dayjs/locale/es'
import 'dayjs/locale/ca' import 'dayjs/locale/ca'
import 'dayjs/locale/pl' import 'dayjs/locale/pl'
import 'dayjs/locale/eu' import 'dayjs/locale/eu'
import 'dayjs/locale/nb' import 'dayjs/locale/nb'
import 'dayjs/locale/fr' import 'dayjs/locale/fr'
import 'dayjs/locale/de'
import 'dayjs/locale/gl'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
dayjs.extend(utc) dayjs.extend(utc)
@@ -23,25 +26,27 @@ export default ({ app, store }) => {
// set timezone to instance_timezone!! // set timezone to instance_timezone!!
// to show local time relative to event's place // to show local time relative to event's place
// not where in the world I'm looking at the page from // not where in the world I'm looking at the page from
dayjs.tz.setDefault(store.state.settings.instance_timezone) const instance_timezone = store.state.settings.instance_timezone
dayjs.locale(store.state.locale) const locale = store.state.locale
dayjs.tz.setDefault(instance_timezone)
dayjs.locale(locale)
// replace links with anchors // replace links with anchors
// TODO: remove fb tracking id? // TODO: remove fb tracking id?
Vue.filter('linkify', value => value.replace(/(https?:\/\/([^\s]+))/g, '<a href="$1">$2</a>')) Vue.filter('linkify', value => value.replace(/(https?:\/\/([^\s]+))/g, '<a href="$1">$2</a>'))
Vue.filter('url2host', url => url.match(/^https?:\/\/(.[^/:]+)/i)[1]) Vue.filter('url2host', url => url.match(/^https?:\/\/(.[^/:]+)/i)[1])
Vue.filter('datetime', value => dayjs(value).locale(store.state.locale).format('ddd, D MMMM HH:mm')) Vue.filter('datetime', value => dayjs.tz(value).locale(locale).format('ddd, D MMMM HH:mm'))
Vue.filter('dateFormat', (value, format) => dayjs(value).format(format)) Vue.filter('dateFormat', (value, format) => dayjs.tz(value).format(format))
Vue.filter('unixFormat', (timestamp, format) => dayjs.unix(timestamp).format(format)) Vue.filter('unixFormat', (timestamp, format) => dayjs.unix(timestamp).tz(instance_timezone).format(format))
// shown in mobile homepage // shown in mobile homepage
Vue.filter('day', value => dayjs.unix(value).locale(store.state.locale).format('dddd, D MMM')) Vue.filter('day', value => dayjs.unix(value).tz(instance_timezone).locale(store.state.locale).format('dddd, D MMM'))
Vue.filter('mediaURL', (event, type) => { Vue.filter('mediaURL', (event, type, format = '.jpg') => {
if (event.media && event.media.length) { if (event.media && event.media.length) {
if (type === 'alt') { if (type === 'alt') {
return event.media[0].name return event.media[0].name
} else { } else {
return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + event.media[0].url return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + event.media[0].url.replace(/.jpg$/, format)
} }
} else if (type !== 'alt') { } else if (type !== 'alt') {
return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + 'logo.svg' return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + 'logo.svg'
@@ -49,16 +54,16 @@ export default ({ app, store }) => {
return '' return ''
}) })
Vue.filter('from', timestamp => dayjs.unix(timestamp).fromNow()) Vue.filter('from', timestamp => dayjs.unix(timestamp).tz(instance_timezone).fromNow())
Vue.filter('recurrentDetail', event => { Vue.filter('recurrentDetail', event => {
const parent = event.parent const parent = event.parent
const { frequency, type } = parent.recurrent const { frequency, type } = parent.recurrent
let recurrent let recurrent
if (frequency === '1w' || frequency === '2w') { if (frequency === '1w' || frequency === '2w') {
recurrent = app.i18n.t(`event.recurrent_${frequency}_days`, { days: dayjs.unix(parent.start_datetime).format('dddd') }) recurrent = app.i18n.t(`event.recurrent_${frequency}_days`, { days: dayjs.unix(parent.start_datetime).tz(instance_timezone).format('dddd') })
} else if (frequency === '1m' || frequency === '2m') { } else if (frequency === '1m' || frequency === '2m') {
const d = type === 'ordinal' ? dayjs.unix(parent.start_datetime).date() : dayjs.unix(parent.start_datetime).format('dddd') const d = type === 'ordinal' ? dayjs.unix(parent.start_datetime).date() : dayjs.unix(parent.start_datetime).tz(instance_timezone).format('dddd')
if (type === 'ordinal') { if (type === 'ordinal') {
recurrent = app.i18n.t(`event.recurrent_${frequency}_days`, { days: d }) recurrent = app.i18n.t(`event.recurrent_${frequency}_days`, { days: d })
} else { } else {
@@ -70,8 +75,8 @@ export default ({ app, store }) => {
}) })
Vue.filter('when', (event) => { Vue.filter('when', (event) => {
const start = dayjs.unix(event.start_datetime) const start = dayjs.unix(event.start_datetime).tz(instance_timezone)
const end = dayjs.unix(event.end_datetime) const end = dayjs.unix(event.end_datetime).tz(instance_timezone)
// const normal = `${start.format('dddd, D MMMM (HH:mm-')}${end.format('HH:mm) ')}` // const normal = `${start.format('dddd, D MMMM (HH:mm-')}${end.format('HH:mm) ')}`
// // recurrent event // // recurrent event

View File

@@ -1,5 +1,4 @@
const config = require('../../config') const config = require('../../config')
const moment = require('dayjs')
const { htmlToText } = require('html-to-text') const { htmlToText } = require('html-to-text')
const { Model, DataTypes } = require('sequelize') const { Model, DataTypes } = require('sequelize')
@@ -14,9 +13,12 @@ const Place = require('./place')
const User = require('./user') const User = require('./user')
const Tag = require('./tag') const Tag = require('./tag')
const utc = require('dayjs/plugin/utc')
const dayjs = require('dayjs') const dayjs = require('dayjs')
const timezone = require('dayjs/plugin/timezone')
const utc = require('dayjs/plugin/utc')
dayjs.extend(utc) dayjs.extend(utc)
dayjs.extend(timezone)
class Event extends Model {} class Event extends Model {}
@@ -76,7 +78,7 @@ Event.prototype.toAP = function (username, locale, to = []) {
const plainDescription = htmlToText(this.description && this.description.replace('\n', '').slice(0, 1000)) const plainDescription = htmlToText(this.description && this.description.replace('\n', '').slice(0, 1000))
const content = ` const content = `
📍 ${this.place && this.place.name} 📍 ${this.place && this.place.name}
📅 ${moment.unix(this.start_datetime).locale(locale).format('dddd, D MMMM (HH:mm)')} 📅 ${dayjs.unix(this.start_datetime).tz().locale(locale).format('dddd, D MMMM (HH:mm)')}
${plainDescription} ${plainDescription}
` `
@@ -99,8 +101,8 @@ Event.prototype.toAP = function (username, locale, to = []) {
name: this.title, name: this.title,
url: `${config.baseurl}/event/${this.slug || this.id}`, url: `${config.baseurl}/event/${this.slug || this.id}`,
type: 'Event', type: 'Event',
startTime: moment.unix(this.start_datetime).locale(locale).format(), startTime: dayjs.unix(this.start_datetime).tz().locale(locale).format(),
endTime: this.end_datetime ? moment.unix(this.end_datetime).locale(locale).format() : null, endTime: this.end_datetime ? dayjs.unix(this.end_datetime).tz().locale(locale).format() : null,
location: { location: {
name: this.place.name, name: this.place.name,
address: this.place.address address: this.place.address

View File

@@ -5,6 +5,10 @@ module.exports = function () {
const log = require('../server/log') const log = require('../server/log')
const settingsController = require('./api/controller/settings') const settingsController = require('./api/controller/settings')
const db = require('./api/models/index') const db = require('./api/models/index')
const dayjs = require('dayjs')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(timezone)
async function start (nuxt) { async function start (nuxt) {
if (config.status == 'READY') { if (config.status == 'READY') {
@@ -27,6 +31,8 @@ module.exports = function () {
await settingsController.load() await settingsController.load()
} }
dayjs.tz.setDefault(settingsController.settings.instance_timezone)
let TaskManager let TaskManager
if (config.status === 'READY' && process.env.NODE_ENV == 'production') { if (config.status === 'READY' && process.env.NODE_ENV == 'production') {
TaskManager = require('../server/taskManager').TaskManager TaskManager = require('../server/taskManager').TaskManager