Replies: 28 comments
-
I don't really understand what the issue is.
|
Beta Was this translation helpful? Give feedback.
-
Yes
Yes, but i do not know how i can use the formatted timestamp with the
?? This is my simple way to format the timestamp
|
Beta Was this translation helpful? Give feedback.
-
So you are constructing the localeNames based on locale? You could just create a custom adapter. |
Beta Was this translation helpful? Give feedback.
-
the performance of Intl.DateTimeFormat is not very good, I load the translations when the locale is changed in order to use them later for the DateFormat.
Yes, an alternative would be, but I don't (yet) understand the settings / methods of the date adapter.
I like it 👍, would be a simple implementation, but don't know the restrictions yet? But would be the preferred method for me. Here is an example of how I prepare the data:
|
Beta Was this translation helpful? Give feedback.
-
Is the slow performance of |
Beta Was this translation helpful? Give feedback.
-
Negativ cached formatter is also slow. |
Beta Was this translation helpful? Give feedback.
-
Bad news, I can use your example statically and it works. Unfortunately, if I create the options with Javascript, I cannot add a callBack function:
|
Beta Was this translation helpful? Give feedback.
-
That is not a limitation of javascript or Chart.js. Why can't you add a callback? |
Beta Was this translation helpful? Give feedback.
-
@kurkle Yes you are right, it was my mistake. It works now, but I don't yet know how to access the In the
Works only for options.scales.x.type = "time" not for options.scales.x.type = "linear" |
Beta Was this translation helpful? Give feedback.
-
Take a look at the default formatters (assigned as callback):
|
Beta Was this translation helpful? Give feedback.
-
Negativ
|
Beta Was this translation helpful? Give feedback.
-
arrow functions don't receive this. |
Beta Was this translation helpful? Give feedback.
-
Sorry,
Tooltip /**
* format the tooltip label
* @param {*} context
* @returns string
*/
const formatToolTipLabel = (context) => {
let label = context.dataset.label || ""
const data = context.raw
const unit = context.dataset.unit ? ` (${context.dataset.unit})` : ""
label += ": " + context.formattedValue + unit
return label
} Works with
|
Beta Was this translation helpful? Give feedback.
-
I have not found a way to access the const xAxisFormat = (tickValue, index, ticks) => {
const dateFormatPattern = "day" // no way to get this from the options !
if (Number.isInteger(ticks[index].value)) {
return formatdate(+ticks[index].value, dateFormatPattern)
}
return tickValue
} So the only possibility is to create your own adapter with which the date is displayed in the user language. Disadvantage:
Modified date-fn custom adapter import { _adapters } from "chart.js";
import {parse,parseISO,toDate,isValid,startOfSecond,startOfMinute,startOfHour,startOfDay,
startOfWeek,startOfMonth,startOfQuarter,startOfYear,addMilliseconds,addSeconds,
addMinutes,addHours,addDays,addWeeks,addMonths,addQuarters,addYears,differenceInMilliseconds,
differenceInSeconds,differenceInMinutes,differenceInHours,differenceInDays,differenceInWeeks,
differenceInMonths,differenceInQuarters,differenceInYears,endOfSecond,endOfMinute,endOfHour,
endOfDay,endOfWeek,endOfMonth,endOfQuarter,endOfYear,
} from "date-fns";
const FORMATS = {
default: "ddd, d mmmm yyyy HH:MM:ss.l",
shortDate: "m.d.yy",
mediumDate: "d.m.yyyy",
longDate: "d mmmm yyyy",
fullDate: "dddd, d mmmm yyyy",
shortTime: "H:MM",
mediumTime: "H:MM:ss",
longTime: "H:MM:ss.L",
isoDate: "yyyy-mm-dd",
isoTime: "HH:MM:ss",
isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'",
week: "W",
quater: "Q",
datetime: "ddd, d mmmm yyyy HH:MM:ss",
millisecond: "H:MM:ss l",
second: "h:mm:ss l",
minute: "h:mm l",
hour: "H",
day: "d mmm",
month: "mmm yyyy",
year: "yyyy"
};
* Get week number in the year.
* @param {Integer} [weekStart=0] First day of the week. 0-based. 0 for Sunday, 6 for Saturday.
* @return {Integer} 0-based number of week.
*/
Date.prototype.getWeek = function (weekStart = 1) {
var januaryFirst = new Date(this.getFullYear(), 0, 1)
weekStart = weekStart || 0
return Math.floor(((this - januaryFirst) / 86400000 + januaryFirst.getDay() - weekStart) / 7)
}
/*
* Date Format 1.2.3
* (c) 2007-2009 Steven Levithan <stevenlevithan.com>
* MIT license
*
* Includes enhancements by Scott Trenda <scott.trenda.net>
* and Kris Kowal <cixar.com/~kris.kowal/>
*
* Accepts a date, a mask, or a date and a mask.
* Returns a formatted version of the given date.
* The date defaults to the current date/time.
* The mask defaults to dateFormat.masks.default.
* see: https://stevenlevithan.com/assets/misc/date.format.js
* https://blog.stevenlevithan.com/archives/date-time-format
* modified for chart.js
*/
var formatdate = (function () {
const token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZWQw]|"[^"]*"|'[^']*'/g,
timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
timezoneClip = /[^-+\dA-Z]/g,
pad = function (val, len) {
val = String(val)
len = len || 2
while (val.length < len) val = "0" + val
return val
}
return function (date, mask, utc) {
const dF = formatdate
if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
mask = date
date = undefined
}
date = date ? new Date(date) : new Date()
if (isNaN(date)) throw SyntaxError("invalid date")
mask = String(dF.masks[mask] || mask || dF.masks["default"])
// Allow setting the utc argument via the mask
if (mask.slice(0, 4) == "UTC:") {
mask = mask.slice(4)
utc = true
}
const _ = utc ? "getUTC" : "get",
d = date[_ + "Date"](),
D = date[_ + "Day"](),
m = date[_ + "Month"](),
y = date[_ + "FullYear"](),
H = date[_ + "Hours"](),
M = date[_ + "Minutes"](),
s = date[_ + "Seconds"](),
L = date[_ + "Milliseconds"](),
o = utc ? 0 : date.getTimezoneOffset(),
flags = {
// day
d: d,
dd: pad(d),
ddd: window.localeNames.days_short[D],
dddd: window.localeNames.days_long[D],
// week
W: date.getWeek(1),
w: date.getWeek(0),
// month
m: m + 1,
mm: pad(m + 1),
mmm: window.localeNames.month_short[m],
mmmm: window.localeNames.month_long[m],
// year
yy: String(y).slice(2),
yyyy: y,
Q: "Q" + Math.floor(((date.getMonth() / 3) % 4) + 1),
// time hour
h: H % 12 || 12,
hh: pad(H % 12 || 12),
H: H,
HH: pad(H),
// time minute
M: M,
MM: pad(M),
// time seconds
s: s,
ss: pad(s),
// time Milliseconds
l: pad(L, 3),
L: pad(L > 99 ? Math.round(L / 10) : L),
// time am / pm
t: H < 12 ? "a" : "p",
tt: H < 12 ? "am" : "pm",
T: H < 12 ? "A" : "P",
TT: H < 12 ? "AM" : "PM",
// GMT/UTC timezone
Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + (Math.abs(o) % 60), 4)
}
return mask.replace(token, function ($0) {
return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1)
})
}
})()
formatdate.masks = FORMATS;
_adapters._date.override({
_id: "date-fns",
formats: function () {
return FORMATS;
},
parse: function (value, fmt) {
if (value === null || typeof value === "undefined") {
return null;
}
const type = typeof value;
if (type === "number" || value instanceof Date) {
value = toDate(value);
} else if (type === "string") {
if (typeof fmt === "string") {
value = parse(value, fmt, new Date(), this.options);
} else {
value = parseISO(value, this.options);
}
}
return isValid(value) ? value.getTime() : null;
},
format: function (time, fmt) {
return formatdate(new Date(time), fmt);
},
add: function (time, amount, unit) {
switch (unit) {
case "millisecond":return addMilliseconds(time, amount);
case "second":return addSeconds(time, amount);
case "minute":return addMinutes(time, amount);
case "hour":return addHours(time, amount);
case "day":return addDays(time, amount);
case "week":return addWeeks(time, amount);
case "month":return addMonths(time, amount);
case "quarter":return addQuarters(time, amount);
case "year":return addYears(time, amount);
default:return time;
}
},
diff: function (max, min, unit) {
switch (unit) {
case "millisecond":return differenceInMilliseconds(max, min);
case "second":return differenceInSeconds(max, min);
case "minute":return differenceInMinutes(max, min);
case "hour":return differenceInHours(max, min);
case "day":return differenceInDays(max, min);
case "week":return differenceInWeeks(max, min);
case "month":return differenceInMonths(max, min);
case "quarter":return differenceInQuarters(max, min);
case "year":return differenceInYears(max, min);
default:return 0;
}
},
startOf: function (time, unit, weekday) {
switch (unit) {
case "second":return startOfSecond(time);
case "minute":return startOfMinute(time);
case "hour":return startOfHour(time);
case "day":return startOfDay(time);
case "week":return startOfWeek(time);
case "isoWeek":return startOfWeek(time, { weekStartsOn: +weekday });
case "month":return startOfMonth(time);
case "quarter":return startOfQuarter(time);
case "year":return startOfYear(time);
default:return time;
}
},
endOf: function (time, unit) {
switch (unit) {
case "second":return endOfSecond(time);
case "minute":return endOfMinute(time);
case "hour":return endOfHour(time);
case "day":return endOfDay(time);
case "week":return endOfWeek(time);
case "month":return endOfMonth(time);
case "quarter":return endOfQuarter(time);
case "year":return endOfYear(time);
default:return time;
}
},
}); I load the translations for the day and month names once when I start the application. /**
* set the local text for the day- and monthnames
*/
function getLocaleText(locale = "DE") {
return {
days_short: [...Array(7).keys()].map((day) =>
new Intl.DateTimeFormat(locale, { weekday: "short" }).format(new Date(Date.UTC(2021, 5, day)))
),
days_long: [...Array(7).keys()].map((day) =>
new Intl.DateTimeFormat(locale, { weekday: "long" }).format(new Date(Date.UTC(2021, 5, day)))
),
month_short: [...Array(12).keys()].map((month) =>
new Intl.DateTimeFormat(locale, { month: "short" }).format(new Date(Date.UTC(2021, month, 1)))
),
month_long: [...Array(12).keys()].map((month) =>
new Intl.DateTimeFormat(locale, { month: "long" }).format(new Date(Date.UTC(2021, month, 1)))
)
}
}
window.localeNames = getLocaleText(window.Chart3.defaults.locale) It would be much more performant and easier if the labels could be used in the dataset. If [
{
"x": 1616968800000,
"x-label": "2021-03-29",
"y": 0.24
},
{
"x": 1617055200000,
"x-label": "2021-03-30",
"y": 0.22
},
{
"x": 1617141600000,
"x-label": "2021-03-31",
"y": 2.86
},
{
"x": 1617228000000,
"x-label": "2021-04-01",
"y": 0.14
},
{
"x": 1617314400000,
"x-label": "2021-04-02",
"y": 0.21
},
{
"x": 1617400800000,
"x-label": "2021-04-03",
"y": 0.09
},
{
"x": 1617487200000,
"x-label": "2021-04-04",
"y": 0.25
}
] |
Beta Was this translation helpful? Give feedback.
-
This is an arrow function, it is not bound and can not use const xAxisFormat = (tickValue, index, ticks) => {
const dateFormatPattern = "day" // no way to get this from the options !
if (Number.isInteger(ticks[index].value)) {
return formatdate(+ticks[index].value, dateFormatPattern)
}
return tickValue
} This is an traditional function and does not have the same limitations. It has the same name, so nothing else should need to be changed, unless something in your environment limits you from using normal/traditional functions? function xAxisFormat(tickValue, index, ticks) {
const dateFormatPattern = this.options.time.unit; // "day"
if (Number.isInteger(ticks[index].value)) {
return formatdate(+ticks[index].value, dateFormatPattern)
}
return tickValue
} |
Beta Was this translation helpful? Give feedback.
-
@kurkle options.scales.x.ticks = { callback: xAxisFormat} |
Beta Was this translation helpful? Give feedback.
-
Why don't you try to use the traditional function instead of the arrow function as I've suggested 2 times already? |
Beta Was this translation helpful? Give feedback.
-
@kurkle
|
Beta Was this translation helpful? Give feedback.
-
Are you processing your package somehow? |
Beta Was this translation helpful? Give feedback.
-
file: graphchart.js // -------------------
// just for testing...
// -------------------
if (window.configdata) {
_options.scales = _options.scales || {}
_options.scales.x = _options.scales.x || {}
_options.scales.x.type = "time"
_options.scales.x.time = {
unit: "day",
displayFormats: {}
}
_options.scales.x.ticks = {
callback: function (value, index, ticks) {
if(this){
console.log("THIS READY:",this)
return "OK " + value
}else{
console.log("THIS NOT PRESENT:",this)
return "FAILED " + value
}
}
}
} |
Beta Was this translation helpful? Give feedback.
-
ok, so its the |
Beta Was this translation helpful? Give feedback.
-
I thought you were going to use |
Beta Was this translation helpful? Give feedback.
-
But Thanks for your help and time 👍 |
Beta Was this translation helpful? Give feedback.
-
I think in your use case, it would be better (and faster) to override the Chart.js/src/scales/scale.time.js Lines 477 to 484 in 3943306 Chart.TimeScale.prototype.generateTickLabels = function(ticks) {
/* loop the ticks and set .label */
}; That could break when Chart.js v4 comes out, so should have a note about that in your code. |
Beta Was this translation helpful? Give feedback.
-
An interesting approach, but it is too specific and not generally applicable. If the callback bug is fixed with I just think I'm not alone with the problem, it affects everyone who wants to display dates in their locale.
Has been fixed, no longer occurs in the current version. |
Beta Was this translation helpful? Give feedback.
-
This is fixed by #8822, and will be available in 3.1 |
Beta Was this translation helpful? Give feedback.
-
Thanks, works now. |
Beta Was this translation helpful? Give feedback.
-
timeseries **
* format the x-axis date/time label
* @param {*} tickValue
* @param {*} index
* @param {*} ticks
* @returns formatted tick value
*/
function xAxisFormat(tickValue, index, ticks) {
console.log("xAxisFormat", this.options)
if (this && this.options.time && this.options.time.unit) {
const dateFormatPattern = this.options.time.unit
if (dateFormatPattern && Number.isInteger(ticks[index].value)) {
return formatdate(+ticks[index].value, dateFormatPattern)
}
}
return tickValue
} {
"type": "bar",
"data": {
"datasets": [
{
"label": "Verbrauch",
"unit": "kWh",
"minval": 0,
"maxval": 0,
"sumval": 0,
"avgval": 0,
"pointRadius": 0,
"current": -0.6300000000000001,
"last_changed": "2021-04-04T13:45:19.303440+00:00",
"mode": "history",
"backgroundColor": "#FF8066",
"data": [
{
"x": 1617055200000,
"localedate": "2021-03-30",
"y": -128.12
},
{
"x": 1617141600000,
"localedate": "2021-03-31",
"y": -160.55
},
{
"x": 1617228000000,
"localedate": "2021-04-01",
"y": -153.49
},
{
"x": 1617314400000,
"localedate": "2021-04-02",
"y": -142.32
},
{
"x": 1617400800000,
"localedate": "2021-04-03",
"y": -189.55
},
{
"x": 1617487200000,
"localedate": "2021-04-04",
"y": -75.08
}
]
},
{
"label": "Energieproduktion",
"unit": "kWh",
"minval": 0,
"maxval": 0,
"sumval": 0,
"avgval": 0,
"pointRadius": 0,
"current": 0,
"last_changed": "2021-04-04T13:45:17.876805+00:00",
"mode": "history",
"backgroundColor": "#fcec34",
"data": [
{
"x": 1617055200000,
"localedate": "2021-03-30",
"y": 99.891
},
{
"x": 1617141600000,
"localedate": "2021-03-31",
"y": 94.916
},
{
"x": 1617228000000,
"localedate": "2021-04-01",
"y": 106.155
},
{
"x": 1617314400000,
"localedate": "2021-04-02",
"y": 99.444
},
{
"x": 1617400800000,
"localedate": "2021-04-03",
"y": 73.143
},
{
"x": 1617487200000,
"localedate": "2021-04-04",
"y": 92.939
}
]
}
],
"labels": []
},
"options": {
"units": "",
"hoverOffset": 8,
"layout": {},
"interaction": {
"mode": "nearest",
"intersect": false
},
"chartArea": {
"backgroundColor": "transparent"
},
"elements": {},
"spanGaps": true,
"plugins": {
"title": {},
"tooltip": {
"callbacks": {}
},
"legend": {
"display": true,
"position": "top"
}
},
"animation": {},
"onResize": null,
"scales": {
"x": {
"axis": "x",
"type": "timeseries",
"ticks": {
"source": "auto",
"major": {
"enabled": false
},
"minRotation": 0,
"maxRotation": 50,
"mirror": false,
"textStrokeWidth": 0,
"textStrokeColor": "",
"padding": 3,
"display": true,
"autoSkip": true,
"autoSkipPadding": 3,
"labelOffset": 0,
"minor": {},
"align": "center",
"crossAlign": "near",
"color": "rgb(225, 225, 225)"
},
"alignToPixels": true,
"stacked": true,
"offset": true,
"title": {
"display": true,
"text": "Zeitraum",
"padding": {
"top": 4,
"bottom": 4
},
"color": "rgb(225, 225, 225)"
},
"grid": {
"offset": true,
"display": true,
"drawBorder": true,
"drawOnChartArea": true,
"drawTicks": true,
"tickLength": 8,
"borderDash": [
1,
1
],
"borderDashOffset": 0,
"borderColor": "rgb(2, 136, 209)",
"borderWidth": 0.88,
"color": "rgba(179, 229, 252, 0.8)"
},
"bounds": "data",
"adapters": {},
"display": true,
"reverse": false,
"beginAtZero": false,
"grace": 0,
"id": "x",
"position": "bottom"
},
"y": {
"axis": "y",
"alignToPixels": true,
"stacked": true,
"title": {
"display": true,
"text": "Energie kWh",
"padding": {
"top": 4,
"bottom": 4
},
"color": "rgb(225, 225, 225)"
},
"type": "linear",
"beginAtZero": true,
"ticks": {
"minRotation": 0,
"maxRotation": 50,
"mirror": false,
"textStrokeWidth": 0,
"textStrokeColor": "",
"padding": 3,
"display": true,
"autoSkip": true,
"autoSkipPadding": 3,
"labelOffset": 0,
"minor": {},
"major": {},
"align": "center",
"crossAlign": "near",
"color": "rgb(225, 225, 225)"
},
"display": true,
"offset": false,
"reverse": false,
"bounds": "ticks",
"grace": 0,
"grid": {
"display": true,
"drawBorder": true,
"drawOnChartArea": true,
"drawTicks": true,
"tickLength": 8,
"offset": false,
"borderDash": [
1,
1
],
"borderDashOffset": 0,
"borderColor": "rgb(2, 136, 209)",
"borderWidth": 0.88,
"color": "rgba(179, 229, 252, 0.8)"
},
"id": "y",
"position": "left"
}
}
}
}
|
Beta Was this translation helpful? Give feedback.
-
Feature Proposal
I am looking for a way to use Time Cartesian Axis without a date adapter, because I have not found a way in the Home Assistant framework how I can use locale files for the adapter. Also I have my concerns, just to display the date / time in the user language, to use the date libs (moment, luxon, date-fn) for it.
I have different data sources that don't always have data for every time interval. It is possible to create a timeseries for Category Cartesian Axis for all data, but then my performance is poor, since many data always have 0 values.
Most likely I could use the chartjs-adapter-date-fns, but I can display the date and time in the user language.
What is my option?
The dataset contains the following data:
X-Axis: Date
Y-Axis: Value
Feature Use Case
Time axis Ticks label (x)
formatted andTooltips date / time
in the user language.Possible Implementation
???
Beta Was this translation helpful? Give feedback.
All reactions