diff --git a/internal/models/models.go b/internal/models/models.go new file mode 100644 index 0000000..af31c80 --- /dev/null +++ b/internal/models/models.go @@ -0,0 +1,81 @@ +// This file is Free Software under the MIT License +// without warranty, see README.md and LICENSES/MIT.txt for details. +// +// SPDX-License-Identifier: MIT +// +// SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) +// Software-Engineering: 2023 Intevation GmbH + +// Package models contains helper models used in the tools internally. +package models + +import ( + "fmt" + "strings" + "time" +) + +// TimeRange is a time interval. +type TimeRange [2]time.Time + +// NewTimeInterval creates a new time range. +// The time values will be sorted. +func NewTimeInterval(a, b time.Time) TimeRange { + if b.Before(a) { + a, b = b, a + } + return TimeRange{a, b} +} + +// guessDate tries to guess an RFC 3339 date time from a given string. +func guessDate(s string) (time.Time, bool) { + for _, layout := range []string{ + "2006-01-02T15:04:05Z07:00", + "2006-01-02T15:04:05", + "2006-01-02T15:04", + "2006-01-02T15", + "2006-01-02", + "2006-01", + "2006", + } { + if t, err := time.Parse(s, layout); err == nil { + return t, true + } + } + return time.Time{}, false +} + +// UnmarshalText implements [encoding/text.TextUnmarshaler]. +func (tr *TimeRange) UnmarshalText(text []byte) error { + + s := strings.TrimSpace(string(text)) + // Handle relative case first. + if duration, err := time.ParseDuration(s); err == nil { + now := time.Now() + *tr = NewTimeInterval(now.Add(-duration), now) + return nil + } + + a, b, found := strings.Cut(s, ",") + a, b = strings.TrimSpace(a), strings.TrimSpace(b) + + // Only start date? + if !found { + start, ok := guessDate(a) + if !ok { + return fmt.Errorf("%q is not a valid RFC date time", a) + } + *tr = NewTimeInterval(start, time.Now()) + } + // Real interval + start, ok := guessDate(a) + if !ok { + return fmt.Errorf("%q is not a valid RFC date time", a) + } + end, ok := guessDate(b) + if !ok { + return fmt.Errorf("%q is not a valid RFC date time", b) + } + *tr = NewTimeInterval(start, end) + return nil +}