package models import ( "fmt" "strings" "time" _ "time/tzdata" // Europe/Warsaw must resolve on hosts without zoneinfo. "git.golem15.com/golem15/summercms/modules/lagoon" ) // Location is the time zone the programme runs in. Timestamps are stored in // UTC (TIMESTAMPTZ) and shown in this zone; a date or time typed without an // offset is read in it. var Location = mustLoadLocation("Europe/Warsaw") func mustLoadLocation(name string) *time.Location { loc, err := time.LoadLocation(name) if err != nil { panic(fmt.Sprintf("jz.bm: load location %s: %v", name, err)) } return loc } // dateTimeLayouts are the forms ParseDateTime accepts. RFC 3339 carries its // own offset; the others are wall-clock times in Location. var dateTimeLayouts = []string{ "2006-01-02T15:04", "2006-01-02 15:04", "2006-01-02T15:04:05", "2006-01-02 15:04:05", } // ParseDateTime reads an RFC 3339 timestamp, or a wall-clock time // ("2026-10-03 08:00") in Location. The seed file uses these forms. func ParseDateTime(s string) (time.Time, error) { s = strings.TrimSpace(s) if t, err := time.Parse(time.RFC3339, s); err == nil { return t, nil } for _, layout := range dateTimeLayouts { if t, err := time.ParseInLocation(layout, s, Location); err == nil { return t, nil } } return time.Time{}, fmt.Errorf("jz.bm: %q is not a date and time", s) } // ParseDate reads a YYYY-MM-DD calendar date. func ParseDate(s string) (lagoon.Date, error) { d, err := lagoon.ParseDate(strings.TrimSpace(s)) if err != nil { return lagoon.Date{}, fmt.Errorf("jz.bm: %q is not a YYYY-MM-DD date", s) } return d, nil } // FormatDateTime is the API form of a timestamp: RFC 3339 in Location // ("2026-10-03T08:00:00+02:00"). func FormatDateTime(t time.Time) string { return t.In(Location).Format(time.RFC3339) }