(timestampMs: number, visibleRangeMs: number)
| 1052 | ]; |
| 1053 | |
| 1054 | const formatTimeTickValue = (timestampMs: number, visibleRangeMs: number): string | null => { |
| 1055 | if (!Number.isFinite(timestampMs)) return null; |
| 1056 | if (!Number.isFinite(visibleRangeMs) || visibleRangeMs < 0) visibleRangeMs = 0; |
| 1057 | |
| 1058 | const d = new Date(timestampMs); |
| 1059 | // Guard against out-of-range timestamps that produce an invalid Date. |
| 1060 | if (!Number.isFinite(d.getTime())) return null; |
| 1061 | const yyyy = d.getFullYear(); |
| 1062 | const mm = d.getMonth() + 1; // 1-12 |
| 1063 | const dd = d.getDate(); |
| 1064 | const hh = d.getHours(); |
| 1065 | const min = d.getMinutes(); |
| 1066 | |
| 1067 | // Requirements (range in ms): |
| 1068 | // - < 1 day: HH:mm |
| 1069 | // - 1-7 days: MM/DD HH:mm |
| 1070 | // - 1-12 weeks (and up to ~3 months): MM/DD |
| 1071 | // - 3-12 months: MMM DD |
| 1072 | // - > 1 year: YYYY/MM |
| 1073 | if (visibleRangeMs < MS_PER_DAY) { |
| 1074 | return `${pad2(hh)}:${pad2(min)}`; |
| 1075 | } |
| 1076 | // Treat the 7-day boundary as inclusive for the “1–7 days” tier. |
| 1077 | if (visibleRangeMs <= 7 * MS_PER_DAY) { |
| 1078 | return `${pad2(mm)}/${pad2(dd)} ${pad2(hh)}:${pad2(min)}`; |
| 1079 | } |
| 1080 | // Keep short calendar dates until the visible range reaches ~3 months. |
| 1081 | // (This covers the 1–12 week requirement, plus the small 12w→3m gap.) |
| 1082 | if (visibleRangeMs < 3 * MS_PER_MONTH_APPROX) { |
| 1083 | return `${pad2(mm)}/${pad2(dd)}`; |
| 1084 | } |
| 1085 | if (visibleRangeMs <= MS_PER_YEAR_APPROX) { |
| 1086 | const mmm = MONTH_SHORT_EN[d.getMonth()] ?? pad2(mm); |
| 1087 | return `${mmm} ${pad2(dd)}`; |
| 1088 | } |
| 1089 | return `${yyyy}/${pad2(mm)}`; |
| 1090 | }; |
| 1091 | |
| 1092 | const generateLinearTicks = (domainMin: number, domainMax: number, tickCount: number): number[] => { |
| 1093 | const count = Math.max(1, Math.floor(tickCount)); |
no test coverage detected