Live video on the AT Protocol
1package aqtime
2
3import (
4 "testing"
5
6 "github.com/stretchr/testify/require"
7)
8
9func TestTimeFormat(t *testing.T) {
10 aqt := FromMillis(1726251017090)
11 require.Equal(t, "2024-09-13T18:10:17.090Z", aqt.String())
12 require.Equal(t, "2024-09-13T18-10-17-090Z", aqt.FileSafeString())
13 yr, mon, day, hr, min, sec, ms := aqt.Parts()
14 require.Equal(t, "2024", yr)
15 require.Equal(t, "09", mon)
16 require.Equal(t, "13", day)
17 require.Equal(t, "18", hr)
18 require.Equal(t, "10", min)
19 require.Equal(t, "17", sec)
20 require.Equal(t, "090", ms)
21}
22
23func TestTimeParse(t *testing.T) {
24 for _, str := range []string{"2024-09-13T18:10:17.090Z", "2024-09-13T18-10-17-090Z"} {
25 aqt, err := FromString(str)
26 require.NoError(t, err)
27 yr, mon, day, hr, min, sec, ms := aqt.Parts()
28 require.Equal(t, "2024", yr)
29 require.Equal(t, "09", mon)
30 require.Equal(t, "13", day)
31 require.Equal(t, "18", hr)
32 require.Equal(t, "10", min)
33 require.Equal(t, "17", sec)
34 require.Equal(t, "090", ms)
35 }
36}
37
38// Valid ATProto datetime examples from the spec
39// https://atproto.com/specs/lexicon#datetime
40func TestATProtoValidCases(t *testing.T) {
41 tests := []struct {
42 input string
43 wantMs string // expected millisecond portion after normalization
44 wantHr string // expected hour after UTC normalization
45 wantMin string
46 }{
47 {"1985-04-12T23:20:50.123Z", "123", "23", "20"},
48 {"1985-04-12T23:20:50.123456Z", "123", "23", "20"},
49 {"1985-04-12T23:20:50.120Z", "120", "23", "20"},
50 {"1985-04-12T23:20:50.120000Z", "120", "23", "20"},
51 {"0001-01-01T00:00:00.000Z", "000", "00", "00"},
52 {"0000-01-01T00:00:00.000Z", "000", "00", "00"},
53 {"1985-04-12T23:20:50.12345678912345Z", "123", "23", "20"},
54 {"1985-04-12T23:20:50Z", "000", "23", "20"},
55 {"1985-04-12T23:20:50.0Z", "000", "23", "20"},
56 {"1985-04-12T23:20:50.123+00:00", "123", "23", "20"},
57 {"1985-04-12T23:20:50.123-07:00", "123", "06", "20"}, // 23+7=30 -> next day 06:20
58 }
59 for _, tt := range tests {
60 t.Run(tt.input, func(t *testing.T) {
61 aqt, err := FromString(tt.input)
62 require.NoError(t, err, "input: %s", tt.input)
63 _, _, _, hr, min, _, ms := aqt.Parts()
64 require.Equal(t, tt.wantMs, ms, "millis mismatch for %s", tt.input)
65 require.Equal(t, tt.wantHr, hr, "hour mismatch for %s", tt.input)
66 require.Equal(t, tt.wantMin, min, "minute mismatch for %s", tt.input)
67 })
68 }
69}
70
71func TestBadCases(t *testing.T) {
72 for _, str := range []string{
73 // existing cases
74 "prefix2024-09-13T18:10:17.090Z",
75 "2024-09-13T18-10-17-090Zsuffix",
76 "2024-09-13T18-10-17-090ZZZZ",
77 "2024-09-13T18-10-17*090ZZZZ",
78 // ATProto spec invalid examples
79 "1985-04-12",
80 "1985-04-12T23:20Z",
81 "1985-04-12T23:20:5Z",
82 "1985-04-12T23:20:50.123",
83 "+001985-04-12T23:20:50.123Z",
84 "23:20:50.123Z",
85 "-1985-04-12T23:20:50.123Z",
86 "1985-4-12T23:20:50.123Z",
87 "01985-04-12T23:20:50.123Z",
88 "1985-04-12T23:20:50.123+00",
89 "1985-04-12T23:20:50.123+0000",
90 // ISO-8601 strict capitalization
91 "1985-04-12t23:20:50.123Z",
92 "1985-04-12T23:20:50.123z",
93 // RFC-3339, but not ISO-8601
94 "1985-04-12T23:20:50.123-00:00",
95 "1985-04-12 23:20:50.123Z",
96 // timezone is required
97 "1985-04-12T23:20:50.123",
98 // syntax looks ok, but datetime is not valid
99 "1985-04-12T23:99:50.123Z",
100 "1985-00-12T23:20:50.123Z",
101 // ISO-8601, but normalizes to a negative time
102 "0000-01-01T00:00:00+01:00",
103 } {
104 t.Run(str, func(t *testing.T) {
105 _, err := FromString(str)
106 require.Error(t, err, "expected error for: %s", str)
107 })
108 }
109}