First working prototype
This commit is contained in:
80
src/lib/time.js
Normal file
80
src/lib/time.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Time helpers. Everything here works in *local* time — the prototype derived day
|
||||
* keys from `toISOString()`, which is UTC and files late/early work under the wrong
|
||||
* day. Timestamps are epoch milliseconds; rounding is applied at render only.
|
||||
*/
|
||||
|
||||
export const MINUTE = 60_000;
|
||||
export const HOUR = 3_600_000;
|
||||
export const DAY = 86_400_000;
|
||||
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
|
||||
/** Local 'YYYY-MM-DD' for a timestamp. */
|
||||
export function localDateKey(ts = Date.now()) {
|
||||
const d = new Date(ts);
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
||||
}
|
||||
|
||||
/** Timestamp of local midnight starting `dateKey`. */
|
||||
export function startOfDay(dateKey) {
|
||||
const [y, m, d] = dateKey.split('-').map(Number);
|
||||
return new Date(y, m - 1, d).getTime();
|
||||
}
|
||||
|
||||
/** Timestamp of 'HH:MM' local time on `dateKey`. */
|
||||
export function atLocalTime(dateKey, hhmm) {
|
||||
const [y, m, d] = dateKey.split('-').map(Number);
|
||||
const [h, min] = hhmm.split(':').map(Number);
|
||||
return new Date(y, m - 1, d, h, min, 0, 0).getTime();
|
||||
}
|
||||
|
||||
/** `dateKey` shifted by `n` days, DST-safe. */
|
||||
export function addDays(dateKey, n) {
|
||||
const [y, m, d] = dateKey.split('-').map(Number);
|
||||
return localDateKey(new Date(y, m - 1, d + n).getTime());
|
||||
}
|
||||
|
||||
/** JS weekday for a date key: 0 = Sunday … 6 = Saturday. */
|
||||
export function weekday(dateKey) {
|
||||
return new Date(startOfDay(dateKey)).getDay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Round to the nearest `minutes`, half up, relative to *local midnight* rather
|
||||
* than the epoch — so zones with a :30 or :45 offset still land on whole
|
||||
* wall-clock steps. Always drops seconds.
|
||||
*/
|
||||
export function roundTs(ts, minutes) {
|
||||
const step = Math.max(1, minutes || 1) * MINUTE;
|
||||
const midnight = startOfDay(localDateKey(ts));
|
||||
return midnight + Math.round((ts - midnight) / step) * step;
|
||||
}
|
||||
|
||||
/** 'HH:MM' in local time, rounded to `roundMinutes`. */
|
||||
export function fmtTime(ts, roundMinutes = 1) {
|
||||
const d = new Date(roundTs(ts, roundMinutes));
|
||||
return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
}
|
||||
|
||||
/** A duration as '8h15' / '0h45'. */
|
||||
export function fmtDuration(ms) {
|
||||
const total = Math.max(0, Math.round(ms / MINUTE));
|
||||
return `${Math.floor(total / 60)}h${pad(total % 60)}`;
|
||||
}
|
||||
|
||||
/** 'vendredi 5 septembre' — the resume heading. */
|
||||
export function fmtDayLabel(dateKey) {
|
||||
const s = new Intl.DateTimeFormat('fr-FR', {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
}).format(new Date(startOfDay(dateKey)));
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
}
|
||||
|
||||
/** 'HH:MM' → minutes since midnight. */
|
||||
export function hhmmToMinutes(hhmm) {
|
||||
const [h, m] = hhmm.split(':').map(Number);
|
||||
return h * 60 + m;
|
||||
}
|
||||
Reference in New Issue
Block a user