Initial MVP commit
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful

This commit is contained in:
2026-03-17 08:35:16 -05:00
parent 8c092c2247
commit 27817f9e43
29 changed files with 1890 additions and 1 deletions

View File

@@ -0,0 +1,30 @@
package timeparse
import (
"fmt"
"strings"
"time"
"gitea.maximumdirect.net/ejr/weatherapi/internal/platform/constants"
)
func ParseTimestamp(raw string) (time.Time, error) {
raw = strings.TrimSpace(raw)
if raw == "" {
return time.Time{}, fmt.Errorf("timestamp is required")
}
var lastErr error
for _, layout := range constants.SupportedTimestampLayouts {
ts, err := time.Parse(layout, raw)
if err == nil {
return ts, nil
}
lastErr = err
}
if lastErr == nil {
lastErr = fmt.Errorf("invalid timestamp")
}
return time.Time{}, fmt.Errorf("timestamp must be RFC3339 (example: 2026-03-17T12:30:00Z): %w", lastErr)
}

View File

@@ -0,0 +1,29 @@
package timeparse
import "testing"
func TestParseTimestamp(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{name: "rfc3339", input: "2026-03-17T12:30:00Z", wantErr: false},
{name: "rfc3339nano", input: "2026-03-17T12:30:00.123456789Z", wantErr: false},
{name: "missing timezone", input: "2026-03-17T12:30:00", wantErr: true},
{name: "invalid", input: "not-a-time", wantErr: true},
{name: "empty", input: "", wantErr: true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := ParseTimestamp(tc.input)
if tc.wantErr && err == nil {
t.Fatalf("expected error")
}
if !tc.wantErr && err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
}
}