@@ -4,6 +4,9 @@ package mpd
4
4
5
5
import (
6
6
"encoding/xml"
7
+ "errors"
8
+ "strconv"
9
+ "strings"
7
10
"time"
8
11
)
9
12
@@ -13,6 +16,15 @@ func (d Duration) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
13
16
return xml.Attr {name , d .String ()}, nil
14
17
}
15
18
19
+ func (d * Duration ) UnmarshalXMLAttr (attr xml.Attr ) error {
20
+ dur , err := parseDuration (attr .Value )
21
+ if err != nil {
22
+ return err
23
+ }
24
+ * d = Duration (dur )
25
+ return nil
26
+ }
27
+
16
28
// String renders a Duration in XML Duration Data Type format
17
29
func (d * Duration ) String () string {
18
30
// Largest time is 2540400h10m10.000000000s
@@ -126,3 +138,95 @@ func fmtInt(buf []byte, v uint64) int {
126
138
}
127
139
return w
128
140
}
141
+
142
+ func parseDuration (str string ) (time.Duration , error ) {
143
+ if len (str ) < 3 {
144
+ return 0 , errors .New ("input duration too short" )
145
+ }
146
+
147
+ var minus bool
148
+ offset := 0
149
+ if str [offset ] == '-' {
150
+ minus = true
151
+ offset ++
152
+ }
153
+
154
+ if str [offset ] != 'P' {
155
+ return 0 , errors .New ("input duration does not have a valid prefix" )
156
+ }
157
+ offset ++
158
+
159
+ base := time .Unix (0 , 0 )
160
+ t := base
161
+
162
+ var dateStr , timeStr string
163
+ if i := strings .IndexByte (str [offset :], 'T' ); i != - 1 {
164
+ dateStr = str [offset : offset + i ]
165
+ timeStr = str [offset + i + 1 :]
166
+ } else {
167
+ dateStr = str [offset :]
168
+ }
169
+
170
+ if len (dateStr ) > 0 {
171
+ var pos int
172
+ var err error
173
+ var years , months , days int
174
+ if i := strings .IndexByte (dateStr [pos :], 'Y' ); i != - 1 {
175
+ years , err = strconv .Atoi (dateStr [pos : pos + i ])
176
+ if err != nil {
177
+ return 0 , err
178
+ }
179
+ pos += i + 1
180
+ }
181
+
182
+ if i := strings .IndexByte (dateStr [pos :], 'M' ); i != - 1 {
183
+ months , err = strconv .Atoi (dateStr [pos : pos + i ])
184
+ if err != nil {
185
+ return 0 , err
186
+ }
187
+ pos += i + 1
188
+ }
189
+
190
+ if i := strings .IndexByte (dateStr [pos :], 'D' ); i != - 1 {
191
+ days , err = strconv .Atoi (dateStr [pos : pos + i ])
192
+ if err != nil {
193
+ return 0 , err
194
+ }
195
+ }
196
+ t = t .AddDate (years , months , days )
197
+ }
198
+
199
+ if len (timeStr ) > 0 {
200
+ var pos int
201
+ var sum float64
202
+ if i := strings .IndexByte (timeStr [pos :], 'H' ); i != - 1 {
203
+ hours , err := strconv .ParseInt (timeStr [pos :pos + i ], 10 , 64 )
204
+ if err != nil {
205
+ return 0 , err
206
+ }
207
+ sum += float64 (hours ) * 3600
208
+ pos += i + 1
209
+ }
210
+ if i := strings .IndexByte (timeStr [pos :], 'M' ); i != - 1 {
211
+ minutes , err := strconv .ParseInt (timeStr [pos :pos + i ], 10 , 64 )
212
+ if err != nil {
213
+ return 0 , err
214
+ }
215
+ sum += float64 (minutes ) * 60
216
+ pos += i + 1
217
+ }
218
+ if i := strings .IndexByte (timeStr [pos :], 'S' ); i != - 1 {
219
+ seconds , err := strconv .ParseFloat (timeStr [pos :pos + i ], 64 )
220
+ if err != nil {
221
+ return 0 , err
222
+ }
223
+ sum += seconds
224
+ }
225
+ t = t .Add (time .Duration (sum * float64 (time .Second )))
226
+ }
227
+
228
+ if minus {
229
+ return - t .Sub (base ), nil
230
+ }
231
+ return t .Sub (base ), nil
232
+ }
0 commit comments