Platform backbone (plan 2026-10-13-plattform-rueckgrat), reviewed tasks: - run roles api/worker/job/all with shared startup checks (Task 1) - schema and migration for DomainEvent, deliveries, JobRun, ThrottlerHit (Task 2) - audit, notifications and domain events written in the same transaction (Task 3) - dispatcher with SKIP LOCKED claims, retries and stale release (Task 4) - worker building blocks: tick, LISTEN/NOTIFY wake-up, health (Task 5) - worker process and entry points (Task 6) - throttler counters in Postgres (Task 19) - separate keys per purpose with key ring (Task 20) - public origin page without exact location (Task 22) - shop keys split into read and write (Task 23) - Kustomize base, components, overlay encry, CI validation (Task 26) Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), reviewed tasks: - brood frames, read endpoints, apiary overview and offline pack - network basics, offline queue v3, navigation sheets, form building blocks - new inspection with last-time hints and weather, varroa per day, treatment prefill, apiary page with round - inspection photo (Task 10) is implemented, review pending Not rolled out. Remaining tasks follow in later commits. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
45 lines
1.6 KiB
TypeScript
45 lines
1.6 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);
|
|
}
|