Skip to content

Commit ac5b066

Browse files
authored
Merge pull request #164 from remicorniere/XEP-0082
Support for XEP-0082.
2 parents ce71bc5 + 17d561f commit ac5b066

File tree

2 files changed

+261
-0
lines changed

2 files changed

+261
-0
lines changed

stanza/datetime_profiles.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package stanza
2+
3+
import (
4+
"errors"
5+
"strings"
6+
"time"
7+
)
8+
9+
// Helper structures and functions to manage dates and timestamps as defined in
10+
// XEP-0082: XMPP Date and Time Profiles (https://xmpp.org/extensions/xep-0082.html)
11+
12+
const dateLayoutXEP0082 = "2006-01-02"
13+
const timeLayoutXEP0082 = "15:04:05+00:00"
14+
15+
var InvalidDateInput = errors.New("could not parse date. Input might not be in a supported format")
16+
var InvalidDateOutput = errors.New("could not format date as desired")
17+
18+
type JabberDate struct {
19+
value time.Time
20+
}
21+
22+
func (d JabberDate) DateToString() string {
23+
return d.value.Format(dateLayoutXEP0082)
24+
}
25+
26+
func (d JabberDate) DateTimeToString(nanos bool) string {
27+
if nanos {
28+
return d.value.Format(time.RFC3339Nano)
29+
}
30+
return d.value.Format(time.RFC3339)
31+
}
32+
33+
func (d JabberDate) TimeToString(nanos bool) (string, error) {
34+
if nanos {
35+
spl := strings.Split(d.value.Format(time.RFC3339Nano), "T")
36+
if len(spl) != 2 {
37+
return "", InvalidDateOutput
38+
}
39+
return spl[1], nil
40+
}
41+
spl := strings.Split(d.value.Format(time.RFC3339), "T")
42+
if len(spl) != 2 {
43+
return "", InvalidDateOutput
44+
}
45+
return spl[1], nil
46+
}
47+
48+
func NewJabberDateFromString(strDate string) (JabberDate, error) {
49+
t, err := time.Parse(time.RFC3339, strDate)
50+
if err == nil {
51+
return JabberDate{value: t}, nil
52+
}
53+
54+
t, err = time.Parse(time.RFC3339Nano, strDate)
55+
if err == nil {
56+
return JabberDate{value: t}, nil
57+
}
58+
59+
t, err = time.Parse(dateLayoutXEP0082, strDate)
60+
if err == nil {
61+
return JabberDate{value: t}, nil
62+
}
63+
64+
t, err = time.Parse(timeLayoutXEP0082, strDate)
65+
if err == nil {
66+
return JabberDate{value: t}, nil
67+
}
68+
69+
return JabberDate{}, InvalidDateInput
70+
}

