Files
KIBU/src/lib/time.ts
T
Paul Schoeneck 75af38a4b6 Add licensing information and update README for KIBU
- Introduced additional terms under Section 7 of the GNU AGPL v3 for KIBU, including attribution, marking of modified versions, and trademark usage.
- Added MIT License file for clarity on licensing options.
- Updated README.md to reflect the new licensing terms, including detailed usage rights and obligations.
- Created REUSE.toml for SPDX compliance and license annotations.
- Added THIRD_PARTY_NOTICES.md to clarify third-party dependencies and their licenses.
- Updated various source files and scripts to include SPDX headers for copyright and licensing.
- Enhanced footer in layout to display licensing information and project link.
- Implemented a new license page to present the AGPL and additional terms clearly.
2026-11-03 08:27:12 +01:00

54 lines
2.4 KiB
TypeScript

// SPDX-FileCopyrightText: 2026 Paul Schoeneck
//
// SPDX-License-Identifier: AGPL-3.0-only AND LicenseRef-KIBU-Additional-Terms
export function localClock(now = new Date()) {
const parts = new Intl.DateTimeFormat("en-GB", {
timeZone: process.env.APP_TIMEZONE ?? "Europe/Berlin",
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", hourCycle: "h23",
}).formatToParts(now);
const get = (type: Intl.DateTimeFormatPartTypes) => parts.find((p) => p.type === type)!.value;
const day = `${get("year")}-${get("month")}-${get("day")}`;
const weekday = new Date(`${day}T12:00:00Z`).getUTCDay() || 7;
return { day, time: `${get("hour")}:${get("minute")}`, weekday };
}
export function pickupState(pickup: string | null, time: string, end?: string | null) {
if (!pickup) return "none";
const minutes = (value: string) => Number(value.slice(0, 2)) * 60 + Number(value.slice(3, 5));
const delta = minutes(pickup) - minutes(time);
if (minutes(end ?? pickup) < minutes(time)) return "overdue";
if (delta <= 15) return "due";
return "later";
}
export function displayDay(day: string) {
return new Intl.DateTimeFormat("de-DE", {
weekday: "long", day: "numeric", month: "long", timeZone: "UTC",
}).format(new Date(`${day}T12:00:00Z`));
}
export function timeInZone(instant: string, timeZone: string) {
return new Intl.DateTimeFormat("en-GB", { timeZone, hour: "2-digit", minute: "2-digit", hourCycle: "h23" })
.format(new Date(instant));
}
export function localInstant(day: string, time: string, timeZone: string): Date {
const wall = Date.parse(`${day}T${time}:00Z`);
const format = (date: Date) => new Intl.DateTimeFormat("sv-SE", {
timeZone, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit",
minute: "2-digit", second: "2-digit", hourCycle: "h23",
}).format(date).replace(" ", "T");
const offsets = new Set([-36, 0, 36].map((hours) => {
const sample = new Date(wall + hours * 3_600_000);
return Date.parse(`${format(sample)}Z`) - sample.getTime();
}));
const candidates = [...offsets].map((offset) => new Date(wall - offset))
.filter((date) => format(date) === `${day}T${time}:00`);
if (candidates.length !== 1) {
throw new Error("Diese Uhrzeit ist wegen der Zeitumstellung nicht eindeutig oder existiert nicht. Bitte eine andere Uhrzeit wählen.");
}
return candidates[0];
}