feat(calendar): Standard-Zeitzone pro Kalender vorbereiten

Neue Spalte cal_calendars.timezone (Default UTC). CalDAV selbst braucht
sie nicht - jeder Termin trägt seine Zeitzone bereits im eigenen .ics,
und die TZID-Auflösung darin läuft schon korrekt über die
System-Zeitzonendatenbank. Gebraucht wird das Feld erst von der
künftigen Web-Oberfläche zum Anlegen neuer Termine (Phase 3) als
Vorgabe, wenn der Nutzer keine eigene Zeitzone wählt - jetzt schon
angelegt, damit dafür keine weitere Migration nötig wird.

Gegen den laufenden Container geprüft: Migration lief durch, Home-Set-
PROPFIND (nutzt die geänderten SELECTs) weiterhin grün.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Sonnet 5 committed 2026-09-03 22:56:28 +02:00
1 parent 469274171c
commit 3f1b2b8a14
6 files changed
+28 -8

No files matched your search

@@ -0,0 +1 @@
ALTER TABLE cal_calendars DROP COLUMN IF EXISTS timezone;
@@ -0,0 +1,7 @@
-- Standard-Zeitzone pro Kalender. Wird von CalDAV selbst nicht gebraucht
-- (jeder Termin trägt seine Zeitzone im .ics), sondern erst von der
-- künftigen Web-Oberfläche zum Anlegen neuer Termine (Phase 3, siehe
-- kalender-plan.md) – ein neuer Termin braucht eine Vorgabe, wenn der
-- Nutzer keine eigene wählt. Jetzt schon angelegt, damit später keine
-- weitere Migration für ein einzelnes Feld nötig ist.
ALTER TABLE cal_calendars ADD COLUMN IF NOT EXISTS timezone TEXT NOT NULL DEFAULT 'UTC';
+6 -2
View File
@@ -20,8 +20,12 @@ type Calendar struct {
Color string `json:"color"`
Description string `json:"description"`
SupportsTodo bool `json:"supports_todo"`
CTag int64 `json:"ctag"`
CreatedAt time.Time `json:"created_at"`
// TimeZone ist die Vorgabe für neue Termine, die die Web-Oberfläche
// künftig hier anlegt (Phase 3). CalDAV selbst braucht sie nicht –
// jeder Termin trägt seine Zeitzone bereits im .ics.
TimeZone string `json:"timezone"`
CTag int64 `json:"ctag"`
CreatedAt time.Time `json:"created_at"`
}
// Object ist ein einzelner Termin oder eine Aufgabe. Data ist die
+6 -6
View File
@@ -23,7 +23,7 @@ func NewStore(db *pgxpool.Pool) *Store {
func (s *Store) ListCalendars(ctx context.Context, ownerID uuid.UUID) ([]Calendar, error) {
rows, err := s.db.Query(ctx,
`SELECT id, owner_id, name, color, description, supports_todo, ctag, created_at
`SELECT id, owner_id, name, color, description, supports_todo, timezone, ctag, created_at
FROM cal_calendars WHERE owner_id = $1 ORDER BY created_at`,
ownerID,
)
@@ -35,7 +35,7 @@ func (s *Store) ListCalendars(ctx context.Context, ownerID uuid.UUID) ([]Calenda
var out []Calendar
for rows.Next() {
var c Calendar
if err := rows.Scan(&c.ID, &c.OwnerID, &c.Name, &c.Color, &c.Description, &c.SupportsTodo, &c.CTag, &c.CreatedAt); err != nil {
if err := rows.Scan(&c.ID, &c.OwnerID, &c.Name, &c.Color, &c.Description, &c.SupportsTodo, &c.TimeZone, &c.CTag, &c.CreatedAt); err != nil {
return nil, fmt.Errorf("scan calendar: %w", err)
}
out = append(out, c)
@@ -46,10 +46,10 @@ func (s *Store) ListCalendars(ctx context.Context, ownerID uuid.UUID) ([]Calenda
func (s *Store) GetCalendar(ctx context.Context, ownerID, id uuid.UUID) (*Calendar, error) {
var c Calendar
err := s.db.QueryRow(ctx,
`SELECT id, owner_id, name, color, description, supports_todo, ctag, created_at
`SELECT id, owner_id, name, color, description, supports_todo, timezone, ctag, created_at
FROM cal_calendars WHERE owner_id = $1 AND id = $2`,
ownerID, id,
).Scan(&c.ID, &c.OwnerID, &c.Name, &c.Color, &c.Description, &c.SupportsTodo, &c.CTag, &c.CreatedAt)
).Scan(&c.ID, &c.OwnerID, &c.Name, &c.Color, &c.Description, &c.SupportsTodo, &c.TimeZone, &c.CTag, &c.CreatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
@@ -82,9 +82,9 @@ func (s *Store) CreateCalendar(ctx context.Context, ownerID uuid.UUID, name, col
err := s.db.QueryRow(ctx,
`INSERT INTO cal_calendars (owner_id, name, color, description)
VALUES ($1, $2, $3, $4)
RETURNING id, owner_id, name, color, description, supports_todo, ctag, created_at`,
RETURNING id, owner_id, name, color, description, supports_todo, timezone, ctag, created_at`,
ownerID, name, color, description,
).Scan(&c.ID, &c.OwnerID, &c.Name, &c.Color, &c.Description, &c.SupportsTodo, &c.CTag, &c.CreatedAt)
).Scan(&c.ID, &c.OwnerID, &c.Name, &c.Color, &c.Description, &c.SupportsTodo, &c.TimeZone, &c.CTag, &c.CreatedAt)
if err != nil {
return nil, fmt.Errorf("insert calendar: %w", err)
}
@@ -0,0 +1 @@
ALTER TABLE cal_calendars DROP COLUMN IF EXISTS timezone;
@@ -0,0 +1,7 @@
-- Standard-Zeitzone pro Kalender. Wird von CalDAV selbst nicht gebraucht
-- (jeder Termin trägt seine Zeitzone im .ics), sondern erst von der
-- künftigen Web-Oberfläche zum Anlegen neuer Termine (Phase 3, siehe
-- kalender-plan.md) – ein neuer Termin braucht eine Vorgabe, wenn der
-- Nutzer keine eigene wählt. Jetzt schon angelegt, damit später keine
-- weitere Migration für ein einzelnes Feld nötig ist.
ALTER TABLE cal_calendars ADD COLUMN IF NOT EXISTS timezone TEXT NOT NULL DEFAULT 'UTC';