stanza/datetime_profiles_test.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package stanza
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestDateToString(t *testing.T) {
9+
t1 := JabberDate{value: time.Now()}
10+
t2 := JabberDate{value: time.Now().Add(24 * time.Hour)}
11+
12+
t1Str := t1.DateToString()
13+
t2Str := t2.DateToString()
14+
15+
if t1Str == t2Str {
16+
t.Fatalf("time representations should not be identical")
17+
}
18+
}
19+
20+
func TestDateToStringOracle(t *testing.T) {
21+
expected := "2009-11-10"
22+
loc, err := time.LoadLocation("Asia/Shanghai")
23+
if err != nil {
24+
t.Fatalf(err.Error())
25+
}
26+
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
27+
28+
t1Str := t1.DateToString()
29+
if t1Str != expected {
30+
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
31+
}
32+
}
33+
34+
func TestDateTimeToString(t *testing.T) {
35+
t1 := JabberDate{value: time.Now()}
36+
t2 := JabberDate{value: time.Now().Add(10 * time.Second)}
37+
38+
t1Str := t1.DateTimeToString(false)
39+
t2Str := t2.DateTimeToString(false)
40+
41+
if t1Str == t2Str {
42+
t.Fatalf("time representations should not be identical")
43+
}
44+
}
45+
46+
func TestDateTimeToStringOracle(t *testing.T) {
47+
expected := "2009-11-10T23:03:22+08:00"
48+
loc, err := time.LoadLocation("Asia/Shanghai")
49+
if err != nil {
50+
t.Fatalf(err.Error())
51+
}
52+
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
53+
54+
t1Str := t1.DateTimeToString(false)
55+
if t1Str != expected {
56+
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
57+
}
58+
}
59+
60+
func TestDateTimeToStringNanos(t *testing.T) {
61+
t1 := JabberDate{value: time.Now()}
62+
time.After(10 * time.Millisecond)
63+
t2 := JabberDate{value: time.Now()}
64+
65+
t1Str := t1.DateTimeToString(true)
66+
t2Str := t2.DateTimeToString(true)
67+
68+
if t1Str == t2Str {
69+
t.Fatalf("time representations should not be identical")
70+
}
71+
}
72+
73+
func TestDateTimeToStringNanosOracle(t *testing.T) {
74+
expected := "2009-11-10T23:03:22.000000089+08:00"
75+
loc, err := time.LoadLocation("Asia/Shanghai")
76+
if err != nil {
77+
t.Fatalf(err.Error())
78+
}
79+
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
80+
81+
t1Str := t1.DateTimeToString(true)
82+
if t1Str != expected {
83+
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
84+
}
85+
}
86+
87+
func TestTimeToString(t *testing.T) {
88+
t1 := JabberDate{value: time.Now()}
89+
t2 := JabberDate{value: time.Now().Add(10 * time.Second)}
90+
91+
t1Str, err := t1.TimeToString(false)
92+
if err != nil {
93+
t.Fatalf(err.Error())
94+
}
95+
t2Str, err := t2.TimeToString(false)
96+
if err != nil {
97+
t.Fatalf(err.Error())
98+
}
99+
100+
if t1Str == t2Str {
101+
t.Fatalf("time representations should not be identical")
102+
}
103+
}
104+
105+
func TestTimeToStringOracle(t *testing.T) {
106+
expected := "23:03:22+08:00"
107+
loc, err := time.LoadLocation("Asia/Shanghai")
108+
if err != nil {
109+
t.Fatalf(err.Error())
110+
}
111+
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
112+
113+
t1Str, err := t1.TimeToString(false)
114+
if err != nil {
115+
t.Fatalf(err.Error())
116+
}
117+
118+
if t1Str != expected {
119+
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
120+
}
121+
}
122+
123+
func TestTimeToStringNanos(t *testing.T) {
124+
t1 := JabberDate{value: time.Now()}
125+
time.After(10 * time.Millisecond)
126+
t2 := JabberDate{value: time.Now()}
127+
128+
t1Str, err := t1.TimeToString(true)
129+
if err != nil {
130+
t.Fatalf(err.Error())
131+
}
132+
t2Str, err := t2.TimeToString(true)
133+
if err != nil {
134+
t.Fatalf(err.Error())
135+
}
136+
137+
if t1Str == t2Str {
138+
t.Fatalf("time representations should not be identical")
139+
}
140+
}
141+
func TestTimeToStringNanosOracle(t *testing.T) {
142+
expected := "23:03:22.000000089+08:00"
143+
loc, err := time.LoadLocation("Asia/Shanghai")
144+
if err != nil {
145+
t.Fatalf(err.Error())
146+
}
147+
t1 := JabberDate{value: time.Date(2009, time.November, 10, 23, 3, 22, 89, loc)}
148+
149+
t1Str, err := t1.TimeToString(true)
150+
if err != nil {
151+
t.Fatalf(err.Error())
152+
}
153+
154+
if t1Str != expected {
155+
t.Fatalf("time is different than expected. Expected: %s, Actual: %s", expected, t1Str)
156+
}
157+
}
158+
159+
func TestJabberDateParsing(t *testing.T) {
160+
date := "2009-11-10"
161+
_, err := NewJabberDateFromString(date)
162+
if err != nil {
163+
t.Fatalf(err.Error())
164+
}
165+
166+
dateTime := "2009-11-10T23:03:22+08:00"
167+
_, err = NewJabberDateFromString(dateTime)
168+
if err != nil {
169+
t.Fatalf(err.Error())
170+
}
171+
172+
dateTimeNanos := "2009-11-10T23:03:22.000000089+08:00"
173+
_, err = NewJabberDateFromString(dateTimeNanos)
174+
if err != nil {
175+
t.Fatalf(err.Error())
176+
}
177+
178+
// TODO : fix these. Parsing a time with an offset doesn't work
179+
//time := "23:03:22+08:00"
180+
//_, err = NewJabberDateFromString(time)
181+
//if err != nil {
182+
// t.Fatalf(err.Error())
183+
//}
184+
185+
//timeNanos := "23:03:22.000000089+08:00"
186+
//_, err = NewJabberDateFromString(timeNanos)
187+
//if err != nil {
188+
// t.Fatalf(err.Error())
189+
//}
190+
191+
}

0 commit comments

Comments
 (0)