I have created this beautiful chart with Lightweight Charts.
The problem that I now face is that in the marker on the x-axis (grey tooltip) the time displayed seems to be off by 2 hours.
I get the feeling that this is a timezone issue, as I am CEST which is UTC+2, but I could not figure out how and where this happened and how to fix it.
This is the code of the Vue SFC which generates the chart part
<template>
<v-container
fluid
id="chart"
>
<v-btn-toggle
v-model="interval"
tile
group
@change="refresh"
>
<v-btn value="day">
Intraday
</v-btn>
<v-btn value="week">
Woche
</v-btn>
<v-btn value="month">
Monat
</v-btn>
<v-btn value="year">
Jahr
</v-btn>
<v-btn value="fiveyears">
5 Jahre
</v-btn>
</v-btn-toggle>
<Alert
:message="errorMessage"
type="error"
></Alert>
</v-container>
</template>
<script>
import { createChart, TickMarkType } from 'lightweight-charts';
import ChartService from '@/service/ChartService'
import Alert from '@/components/Alert'
import FormatterService from "../../service/FormatterService";
export default {
name: "ShareChart",
components: {
Alert
},
props: [
'quote',
'bus'
],
data() {
return {
errorMessage: '',
chart: null,
interval: 'day',
series: null,
format: null
}
},
methods: {
create() {
const chart = createChart(
document.getElementById('chart'),
{
height: 200,
localization: {
locale: 'de-DE',
dateFormat: 'dd.MM.yyyy',
priceFormatter: price => this.format.eur(price)
},
handleScale: false,
handleScroll: false
});
chart.applyOptions({
timeScale: {
timeVisible: true,
tickMarkFormatter: (time, tickMarkType) => {
time = new Date(time*1000)
let opt = {}
switch(tickMarkType) {
case TickMarkType.Year:
opt = {
year: 'numeric'
}
break
case TickMarkType.Month:
opt = {
month: 'short'
}
break
case TickMarkType.DayOfMonth:
opt = {
day: '2-digit'
}
break
case TickMarkType.Time:
opt = {
hour: '2-digit',
minute: '2-digit'
}
break
case TickMarkType.TimeWithSeconds:
opt = {
hour: '2-digit',
minute: '2-digit',
seconds: '2-digit'
}
}
return time.toLocaleString('de-DE', opt)
},
},
})
this.chart = chart
},
async getData() {
let res = null
try {
res = await ChartService.get(
this.interval,
{
isin: this.quote.isin,
date: new Date().toISOString(),
currency: this.quote.currency,
mic: this.quote.mic
}
)
} catch (e) {
this.errorMessage = e
return null
}
return res.quotes
},
draw(datasets) {
if(!this.chart) return
if(!datasets) return
if (this.series) this.chart.removeSeries(this.series);
this.series = this.chart.addAreaSeries()
let chartData = []
for(let ds of datasets) {
chartData.push({
time: new Date(ds.date).getTime()/1000,
value: ds.value
})
}
this.series.setData(chartData)
this.setAreaColors(datasets[0].value, datasets[datasets.length-1].value)
this.setTimeRange()
},
setAreaColors(open, close) {
let red = 0
let green = 200
if(open > close) {
red = 250
green = 0
}
this.series.applyOptions({
lineColor: 'rgb('+red+', '+green+', 0)',
topColor: 'rgba('+red+', '+green+', 0, 1)',
bottomColor: 'rgba('+red+', '+green+', 0, 0)'
});
},
setTimeRange() {
if(this.interval != 'day') {
return this.chart.timeScale().fitContent()
}
const fromTime = new Date()
fromTime.setHours(8, 0, 0)
const toTime = new Date()
toTime.setHours(20, 0, 0)
// TODO: Ignores timezones
this.chart.timeScale().setVisibleRange({
from: fromTime.getTime() / 1000,
to: toTime.getTime() / 1000,
});
},
async refresh() {
const quotes = await this.getData()
this.draw(quotes)
}
},
mounted() {
this.format = new FormatterService({
currency: this.quote.currency
})
this.create()
this.refresh()
},
watch: {
quote: function () {
this.refresh()
}
}
}
</script>
<style scoped>
</style>
The purpose of the setTimeRange() function was to set the visible time range to 8am to 8pm local time for the 'intraday' chart. If there was no data anywhere in that timeframe, it should display white space, which does not work either.
Can someone assist?
Thanks to @timocov to pointing me to a related issue on the projects github. Turns out this is a known bug.
This is how I worked around it:
chart.applyOptions({
timeScale: {
timeVisible: true,
tickMarkFormatter: (time, tickMarkType) => {
time = this.getTimezoneCorrectedTime(time)
[
...]
}
}
})
getTimezoneCorrectedTime(utcTime, returnAsUnixTimestamp = false) {
if(utcTime instanceof Date) {
utcTime = utcTime.getTime()/1000
}
const timezoneOffsetMinutes = new Date().getTimezoneOffset()
const correctedTime = utcTime+(timezoneOffsetMinutes*60)
if(returnAsUnixTimestamp) return correctedTime
return new Date(correctedTime*1000)
}