Files
ApiCore/ApiCore-Frontend/src/lib/dates.ts
T
Nick SzaunigandClaude Opus 5.5 02eb16924e feat: T4 wave 4 - rules suggest, takeover, chains, autopilot A1-A3, suggestions page, varroa bands UI
- Automation rules propose suggestions instead of creating tasks;
  notify per rule config; corrected varroa measurements refresh or
  resolve their suggestion
- Inspection values can be taken over into the colony card
  (occupiedFrames 0-40, strength deprecated); warning counts use the
  same visibility rule as the notification list
- Biotech chains: swarm check after queen cells, queen acceptance
  check, split schedules (raise own, queen cell, queen)
- Season autopilot core with A1 inspection due, A2 varroa measurement,
  A3 recheck after treatment; colony sync and daily step
- Frontend: suggestions page with groups, bulk accept, undo, paging,
  focus management and permission gating
- Frontend: varroa traffic light from seasonal bands, sample size,
  one range request for the colony chart
- Notifications: mark-read answers 404 for rows the user may no
  longer see

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-14 15:22:36 +02:00

57 lines
1.9 KiB
TypeScript

/**
* The calendar day the beekeeper sees on their phone, as "YYYY-MM-DD".
*
* Not `toISOString().slice(0, 10)`: that is the UTC day, which in Germany is
* still yesterday until 01:00 (winter) or 02:00 (summer) - a form opened
* shortly after midnight would prefill the wrong date.
*/
export function toLocalIsoDate(date: Date): string {
const year = String(date.getFullYear()).padStart(4, "0");
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
/** Today's local calendar day, e.g. the default date of a new record. */
export function todayLocalIsoDate(): string {
return toLocalIsoDate(new Date());
}
const CALENDAR_DAY = /^\d{4}-\d{2}-\d{2}$/;
/**
* A day picked in a date field ("YYYY-MM-DD") as the API stores calendar
* days: midnight UTC of exactly that day, whatever the phone's time zone.
* Throws on anything that is not a real calendar day rather than sending a
* shifted or invalid date.
*/
export function toApiDate(calendarDay: string): string {
if (!CALENDAR_DAY.test(calendarDay)) {
throw new RangeError(`Kein Kalendertag: "${calendarDay}"`);
}
const iso = new Date(`${calendarDay}T00:00:00.000Z`).toISOString();
if (iso.slice(0, 10) !== calendarDay) {
throw new RangeError(`Kein Kalendertag: "${calendarDay}"`);
}
return iso;
}
const CALENDAR_DAY_LENGTH = 10;
/** The "YYYY-MM-DD" part of an ISO timestamp as written, without any time zone shift. */
export function toCalendarDay(isoTimestamp: string): string {
return isoTimestamp.slice(0, CALENDAR_DAY_LENGTH);
}
const BERLIN_DAY = new Intl.DateTimeFormat("en-CA", {
timeZone: "Europe/Berlin",
year: "numeric",
month: "2-digit",
day: "2-digit",
});
/** The Europe/Berlin calendar day of an instant, as "YYYY-MM-DD" (the backend's day). */
export function berlinCalendarDay(isoTimestamp: string): string {
return BERLIN_DAY.format(new Date(isoTimestamp));
}