Weather Station / ECOWITT / DNT
1/**
2 * Represents the time resolution for data aggregation.
3 */
4export type Resolution = "minute" | "hour" | "day";
5
6/**
7 * Parses a timestamp string into a Date object.
8 * Handles formats like "YYYY/M/D H:mm" or "YYYY-M-DTH:mm:ss".
9 * @param {string} ts - The timestamp string to parse.
10 * @returns {Date | null} A Date object, or null if parsing fails.
11 */
12export function parseTimestamp(ts: string): Date | null {
13 // Expected like: 2025/8/1 0:03 (no zero padding guaranteed)
14 if (!ts) return null;
15 const [datePart, timePart] = ts.trim().split(/[\sT]+/);
16 if (!datePart) return null;
17 const dateSep = datePart.includes("-") ? "-" : "/";
18 const [y, m, d] = datePart.split(dateSep).map((s) => Number(s));
19 let hh = 0, mm = 0, ss = 0;
20 if (timePart) {
21 const [h, m2, s2] = timePart.split(":");
22 hh = Number(h ?? 0);
23 mm = Number(m2 ?? 0);
24 ss = Number(s2 ?? 0);
25 }
26 if (!Number.isFinite(y) || !Number.isFinite(m) || !Number.isFinite(d)) return null;
27 return new Date(y, (m || 1) - 1, d || 1, hh, mm, ss, 0);
28}
29
30/**
31 * Floors a date to the specified resolution.
32 * @param {Date} dt - The date to floor.
33 * @param {Resolution} res - The resolution ("minute", "hour", or "day").
34 * @returns {Date} The floored date.
35 */
36export function floorToResolution(dt: Date, res: Resolution): Date {
37 const d = new Date(dt.getTime());
38 if (res === "day") {
39 d.setHours(0, 0, 0, 0);
40 } else if (res === "hour") {
41 d.setMinutes(0, 0, 0);
42 } else {
43 d.setSeconds(0, 0);
44 }
45 return d;
46}
47
48/**
49 * Creates a string key for a date based on the specified resolution.
50 * @param {Date} dt - The date to create a key for.
51 * @param {Resolution} res - The resolution.
52 * @returns {string} The formatted key string.
53 */
54export function keyForResolution(dt: Date, res: Resolution): string {
55 const y = dt.getFullYear();
56 const m = dt.getMonth() + 1;
57 const d = dt.getDate();
58 const hh = dt.getHours();
59 const mm = dt.getMinutes();
60 if (res === "day") return `${y}-${pad(m)}-${pad(d)}`;
61 if (res === "hour") return `${y}-${pad(m)}-${pad(d)} ${pad(hh)}:00`;
62 return `${y}-${pad(m)}-${pad(d)} ${pad(hh)}:${pad(mm)}`;
63}
64
65/**
66 * Pads a number with a leading zero if it is less than 10.
67 * @param {number} n - The number to pad.
68 * @returns {string} The padded string.
69 * @private
70 */
71function pad(n: number): string { return n < 10 ? `0${n}` : String(n); }
72
73/**
74 * Converts a Date object to an ISO string, adjusted for the local timezone.
75 * @param {Date} dt - The date to convert.
76 * @returns {string} The ISO string.
77 */
78export function iso(dt: Date): string {
79 return new Date(dt.getTime() - dt.getTimezoneOffset() * 60000).toISOString();
80}