chartjs / chartjs/Chart.js

`DateAdapter#format` type declaration error

Open
#11,199 0 comments 0 reactions 0 assignees View on GitHub
type: bug
Dominant language
JavaScript
Stars
67.7k
Forks
11.9k
Avg merge
7h 39m
Merged PRs (30d)
5

Description

### Expected behavior

The type declaration `DateAdapter#format` contains an error

https://github.com/chartjs/Chart.js/blob/c0bf05f87df431202c991cfb5f3ee34106d9b6f4/src/core/core.adapters.ts#L34

Here, `format` (3rd param) is of type whatever `DateAdapter#formats` return, not `TimeUnit`

---

Additional problems:

- `options` should not be of type `T` but whatever the adapter wants it to be
- `init` should be optional
- Maybe `diff` should not exist `diff(a, b, unit) <=> add(a, -b, unit)`
- `endOf` might writable as a combination of `add` and `startOf`

### Current behavior

N/A

### Reproducible sample

N/A

### Optional extra steps/info to reproduce

_No response_

### Possible solution

_No response_

### Context

I'm currently implementing a native date adapter (with `Intl.DateTimeFormat` and the likes)

WIP

```ts
import type { DateAdapter } from 'chart.js';

/**
* Adapter to work with Time Cartesian Axis, without dependencies.
*
* @see https://www.chartjs.org/docs/next/axes/cartesian/time.html
*/
export default {
options: new Date(),
init() {},
add(timestamp, amount, unit) {
switch (unit) {
case 'millisecond': {
return timestamp + amount;
}
case 'second': {
return timestamp + amount * 1000;
}
case 'minute': {
return timestamp + amount * 1000 * 60;
}
case 'hour': {
return timestamp + amount * 1000 * 60 * 60;
}
case 'day': {
return timestamp + amount * 1000 * 60 * 60 * 24;
}
case 'week': {
return timestamp + amount * 1000 * 60 * 60 * 24 * 7;
}
// Starting with month, units become irregular
case 'month': {
const source = new Date(timestamp);
return new Date(
source.getFullYear(),
source.getMonth() + amount,
source.getDate(),
source.getHours(),
source.getMinutes(),
source.getSeconds(),
source.getMilliseconds()
).getTime();
}
case 'quarter': {
const source = new Date(timestamp);
return new Date(
source.getFullYear(),
source.getMonth() + amount * 3,
source.getDate(),
source.getHours(),
source.getMinutes(),
source.getSeconds(),
source.getMilliseconds()
).getTime();
}
case 'year': {
const source = new Date(timestamp);
return new Date(
source.getFullYear() + amount,
source.getMonth(),
source.getDate(),
source.getHours(),
source.getMinutes(),
source.getSeconds(),
source.getMilliseconds()
).getTime();
}
}
},
diff(a, b, unit) {
return this.add(a, -b, unit);
},
startOf(timestamp, unit, startOfWeek) {
switch (unit) {
case 'millisecond': {
return timestamp;
}
case 'second': {
return timestamp - (timestamp % 1000);
}
case 'minute': {
const source = new Date(timestamp);
return new Date(
source.getFullYear(),
source.getMonth(),
source.getDate(),
source.getHours(),
source.getMinutes()
).getTime();
}
case 'hour': {
const source = new Date(timestamp);
return new Date(
source.getFullYear(),
source.getMonth(),
source.getDate(),
source.getHours()
).getTime();
}
case 'day': {
const source = new Date(timestamp);
return new Date(source.getFullYear(), source.getMonth(), source.getDate()).getTime();
}
case 'week': {
const source = new Date(timestamp);
return new Date(
source.getFullYear(),
source.getMonth(),
source.getDate() - source.getDay()
).getTime();
}
case 'quarter': {
const source = new Date(timestamp);
return new Date(source.getFullYear(), Math.floor(source.getMonth() / 3) * 3, 1).getTime();
}
case 'month': {
const source = new Date(timestamp);
return new Date(source.getFullYear(), source.getMonth(), 1).getTime();
}
case 'year': {
const source = new Date(timestamp);
return new Date(source.getFullYear(), 0, 1).getTime();
}
case 'isoWeek': {
const source = new Date(timestamp);
return new Date(
source.getFullYear(),
source.getMonth(),
source.getDate() - source.getDay() + (startOfWeek ?? 1)
).getTime();
}
}
},
endOf(timestamp, unit) {
if (unit === 'isoWeek') return this.add(this.startOf(timestamp, unit, 1), 1, 'week') - 1;
return this.add(this.startOf(timestamp, unit), 1, unit) - 1;
},
parse: (value) => new Date(value as string).getTime(),
// Type declarations are broken, see https://github.com/chartjs/Chart.js/issues/11199
formats: () => formatters as unknown as Record,
format: (timestamp, formatter) => (formatter as unknown as Intl.DateTimeFormat).format(timestamp),
} satisfies DateAdapter;

export const formatters = {
millisecond: new Intl.DateTimeFormat('en-US', {
second: 'numeric',
fractionalSecondDigits: 3,
}),
second: new Intl.DateTimeFormat('en-US', { minute: 'numeric', second: '2-digit' }),
minute: new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit' }),
hour: new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit' }),
day: new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' }),
week: new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' }),
month: new Intl.DateTimeFormat('en-US', { month: 'short', year: 'numeric' }),
quarter: new Intl.DateTimeFormat('en-US', { month: 'short', year: 'numeric' }),
year: new Intl.DateTimeFormat('en-US', { year: 'numeric' }),
datetime: new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeStyle: 'short' }),
} satisfies Record;

```

### chart.js version

master

### Browser name and version

_No response_

### Link to your project

_No response_

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.