1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
diff --git a/README.md b/README.md
index 43e8950c3ee7bebc4ba0f0f3a099492892d6086e..4b43d3427f8fb6649b905350a1c2deddbce6ed93 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ | LIST OVERVIEW.FMT | OVER | ☓ | RFC3977 |
| POST | POST | ☓ | RFC3977 |
| ARTICLE | READER | 🗸 | RFC3977 |
| BODY | READER | 🗸 | RFC3977 |
-| DATE | READER | ☓ | RFC3977 |
+| DATE | READER | 🗸 | RFC3977 |
| GROUP | READER | 🗸 | RFC3977 |
| LAST | READER | ☓ | RFC3977 |
| LISTGROUP | READER | ☓ | RFC3977 |
diff --git a/reader.go b/reader.go
index 0285c7ee2ca93d2bae93ab1c59be5d13bff8b6ff..92ff81210e4ce2100c498569010a2192004a5692 100644
--- a/reader.go
+++ b/reader.go
@@ -3,6 +3,7 @@
import (
"context"
"fmt"
+ "time"
)
func (c *Conn) Article(ctx context.Context, msg MessageIdentifier) (*Response, error) {
@@ -54,6 +55,28 @@ case StatusNoArticleWithGivenNumber:
return resp, ErrNoArticleWithGivenNumber
default:
return resp, ErrUnexpectedResponse
+ }
+}
+
+func (c *Conn) Date(ctx context.Context) (time.Time, error) {
+ if c.caps&CapReader == 0 {
+ return time.Time{}, ErrCapabilityNotSupported
+ }
+
+ resp, err := c.CmdNoBody(ctx, "DATE")
+ if err != nil {
+ return time.Time{}, fmt.Errorf("failed to get date: %w", err)
+ }
+
+ switch resp.Status.Code {
+ case StatusServerDateAndTime:
+ t, err := time.Parse("20060102150405", resp.Status.Message)
+ if err != nil {
+ return t, fmt.Errorf("server returned invalid timeformat: %w", err)
+ }
+ return t, nil
+ default:
+ return time.Time{}, ErrUnexpectedResponse
}
}
diff --git a/reader_test.go b/reader_test.go
index 4d20970b2185d548f38b8184e02891d90f67be1b..15a138a691d1141d8276ba0d1ac879f4a6bcd885 100644
--- a/reader_test.go
+++ b/reader_test.go
@@ -148,6 +148,34 @@ })
}
}
+func TestDate(t *testing.T) {
+ if NewsServerSecure == "" || NewsServerUser == "" || NewsServerPassword == "" {
+ t.Log("secure server address, username, and password required in variables_test.go")
+ t.SkipNow()
+ }
+
+ c, err := nntp.Dial(NewsServerSecure)
+ if err != nil {
+ t.Skipf("connection to '%s' failed: %v", NewsServerSecure, err)
+ }
+ defer c.Close(context.Background())
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ err = c.LoginUserPass(ctx, NewsServerUser, NewsServerPassword)
+ if err != nil {
+ t.Skipf("login failed: %v", err)
+ }
+
+ ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ ts, err := c.Date(ctx)
+ tsNow := time.Now()
+ if err != nil || tsNow.Sub(ts).Seconds() > 5 { // allow up to 5 seconds deviation in both directions
+ t.Errorf("\ndate returned: %s\ndate expected: %s", ts, tsNow)
+ }
+}
+
func TestGroup(t *testing.T) {
if NewsServerSecure == "" || NewsServerUser == "" || NewsServerPassword == "" {
t.Log("secure server address, username, and password required in variables_test.go")
diff --git a/status.go b/status.go
index b884707119a292035598616fa66db11ee917c7dc..1ab9a2bcf7ce1467e8b262c3e3b0fe887ea413ed 100644
--- a/status.go
+++ b/status.go
@@ -4,6 +4,7 @@ import "errors"
const (
StatusHelpTextFollows = 100
+ StatusServerDateAndTime = 111
StatusServiceAvailable = 200
StatusServiceNoPosting = 201
StatusConnectionClosing = 205
|